From 2bf32649af4efeed1be7bc60636e72e9c8b8d1c8 Mon Sep 17 00:00:00 2001 From: chrg6 Date: Fri, 13 Nov 2026 18:54:18 -0500 Subject: [PATCH] Name the game's frame-rate ceiling as a period of its own, frame_step_ceiling_ns, for a pacer that hands the limit over. When Reflex holds the frame limit with DLSS Frame Generation, the game's pacer is to keep only the rate the game's code survives (240 a second; the quick menu read address 0 at 640 to 700), so that a limiter that lets go, or one that counts other frames than the game's, cannot run the game past it. frame_step_ceiling_ns gives that period from LIMIT_FREE_MAX_HZ, or LIMIT_MAX_HZ for a build with the fps60 or realtime patch set; frame_step_cap_ns is left as it is. tests/frame_step_check.c checks 4166666 ns for a game stepping by the clock; frame_step_check passes. Co-Authored-By: Claude Opus 5.5 --- runtime/frame_rate.c | 7 +++++++ runtime/host.h | 1 + tests/frame_step_check.c | 3 +++ 3 files changed, 11 insertions(+) diff --git a/runtime/frame_rate.c b/runtime/frame_rate.c index 87e74553..1f32e8da 100644 --- a/runtime/frame_rate.c +++ b/runtime/frame_rate.c @@ -180,6 +180,13 @@ static double shown_hz(double hz) { return hz <= 0 ? 60 : hz < LIMIT_MIN_HZ ? LI #define LIMIT_FREE_MAX_HZ 240 int frame_free_running(void) { return rt_patch_set("fps60") < 0 && rt_patch_set("realtime") < 0; } int frame_limit_adjustable(void) { return 1; } + +// The period the pacer keeps while something else holds the frame limit where the frame starts (Reflex with DLSS Frame +// Generation, vk_dlssg.c vk_dlssg_pace): a limiter that lets go, or counts other frames than the game's, still leaves +// the game no faster than the rate its code survives. +uint64_t frame_step_ceiling_ns(void) { + return 1000000000ull / (frame_free_running() ? LIMIT_FREE_MAX_HZ : LIMIT_MAX_HZ); +} static unsigned limit_seen; static int limit_read; static int limit_hz = -1; // 0: display; -2: unlimited (free-running builds); -1 before the first read diff --git a/runtime/host.h b/runtime/host.h index 79701b0c..f7c17951 100644 --- a/runtime/host.h +++ b/runtime/host.h @@ -451,6 +451,7 @@ void frame_step_init(void); // after rt_patches_i int frame_step_realtime(void); // the real-time step is on: presents ignore the interval uint64_t frame_step_cap_ns(void); // with the real-time step: the shortest frame in ns int frame_step_movie_hz(void); // the movie rate frame_step_cap_ns paces at; 0: no movie +uint64_t frame_step_ceiling_ns(void); // the shortest frame the game survives, whoever holds the limit int ov_movie_live(void); // ov_movie2.c: a movie plays (frame_rate.c movie_cap_hz) int frame_limit_adjustable(void); // the frame limit setting changes the rate (always) int frame_free_running(void); // no fps60/realtime patch set: the game steps by the clock diff --git a/tests/frame_step_check.c b/tests/frame_step_check.c index 01ffc8ba..7a045254 100644 --- a/tests/frame_step_check.c +++ b/tests/frame_step_check.c @@ -182,6 +182,9 @@ int main(int argc, char **argv) { int mean = argc > 1 && !strcmp(argv[1], "mean"); if (mean) steady = "0"; frame_step_init(); + // the pacer's period while Reflex holds the limit (vk_dlssg.c vk_dlssg_pace): 240 fps for a game stepping by the clock + CHECK(frame_step_ceiling_ns() == 4166666, "the ceiling of a game that steps by the clock: %llu ns", + (unsigned long long)frame_step_ceiling_ns()); float one, cut0; memcpy(&one, page + 256, 4); memcpy(&cut0, page + 260, 4);