From 50a3d09ee7de9e82965d18c12dc4b181aa76081f Mon Sep 17 00:00:00 2001 From: Mihawk <163614687+mihawk-99@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:10:44 -0400 Subject: [PATCH] Governor level, calls and floor in the driver; floor and honest overlay in the menu code; the early presents default fixed --- src/ps5_framegen_menu.c | 47 ++++++++++++++++++++++++++++++------- tools/apply-port-patches.py | 32 +++++++++++++++---------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/src/ps5_framegen_menu.c b/src/ps5_framegen_menu.c index e4ec56a..20f29ed 100644 --- a/src/ps5_framegen_menu.c +++ b/src/ps5_framegen_menu.c @@ -41,6 +41,8 @@ unsigned ps5_fg_dup = 1; 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; +/* 1: the core cannot keep its own rate, so frames are shown as it makes them (the driver reads it) */ +int ps5_fg_floor; /* 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; @@ -178,13 +180,15 @@ 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 unique) + unsigned long generated, unsigned long unique, unsigned level, double gen_ms, unsigned long calls) { 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], last_unique; - static double new_per_s; + static unsigned long gap_n[2], gap_off[2], last_unique, last_calls; + static double new_per_s, calls_per_s; + static unsigned slow_windows; + static time_t floor_until; static double gap_sum[2], gap_max[2]; static struct timespec last; struct ps5_fg_options now; @@ -238,10 +242,34 @@ 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; - new_per_s = (double)(unique - last_unique) / seconds; + new_per_s = (double)(unique - last_unique) / seconds; + calls_per_s = (double)(calls - last_calls) / 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); + last_calls = calls; + RARCH_LOG("[FrameGen] pace: %.1f presents/s, %.1f generated/s, %.1f new frames/s, core %.1f calls/s, governor level %u (%.2f ms a picture)\n", + (double)presents / seconds, (double)(generated - last_generated) / seconds, new_per_s, calls_per_s, level, gen_ms); + /* The floor: a core that cannot keep its own rate gains nothing from frames made between its frames, and the work + * only slows it more. It is shown as it makes its frames; every ten seconds the generator is tried again. */ + if (ps5_fg_floor) + { + if (t.tv_sec >= floor_until) + { + ps5_fg_floor = 0; + slow_windows = 0; + } + } + else if (plan && ps5_fg_core_fps > 20.0f && calls_per_s < 0.85 * (double)ps5_fg_core_fps) + { + if (++slow_windows >= 2) + { + ps5_fg_floor = 1; + floor_until = t.tv_sec + 10; + RARCH_LOG("[FrameGen] floor: the core runs at %.1f of %.1f frames/s; frames are shown as it makes them for 10 s\n", + calls_per_s, (double)ps5_fg_core_fps); + } + } + else + slow_windows = 0; 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]); @@ -259,12 +287,15 @@ void ps5_fg_osd(bool plan, unsigned subframes, unsigned period, unsigned width, 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 (ps5_fg_floor) + snprintf(text, sizeof(text), "FrameGen: core at %.0f fps (needs %.0f), too slow: frames shown as the core makes them", + calls_per_s, (double)ps5_fg_core_fps); else if (!plan) snprintf(text, sizeof(text), "FrameGen: paused (menu, fast-forward or pause)"); else - snprintf(text, sizeof(text), "FrameGen: %s game %.1f fps (%.1f new/s) -> %.0f fps cadence %u%s %ux%u generated %lu", + snprintf(text, sizeof(text), "FrameGen: %s game %.1f fps (%.1f new/s) -> %.0f fps cadence %u%s %ux%u generated %lu cost level %u (%.1f ms)", 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); + period ? period : 1, now.cadence ? " (forced)" : "", width, height, generated, level, gen_ms); runloop_msg_queue_push(text, strlen(text), 1, 150, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } diff --git a/tools/apply-port-patches.py b/tools/apply-port-patches.py index 9bfe196..46da99a 100755 --- a/tools/apply-port-patches.py +++ b/tools/apply-port-patches.py @@ -4021,8 +4021,9 @@ EDITS += [('configuration.h', '#include "vulkan_framegen.h"\n', '#include "vulkan_framegen.h"\n' 'extern unsigned long ps5_fg_size_pixels_of(unsigned size); /* patches/series, 0120 */\n' + 'extern int ps5_fg_floor; /* patches/series, 0121 */\n' 'extern void ps5_fg_osd(bool plan, unsigned subframes, unsigned period, unsigned width, unsigned height,\n' - ' unsigned long generated);\n', + ' unsigned long generated, unsigned long unique, unsigned level, double gen_ms, unsigned long calls);\n', 'ps5_fg_size_pixels_of(unsigned size); /* patches/series, 0120 */'), ('gfx/drivers/vulkan.c', ' vulkan_filter_chain_set_input_texture((vulkan_filter_chain_t*)\n' @@ -4032,9 +4033,13 @@ EDITS += [('configuration.h', ' {\n' ' unsigned fg_w = 0, fg_h = 0;\n' ' unsigned long fg_n_made = 0;\n' + ' unsigned fg_level = 0;\n' + ' double fg_gen_ms = 0.0;\n' ' vk_framegen_stats(vk->framegen, &fg_w, &fg_h, &fg_n_made);\n' + ' vk_framegen_governor(vk->framegen, &fg_level, &fg_gen_ms); /* patches/series, 0121 */\n' ' ps5_fg_osd(vk->framegen_plan && vk->framegen && config_get_ptr()->bools.video_framegen,\n' - ' 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),\n' + ' fg_level, fg_gen_ms, vk->framegen_calls); /* patches/series, 0121 */\n' ' }\n' '\n' ' vulkan_filter_chain_set_input_texture((vulkan_filter_chain_t*)\n' @@ -4488,9 +4493,9 @@ EDITS += [('gfx/drivers/vulkan.c', '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' + ' if (!(vk->context->flags & VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK))\n {\n vk->framegen_presents = video_info->shader_subframes; /* patches/series, 0121: presents of this call */\n vk->framegen_calls++;\n }\n' ' vulkan_filter_chain_t *filter_chain = NULL;\n bool waits_for_semaphores = false;\n', - 'presents of this call */\n'), + 'vk->framegen_calls++;'), ('gfx/drivers/vulkan.c', ' (fg_n >= 2 && fg_n <= 4)\n', ' (fg_s >= 2 && fg_s <= 8) /* patches/series, 0121 */\n', @@ -4511,14 +4516,6 @@ EDITS += [('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', @@ -4550,6 +4547,17 @@ EDITS += [('gfx/drivers/vulkan.c', ' if (fg_final)\n vk_framegen_commit(vk->framegen);\n', 'ps5_fg_dbg_t = fg_shown_generated ? fg_t : 1.0f; } /* patches/series, 0121 */')] +# Frame generation, the governor's level, the calls and the floor in the status (patches/series, 0121) +EDITS += [('gfx/drivers/vulkan.c', + ' unsigned framegen_presents; /* presents of this call (patches/series, 0121: the timeline) */\n', + ' unsigned framegen_presents; /* presents of this call (patches/series, 0121: the timeline) */\n' + ' unsigned long framegen_calls; /* calls (patches/series, 0121: the floor) */\n', + 'unsigned long framegen_calls; /* calls (patches/series, 0121'), + ('gfx/drivers/vulkan.c', + ' && !(vk->context->swap_interval > 1);\n', + ' && !(vk->context->swap_interval > 1)\n && !ps5_fg_floor; /* patches/series, 0121: a core too slow for its own rate is shown as it is */\n', + '&& !ps5_fg_floor; /* 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