From 46bf4823e6548337d835659df6cbb23b09d1968f Mon Sep 17 00:00:00 2001 From: chrg6 Date: Fri, 13 Nov 2026 19:23:43 -0500 Subject: [PATCH] Count Reflex's sleep at the frame start as the pacer's wait only while Reflex holds a frame limit. The review found that vk_dlssg.c vk_dlssg_frame_start timed every held sleep as PERF_PACE, though the sleep takes the pacer's place only while Streamline holds a frame limit. With reflex_limit 0, a DLL older than nx2sl_reflex2, during a movie, after a refused limit or once the frames showed that Reflex does not hold the limit, the sleep is the driver's wait for the GPU while the pacer waits as well, so GPU-bound frames read "bound: pace" in the perf windows (the misattribution P-7 and F-7-04 describe), and reflex_limit 0 did not give back the old attribution. The sleep is now timed only when it holds the frame (as before) and limit_set is not 0; the statistics line's held frame starts and their time are counted as they were. Not reproduced before the change: the compile check and -Wall on vk_dlssg.c (no new warnings), dlssg_pace_check and the test matrix pass. Co-Authored-By: Claude Opus 5.5 --- runtime/vk_dlssg.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/vk_dlssg.c b/runtime/vk_dlssg.c index 705d209c..3c988fe9 100644 --- a/runtime/vk_dlssg.c +++ b/runtime/vk_dlssg.c @@ -334,8 +334,11 @@ VkResult vk_dlssg_present(VkQueue q, const VkPresentInfoKHR *pi) { // while no host present went out for DLSSG_HOLD_IDLE_NS (the window minimized: gfx_present.c skips the host presents // while the game runs on): the driver then has no present to time the frame by, and the sleep would wait out // Streamline's half-second timeout each frame. The guest thread gave its core up for the acquire (NVN_BLOCKING). The -// sleep counts as the pacer's wait (PERF_PACE), whose place it takes while Reflex holds the limit, on whichever guest -// thread acquires (perf_end_any), as the acquire's own waits do. +// sleep counts as the pacer's wait (PERF_PACE) only while Reflex holds a frame limit (limit_set), whose place it then +// takes, on whichever guest thread acquires (perf_end_any), as the acquire's own waits do. Without one (reflex_limit 0, +// an older DLL, a movie, a refused limit, frames that showed Reflex does not hold it) the sleep is only the driver's +// wait for the GPU while the pacer waits as well, so it stays out of PERF_PACE, as before Reflex held a frame limit: +// counted there, GPU-bound frames read as paced in the perf windows. void vk_dlssg_frame_start(void) { if (!S.active) return; uint32_t f = __atomic_load_n(&S.game, __ATOMIC_RELAXED) + 1; @@ -343,13 +346,14 @@ void vk_dlssg_frame_start(void) { S.started = f; uint64_t hosted = __atomic_load_n(&S.hosted_ns, __ATOMIC_ACQUIRE), t0 = kern_now_ns(); // in this order: t0 >= hosted int hold = hosted && t0 - hosted < DLSSG_HOLD_IDLE_NS; - uint64_t p0 = hold ? perf_begin() : 0; + int paced = hold && __atomic_load_n(&S.limit_set, __ATOMIC_ACQUIRE) != 0; + uint64_t p0 = paced ? perf_begin() : 0; S.sleep(f, hold); if (!hold) { __atomic_add_fetch(&S.n_unheld, 1, __ATOMIC_RELAXED); return; } - perf_end_any(PERF_PACE, p0); + if (paced) perf_end_any(PERF_PACE, p0); __atomic_add_fetch(&S.hold_ns, kern_now_ns() - t0, __ATOMIC_RELAXED); __atomic_add_fetch(&S.n_hold, 1, __ATOMIC_RELAXED); }