Target Framerate (60/120), the timeline of pictures, any-cadence generation, present sequence log, honest overlay

This commit is contained in:
Mihawk committed 2026-10-11 14:03:38 -04:00
1 parent da4cf320b9
commit 72a3bac0b3
3 files changed
+190 -43

No files matched your search

+21 -24
View File
@@ -21,7 +21,7 @@
enum ps5_fg_item
{
PS5_FG_ENABLE = 0,
PS5_FG_SUBFRAMES,
PS5_FG_TARGET,
PS5_FG_METHOD,
PS5_FG_CADENCE,
PS5_FG_SIZE,
@@ -33,7 +33,7 @@ enum ps5_fg_item
struct ps5_fg_options
{
unsigned enable; /* 0 off, 1 on */
unsigned subframes; /* presents per content frame, 1 (nothing generated) to 4 */
unsigned target; /* 0 120 fps, 1 60 fps: the rate of the pictures the display gets */
unsigned method; /* 0 neural, 1 blend, 2 repeat */
unsigned cadence; /* 0 measured, else core frames per new frame, 1 to 4 */
unsigned size; /* 0 4K, 1 1440p, 2 1080p, 3 720p: the most pixels the generator works at */
@@ -47,8 +47,8 @@ static inline unsigned ps5_fg_choice_count(unsigned item)
case PS5_FG_ENABLE:
case PS5_FG_OSD:
return 2;
case PS5_FG_SUBFRAMES:
return 5;
case PS5_FG_TARGET:
return 2;
case PS5_FG_METHOD:
return 3;
case PS5_FG_CADENCE:
@@ -63,7 +63,7 @@ static inline unsigned ps5_fg_choice_count(unsigned item)
static inline const char *ps5_fg_item_name(unsigned item)
{
static const char *const names[PS5_FG_ITEM_COUNT] = {
"Frame Generation", "Output Rate", "Method",
"Frame Generation", "Target Framerate", "Method",
"Content Frame Cadence", "Working Size", "Status On Screen",
"Recommended Settings",
};
@@ -73,15 +73,15 @@ static inline const char *ps5_fg_item_name(unsigned item)
static inline const char *ps5_fg_item_help(unsigned item)
{
static const char *const help[PS5_FG_ITEM_COUNT] = {
"Makes the frames between a game's frames with a trained network. Needs Output Rate 2x or more.",
"Presents per game frame. Auto picks the display's rate over the game's (2x for 60 fps games on a 120 Hz output). "
"A rate the display cannot show makes the game run slow. Changing it restarts the video output.",
"Makes the frames between a game's frames with a trained network, up to the Target Framerate.",
"The frame rate of what you see. 120: a 60 fps game gets one made frame between each pair (30 fps games, three). "
"60: a 30 fps game gets one; a 60 fps game is shown as it is. Changing it restarts the video output.",
"Neural is the trained network. Blend and Repeat are the baselines to compare it with.",
"Auto measures how often a game makes a new frame. Force 1 to 4 when a game's cadence is "
"misjudged (30 fps games: 2).",
"The most pixels the generator works at. Games that render larger are averaged down. Smaller is faster.",
"Shows the method, the rate, the frame size and a count of generated frames over the game.",
"Frame generation on, rate auto, neural, auto cadence, up to 4K, status off.",
"Frame generation on, 120 fps, neural, auto cadence, up to 4K, status off.",
};
return item < PS5_FG_ITEM_COUNT ? help[item] : "";
}
@@ -90,7 +90,7 @@ static inline const char *ps5_fg_item_help(unsigned item)
static inline const char *ps5_fg_choice_name(unsigned item, unsigned value)
{
static const char *const on_off[] = {"Off", "On"};
static const char *const rates[] = {"Auto", "Off (1x)", "2x", "3x", "4x"};
static const char *const rates[] = {"120 fps", "60 fps"};
static const char *const methods[] = {"Neural", "Blend", "Repeat"};
static const char *const cadence[] = {"Auto", "1", "2", "3", "4"};
static const char *const sizes[] = {"Up to 4K", "Up to 1440p", "Up to 1080p", "Up to 720p"};
@@ -99,8 +99,8 @@ static inline const char *ps5_fg_choice_name(unsigned item, unsigned value)
case PS5_FG_ENABLE:
case PS5_FG_OSD:
return on_off[value & 1];
case PS5_FG_SUBFRAMES:
return value < 5 ? rates[value] : "";
case PS5_FG_TARGET:
return value < 2 ? rates[value] : "";
case PS5_FG_METHOD:
return value < 3 ? methods[value] : "";
case PS5_FG_CADENCE:
@@ -115,20 +115,20 @@ static inline const char *ps5_fg_choice_name(unsigned item, unsigned value)
static inline void ps5_fg_defaults(struct ps5_fg_options *o)
{
o->enable = 1;
o->subframes = 0;
o->target = 0;
o->method = 0;
o->cadence = 0;
o->size = 0;
o->osd = 0;
}
/* An item's value as its choice index (subframes 1..4 are choices 0..3). */
/* An item's value as its choice index. */
static inline unsigned ps5_fg_get(const struct ps5_fg_options *o, unsigned item)
{
switch (item)
{
case PS5_FG_ENABLE: return o->enable;
case PS5_FG_SUBFRAMES: return o->subframes <= 4 ? o->subframes : 0;
case PS5_FG_TARGET: return o->target ? 1 : 0;
case PS5_FG_METHOD: return o->method;
case PS5_FG_CADENCE: return o->cadence;
case PS5_FG_SIZE: return o->size;
@@ -148,7 +148,7 @@ static inline void ps5_fg_set(struct ps5_fg_options *o, unsigned item, unsigned
switch (item)
{
case PS5_FG_ENABLE: o->enable = value; break;
case PS5_FG_SUBFRAMES: o->subframes = value; break;
case PS5_FG_TARGET: o->target = value; break;
case PS5_FG_METHOD: o->method = value; break;
case PS5_FG_CADENCE: o->cadence = value; break;
case PS5_FG_SIZE: o->size = value; break;
@@ -182,8 +182,8 @@ static inline int ps5_fg_save(const struct ps5_fg_options *o)
if (!file)
return -1;
fprintf(file, "video_framegen = \"%s\"\n", o->enable ? "true" : "false");
fprintf(file, "video_framegen_auto = \"%s\"\n", o->subframes == 0 ? "true" : "false");
fprintf(file, "video_shader_subframes = \"%u\"\n", o->subframes ? o->subframes : 2);
fprintf(file, "video_framegen_auto = \"true\"\n");
fprintf(file, "video_framegen_target = \"%u\"\n", o->target ? 60 : 120);
fprintf(file, "video_framegen_method = \"%u\"\n", o->method);
fprintf(file, "video_framegen_cadence = \"%u\"\n", o->cadence);
fprintf(file, "video_framegen_size = \"%u\"\n", o->size);
@@ -195,8 +195,6 @@ static inline int ps5_fg_save(const struct ps5_fg_options *o)
static inline void ps5_fg_load(struct ps5_fg_options *o)
{
char line[160];
unsigned rate = o->subframes;
int auto_rate = o->subframes == 0;
FILE *file = fopen(PS5_FG_CONFIG_PATH, "r");
if (!file)
return;
@@ -220,15 +218,14 @@ static inline void ps5_fg_load(struct ps5_fg_options *o)
o->size = number % 4;
else if (strstr(line, "video_framegen_osd"))
o->osd = strncmp(value, "true", 4) == 0;
else if (strstr(line, "video_framegen_target"))
o->target = number == 60;
else if (strstr(line, "video_framegen_auto"))
auto_rate = strncmp(value, "true", 4) == 0;
continue; /* (older files: the rate is the target's now) */
else if (strstr(line, "video_framegen"))
o->enable = strncmp(value, "true", 4) == 0;
else if (strstr(line, "video_shader_subframes"))
rate = number >= 1 && number <= 4 ? number : 2;
}
fclose(file);
o->subframes = auto_rate ? 0 : rate;
}
#endif
+45 -19
View File
@@ -35,6 +35,13 @@
#define PS5_FG_LABEL_FOLDER "ps5_framegen"
/* Presents of each picture (the display's rate over the target's), set with the rate (patches/series, 0121) */
unsigned ps5_fg_dup = 1;
/* The rate of the pictures the display gets: 119.88 or 59.94 (the Target Framerate), set with the rate */
float ps5_fg_target_fps = 119.88f;
/* The instant of the picture the last present showed (1: a real frame), set by the driver */
float ps5_fg_dbg_t = 1.0f;
/* The loaded core's frame rate, kept by the video setup (patches/series, 0121) for the driver's cadence check */
float ps5_fg_core_fps;
@@ -84,11 +91,7 @@ static void ps5_fg_current(struct ps5_fg_options *o)
{
settings_t *settings = config_get_ptr();
o->enable = settings->bools.video_framegen ? 1 : 0;
o->subframes = settings->bools.video_framegen_auto ? 0 : settings->uints.video_shader_subframes;
if (o->subframes > 4)
o->subframes = 1;
if (!settings->bools.video_framegen_auto && o->subframes < 1)
o->subframes = 1;
o->target = settings->uints.video_framegen_target == 60 ? 1 : 0;
o->method = settings->uints.video_framegen_method % 3;
o->cadence = settings->uints.video_framegen_cadence % 5;
o->size = settings->uints.video_framegen_size % 4;
@@ -118,16 +121,15 @@ void ps5_fg_menu_step(const char *label, int direction)
before = now;
ps5_fg_step(&now, (unsigned)item, direction);
settings->bools.video_framegen = now.enable != 0;
settings->bools.video_framegen_auto = now.subframes == 0;
if (now.subframes)
settings->uints.video_shader_subframes = now.subframes;
settings->bools.video_framegen_auto = true;
settings->uints.video_framegen_target = now.target ? 60 : 120;
settings->uints.video_framegen_method = now.method;
settings->uints.video_framegen_cadence = now.cadence;
settings->uints.video_framegen_size = now.size;
settings->bools.video_framegen_osd = now.osd != 0;
ps5_fg_save(&now);
/* The rate is read when the video output starts: its timing follows it */
if (now.subframes != before.subframes)
if (now.target != before.target)
command_event(CMD_EVENT_REINIT, NULL); /* (Auto picks its rate as the video output starts) */
}
@@ -176,11 +178,13 @@ void ps5_fg_menu_help(const char *label, char *s, size_t len)
/* The driver calls this for every present; the message is refreshed about twice a second */
void ps5_fg_osd(bool plan, unsigned subframes, unsigned period, unsigned width, unsigned height,
unsigned long generated)
unsigned long generated, unsigned long unique)
{
static unsigned calls;
const unsigned pictures = subframes / (ps5_fg_dup ? ps5_fg_dup : 1);
static unsigned long presents, last_generated;
static unsigned long gap_n[2], gap_off[2];
static unsigned long gap_n[2], gap_off[2], last_unique;
static double new_per_s;
static double gap_sum[2], gap_max[2];
static struct timespec last;
struct ps5_fg_options now;
@@ -191,6 +195,26 @@ void ps5_fg_osd(bool plan, unsigned subframes, unsigned period, unsigned width,
return;
presents++;
clock_gettime(CLOCK_MONOTONIC, &t);
{
/* the last presents: instant shown and ms since the one before (a line with the pace) */
static float seq_t[24];
static float seq_ms[24];
static unsigned seq_n;
static struct timespec seq_prev;
if (seq_prev.tv_sec)
seq_ms[seq_n % 24] = (float)((t.tv_sec - seq_prev.tv_sec) * 1e3 + (t.tv_nsec - seq_prev.tv_nsec) / 1e6);
seq_t[seq_n % 24] = ps5_fg_dbg_t;
seq_prev = t;
seq_n++;
if (seq_n % 240 == 0)
{
char line[400];
unsigned q, o = 0;
for (q = 0; q < 24 && o < sizeof(line) - 24; q++)
o += (unsigned)snprintf(line + o, sizeof(line) - o, " %.2f@%.1f", seq_t[(seq_n + q) % 24], seq_ms[(seq_n + q) % 24]);
RARCH_LOG("[FrameGen] seq:%s\n", line);
}
}
if (last.tv_sec == 0)
last = t;
{
@@ -214,8 +238,10 @@ void ps5_fg_osd(bool plan, unsigned subframes, unsigned period, unsigned width,
if ((t.tv_sec - last.tv_sec) * 1000000000L + (t.tv_nsec - last.tv_nsec) >= 2000000000L)
{
double seconds = (double)(t.tv_sec - last.tv_sec) + (double)(t.tv_nsec - last.tv_nsec) / 1e9;
RARCH_LOG("[FrameGen] pace: %.1f presents/s, %.1f generated/s\n", (double)presents / seconds,
(double)(generated - last_generated) / seconds);
new_per_s = (double)(unique - last_unique) / seconds;
last_unique = unique;
RARCH_LOG("[FrameGen] pace: %.1f presents/s, %.1f generated/s, %.1f new frames/s\n", (double)presents / seconds,
(double)(generated - last_generated) / seconds, new_per_s);
RARCH_LOG("[FrameGen] spacing (ms): real n %lu mean %.2f max %.2f off %lu | generated n %lu mean %.2f max %.2f off %lu\n",
gap_n[0], gap_n[0] ? gap_sum[0] / (double)gap_n[0] : 0.0, gap_max[0], gap_off[0],
gap_n[1], gap_n[1] ? gap_sum[1] / (double)gap_n[1] : 0.0, gap_max[1], gap_off[1]);
@@ -230,15 +256,15 @@ void ps5_fg_osd(bool plan, unsigned subframes, unsigned period, unsigned width,
ps5_fg_current(&now);
if (!now.enable)
snprintf(text, sizeof(text), "FrameGen: off");
else if (subframes < 2)
snprintf(text, sizeof(text), "FrameGen: waiting - the output rate is not a multiple of the game's (Output Rate %s)",
ps5_fg_choice_name(PS5_FG_SUBFRAMES, now.subframes));
else if (pictures < 2)
snprintf(text, sizeof(text), "FrameGen: nothing to make - the game already runs at the Target Framerate (%s)",
ps5_fg_choice_name(PS5_FG_TARGET, now.target));
else if (!plan)
snprintf(text, sizeof(text), "FrameGen: paused (menu, fast-forward or pause)");
else
snprintf(text, sizeof(text), "FrameGen: %s %ux cadence %u%s %ux%u generated %lu",
ps5_fg_choice_name(PS5_FG_METHOD, now.method), subframes, period ? period : 1,
now.cadence ? " (forced)" : "", width, height, generated);
snprintf(text, sizeof(text), "FrameGen: %s game %.1f fps (%.1f new/s) -> %.0f fps cadence %u%s %ux%u generated %lu",
ps5_fg_choice_name(PS5_FG_METHOD, now.method), (double)ps5_fg_core_fps, new_per_s, (double)ps5_fg_target_fps,
period ? period : 1, now.cadence ? " (forced)" : "", width, height, generated);
runloop_msg_queue_push(text, strlen(text), 1, 150, false, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
+124
View File
@@ -4426,6 +4426,130 @@ EDITS += [('configuration.h',
' bool video_adaptive_vsync = settings->bools.video_adaptive_vsync;\n',
'Output Rate Auto: the presents per game frame')]
# Frame generation, Target Framerate (patches/series, 0121): the rate of the pictures the display gets, 60 or 120. The
# display runs at its own rate (119.88 Hz on this console); a target below it shows each picture "dup" times, the
# presents of one picture being copies. shader_subframes is the presents per game frame: pictures x dup.
EDITS += [('configuration.h',
' unsigned video_framegen_cadence; /* patches/series, 0120: 0 measured, else core frames per new frame */\n',
' unsigned video_framegen_cadence; /* patches/series, 0120: 0 measured, else core frames per new frame */\n'
' unsigned video_framegen_target; /* patches/series, 0121: Target Framerate, 60 or 120 */\n',
'unsigned video_framegen_target; /* patches/series, 0121'),
('configuration.c',
' SETTING_UINT("video_framegen_cadence", &settings->uints.video_framegen_cadence, true, 0, false); /* '
'patches/series, 0120 */\n',
' SETTING_UINT("video_framegen_cadence", &settings->uints.video_framegen_cadence, true, 0, false); /* '
'patches/series, 0120 */\n'
' SETTING_UINT("video_framegen_target", &settings->uints.video_framegen_target, true, 120, false); /* '
'patches/series, 0121 */\n',
'SETTING_UINT("video_framegen_target"'),
('retroarch.c',
' float n = display / (float)input_fps;\n'
' unsigned s = n < 1.5f ? 1 : n < 2.5f ? 2 : n < 3.5f ? 3 : 4;\n'
' RARCH_LOG("[FrameGen] Output Rate Auto: display %.3f Hz, game %.3f fps -> %ux\\n", display, '
'input_fps, s);\n'
' settings->uints.video_shader_subframes = s;\n',
' /* patches/series, 0121: Target Framerate. The pictures the display gets per second are the target (60 or\n'
' * 120); a display at a multiple of it shows each picture that many times (dup). */\n'
' {\n'
' extern unsigned ps5_fg_dup;\n'
' const float target = settings->uints.video_framegen_target == 60 ? 59.94f : 119.88f;\n'
' unsigned dup = (unsigned)(display / target + 0.5f);\n'
' float n;\n'
' unsigned s;\n'
' if (dup < 1)\n'
' dup = 1;\n'
' n = (display / (float)dup) / (float)input_fps;\n'
' s = n < 1.5f ? 1 : n < 2.5f ? 2 : n < 3.5f ? 3 : 4;\n'
' if (s * dup > 4)\n'
' s = 4 / dup > 0 ? 4 / dup : 1;\n'
' ps5_fg_dup = dup;\n'
' RARCH_LOG("[FrameGen] Target Framerate %.0f: display %.3f Hz, game %.3f fps -> %ux, each shown %ux\\n", '
'target, display, input_fps, s, dup);\n'
' settings->uints.video_shader_subframes = s * dup;\n'
' }\n',
'Target Framerate. The pictures the display gets per second'),
('gfx/drivers/vulkan.c',
' const unsigned fg_n = video_info->shader_subframes;\n'
' const unsigned fg_j = fg_nested ? vk->framegen_subframe : 1;\n',
' /* patches/series, 0121: presents per game frame fg_s = pictures fg_n x copies of each (Target Framerate) */\n'
' extern unsigned ps5_fg_dup;\n'
' const unsigned fg_s = video_info->shader_subframes;\n'
' const unsigned fg_dup = ps5_fg_dup >= 1 && fg_s % ps5_fg_dup == 0 ? ps5_fg_dup : 1;\n'
' const unsigned fg_n = fg_s / fg_dup;\n'
' const unsigned fg_j = ((fg_nested ? vk->framegen_subframe : 1) - 1) / fg_dup + 1;\n',
'presents per game frame fg_s = pictures fg_n'),
]
# Frame generation, the timeline (patches/series, 0121): the pictures of a group are placed at the instants the target
# rate puts them at, the presents of each call vary, and the game is never run faster or slower to fit a multiple.
EDITS += [('gfx/drivers/vulkan.c',
" unsigned framegen_period; /* calls in a group: 1, or 2 for a game at half the core's rate */\n",
" unsigned framegen_period; /* calls in a group: 1, or 2 for a game at half the core's rate */\n unsigned framegen_presents; /* presents of this call (patches/series, 0121: the timeline) */\n",
'unsigned framegen_presents; /* presents of this call'),
('gfx/drivers/vulkan.c',
' vulkan_filter_chain_t *filter_chain = NULL;\n bool waits_for_semaphores = false;\n',
' if (!(vk->context->flags & VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK))\n vk->framegen_presents = video_info->shader_subframes; /* patches/series, 0121: presents of this call */\n'
' vulkan_filter_chain_t *filter_chain = NULL;\n bool waits_for_semaphores = false;\n',
'presents of this call */\n'),
('gfx/drivers/vulkan.c',
' (fg_n >= 2 && fg_n <= 4)\n',
' (fg_s >= 2 && fg_s <= 8) /* patches/series, 0121 */\n',
'(fg_s >= 2 && fg_s <= 8) /* patches/series, 0121 */'),
('gfx/drivers/vulkan.c',
' vk->framegen_call = 0;\n vk->framegen_group_open = true;\n',
' vk->framegen_call = 0;\n vk->framegen_group_open = true;\n {\n /* patches/series, 0121: the pictures of the group, at the instants of the target rate */\n extern float ps5_fg_core_fps, ps5_fg_target_fps;\n vk_framegen_begin_group(vk->framegen, vk->framegen_period ? vk->framegen_period : 1,\n ps5_fg_core_fps, ps5_fg_target_fps, fg_dup);\n }\n',
'vk_framegen_begin_group(vk->framegen'),
('gfx/drivers/vulkan.c',
" /* The present's place in the group */\n const unsigned fg_total = (vk->framegen_period ? vk->framegen_period : 1) * fg_n;\n bool fg_shown_generated = false;\n const unsigned fg_place = vk->framegen_call * fg_n + fg_j;\n if (fg_place < fg_total)\n {\n if (vk_framegen_generate(vk->framegen, vk->cmd, frame_index,\n (float)fg_place / (float)fg_total, &vk->framegen_frame))\n {\n input.image = vk->framegen_frame.image;\n input.view = vk->framegen_frame.view;\n input.layout = vk->framegen_frame.layout;\n input.width = vk->framegen_frame.width;\n input.height = vk->framegen_frame.height;\n input.format = vk->framegen_frame.format;\n input.physical_width = vk->framegen_frame.width;\n fg_shown_generated = true;\n }\n }\n else if (fg_place == fg_total)\n vk_framegen_commit(vk->framegen);\n",
" /* The present's place in the call (patches/series, 0121: the timeline of the Target Framerate) */\n bool fg_shown_generated = false;\n bool fg_final = false;\n float fg_t = 0.0f;\n if (!fg_nested)\n vk->framegen_presents = vk_framegen_call_presents(vk->framegen, vk->framegen_call);\n if ( vk_framegen_picture(vk->framegen, vk->framegen_call, fg_nested ? vk->framegen_subframe : 1,\n vk->framegen_presents, &fg_t, &fg_final)\n && vk_framegen_generate(vk->framegen, vk->cmd, frame_index, fg_t, &vk->framegen_frame))\n {\n input.image = vk->framegen_frame.image;\n input.view = vk->framegen_frame.view;\n input.layout = vk->framegen_frame.layout;\n input.width = vk->framegen_frame.width;\n input.height = vk->framegen_frame.height;\n input.format = vk->framegen_frame.format;\n input.physical_width = vk->framegen_frame.width;\n fg_shown_generated = true;\n }\n if (fg_final)\n vk_framegen_commit(vk->framegen);\n",
'vk_framegen_call_presents(vk->framegen, vk->framegen_call)'),
('gfx/drivers/vulkan.c',
' if ( (video_info->shader_subframes > 1)\n && (backbuffer->image != VK_NULL_HANDLE)\n && (vk->context->flags & VK_CTX_FLAG_HAS_ACQUIRED_SWAPCHAIN)\n && !black_frame_insertion\n && !input_driver_nonblock_state\n && !runloop_is_slowmotion\n && !runloop_is_paused\n && (!(vk->flags & VK_FLAG_MENU_ENABLE))\n && !(vk->context->swap_interval > 1)\n && (!(vk->context->flags & VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK)))\n {\n vk->context->flags |= VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK;\n for (j = 1; j < (int) video_info->shader_subframes; j++)\n',
' if ( (vk->framegen_presents > 1) /* patches/series, 0121: the presents of this call */\n && (backbuffer->image != VK_NULL_HANDLE)\n && (vk->context->flags & VK_CTX_FLAG_HAS_ACQUIRED_SWAPCHAIN)\n && !black_frame_insertion\n && !input_driver_nonblock_state\n && !runloop_is_slowmotion\n && !runloop_is_paused\n && (!(vk->flags & VK_FLAG_MENU_ENABLE))\n && !(vk->context->swap_interval > 1)\n && (!(vk->context->flags & VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK)))\n {\n vk->context->flags |= VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK;\n for (j = 1; j < (int) vk->framegen_presents; j++)\n',
'(vk->framegen_presents > 1) /* patches/series, 0121'),
('gfx/drivers/vulkan.c',
' && !(video_info->shader_subframes > 1)\n && !black_frame_insertion\n && (!(vk->context->flags & VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK)))\n',
' && !(vk->framegen_presents > 1) /* patches/series, 0121 */\n && !black_frame_insertion\n && (!(vk->context->flags & VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK)))\n',
'!(vk->framegen_presents > 1) /* patches/series, 0121 */'),
('gfx/drivers/vulkan.c',
'extern void ps5_fg_osd(bool plan, unsigned subframes, unsigned period, unsigned width, unsigned height,\n unsigned long generated);\n',
'extern void ps5_fg_osd(bool plan, unsigned subframes, unsigned period, unsigned width, unsigned height,\n unsigned long generated, unsigned long unique); /* patches/series, 0121: and the new frames held */\n',
'unsigned long generated, unsigned long unique); /* patches/series, 0121'),
('gfx/drivers/vulkan.c',
' video_info->shader_subframes, vk->framegen_period, fg_w, fg_h, fg_n_made);\n',
' video_info->shader_subframes, vk->framegen_period, fg_w, fg_h, fg_n_made, vk_framegen_unique(vk->framegen)); /* patches/series, 0121 */\n',
'fg_n_made, vk_framegen_unique(vk->framegen)); /* patches/series, 0121 */'),
('retroarch.c',
' /* patches/series, 0121: Target Framerate. The pictures the display gets per second are the target (60 or\n * 120); a display at a multiple of it shows each picture that many times (dup). */\n {\n extern unsigned ps5_fg_dup;\n const float target = settings->uints.video_framegen_target == 60 ? 59.94f : 119.88f;\n unsigned dup = (unsigned)(display / target + 0.5f);\n float n;\n unsigned s;\n if (dup < 1)\n dup = 1;\n n = (display / (float)dup) / (float)input_fps;\n s = n < 1.5f ? 1 : n < 2.5f ? 2 : n < 3.5f ? 3 : 4;\n if (s * dup > 4)\n s = 4 / dup > 0 ? 4 / dup : 1;\n ps5_fg_dup = dup;\n RARCH_LOG("[FrameGen] Target Framerate %.0f: display %.3f Hz, game %.3f fps -> %ux, each shown %ux\\n", target, display, input_fps, s, dup);\n settings->uints.video_shader_subframes = s * dup;\n }\n',
' /* patches/series, 0121: Target Framerate. The pictures the display gets per second are the target (60 or\n * 120); a display at a multiple of it shows each picture that many times (dup). The game\'s rate is never\n * changed to fit: the number of pictures per game frame varies (the driver places each at its instant), and\n * the presents set here are the nominal ones, the pacing the runloop assumes. */\n {\n extern unsigned ps5_fg_dup;\n extern float ps5_fg_target_fps;\n const float target = settings->uints.video_framegen_target == 60 ? 59.94f : 119.88f;\n unsigned dup = (unsigned)(display / target + 0.5f);\n unsigned s;\n if (dup < 1)\n dup = 1;\n s = (unsigned)(target / (float)input_fps + 0.5f);\n if (input_fps > target * 1.06f || s < 1)\n {\n /* the game is already faster than the target: nothing to make */\n s = 1;\n dup = 1;\n }\n if (s * dup > 8)\n s = 8 / dup > 0 ? 8 / dup : 1;\n /* the nominal rate must not be below the game\'s, or the runloop would not sync to the display */\n if (s > 1 && display / (float)(s * dup) < (float)input_fps * 0.95f)\n s--;\n ps5_fg_dup = dup;\n ps5_fg_target_fps = target;\n RARCH_LOG("[FrameGen] Target Framerate %.0f: display %.3f Hz, game %.3f fps -> %.2f pictures per game frame, "\n "each shown %ux\\n", target, display, input_fps, (double)target / (double)input_fps, dup);\n settings->uints.video_shader_subframes = s * dup;\n }\n',
'the nominal ones, the pacing the runloop assumes'),
('retroarch.c',
' shader_subframes,\n audio_max_timing_skew);\n',
' shader_subframes,\n /* patches/series, 0121: the core runs at its own rate (the driver places the pictures), so the audio is not retimed */\n (settings->bools.video_framegen && settings->bools.video_framegen_auto && fabsf((settings->uints.video_framegen_target == 60 ? 59.94f : 119.88f) / (float)input_fps - floorf((settings->uints.video_framegen_target == 60 ? 59.94f : 119.88f) / (float)input_fps + 0.5f)) > 0.02f * ((settings->uints.video_framegen_target == 60 ? 59.94f : 119.88f) / (float)input_fps)) ? 0.0f : audio_max_timing_skew);\n',
'so the audio is not retimed')]
# Frame generation, any cadence (patches/series, 0121): the last gap between new frames predicts the next, and a gap that
# differs is not a reason to stop generating (a game that steps its animation unevenly would be left at the game's own rate).
EDITS += [('gfx/drivers/vulkan.c',
' if (fg_gap == vk->framegen_last_gap && fg_gap >= 1 && fg_gap <= 4)\n'
' vk->framegen_period = fg_gap;\n',
' if (fg_gap >= 1 && fg_gap <= 8) /* patches/series, 0121: the last gap predicts the next */\n'
' vk->framegen_period = fg_gap;\n',
'the last gap predicts the next */'),
('gfx/drivers/vulkan.c',
' if (fg_gap != (vk->framegen_period ? vk->framegen_period : 1))\n'
' vk_framegen_invalidate(vk->framegen);\n',
' if (fg_gap > 8) /* patches/series, 0121: only a pause or a cut breaks the sequence */\n'
' vk_framegen_invalidate(vk->framegen);\n',
'only a pause or a cut breaks the sequence */')]
# Frame generation, the sequence of presents in the status log (patches/series, 0121)
EDITS += [('gfx/drivers/vulkan.c',
' if (fg_final)\n vk_framegen_commit(vk->framegen);\n',
' { extern float ps5_fg_dbg_t; ps5_fg_dbg_t = fg_shown_generated ? fg_t : 1.0f; } /* patches/series, 0121 */\n'
' if (fg_final)\n vk_framegen_commit(vk->framegen);\n',
'ps5_fg_dbg_t = fg_shown_generated ? fg_t : 1.0f; } /* patches/series, 0121 */')]
# Changes that are withdrawn rather than deleted, by marker.
#
# An entry here names an insertion this driver no longer needs, and the