From 01cb13dc31f933d6af4b3be47ae11ca86176ba1f Mon Sep 17 00:00:00 2001 From: chrg6 Date: Fri, 13 Nov 2026 18:59:10 -0500 Subject: [PATCH] Set, count down and clear vk_dlaa.c's setting_presents with atomics, as vk_dlaa_frame and vk_dlaa_resized_take can run on two threads. setting_presents says how many more presents a new scene size counts as the result of a settings change (the resize hold of vk_backend.c frame_hold). vk_dlaa_frame sets and counts it down, and vk_dlaa_resized_take tests and clears it at the same present. With the replay on the submitting threads the frame's bookkeeping, vk_dlaa_frame among it, can run on another thread than the presenting one (another thread's replay, or the next submission once the bookkeeping goes in replay order), so a plain test-then-clear beside a plain decrement could take the hold twice or miss it. Now the take is two exchanges, which give the outcome of the old two statements, and the count-down is a compare-and-swap that stops at 0, so it never goes below a clear made meanwhile. Not reproduced before the change. Checks that passed: pass_cc_check of vk_dlaa.c with no new -Wall -Wextra warnings. Co-Authored-By: Claude Opus 5.5 --- runtime/vk_dlaa.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/runtime/vk_dlaa.c b/runtime/vk_dlaa.c index 9c5ca9c5..7bda4300 100644 --- a/runtime/vk_dlaa.c +++ b/runtime/vk_dlaa.c @@ -3244,15 +3244,16 @@ static void preset_check(void) { // intact (Ultra Performance and DLAA). Measured by capturing every present over six mode changes, 2026-11-10: bad // frames 3 and 5 presents after the change, with every other setting the same. evaluate notes a scene of another size; // vk_dlaa_frame counts the presents since a setting last changed, so a game that draws two sizes in turn holds nothing. -static int setting_presents; // vk_dlaa_frame: presents left in which that counts as a mode change +// setting_presents: presents left in which that counts as a mode change. vk_dlaa_frame sets and counts it down on the +// thread that runs the frame's bookkeeping, vk_dlaa_resized_take clears it on the presenting thread: atomics. +static int setting_presents; #define SETTING_PRESENTS 60 // vk_backend.c frame_hold, each present after vk_dlaa_frame: 1 once when the scene took a new size after a setting // changed. The previous picture then stays on screen over the frames described above. int vk_dlaa_resized_take(void) { - if (!__atomic_exchange_n(&size_changed, 0, __ATOMIC_ACQ_REL) || !setting_presents) return 0; - setting_presents = 0; - return 1; + if (!__atomic_exchange_n(&size_changed, 0, __ATOMIC_ACQ_REL)) return 0; + return __atomic_exchange_n(&setting_presents, 0, __ATOMIC_ACQ_REL) != 0; } // What DLSS holds while nothing uses it (NX2NAVI_DLSS_IDLE_FREE 1 or 2, the default 1). With the upscaler switched off @@ -3423,8 +3424,13 @@ void vk_dlaa_frame(void) { static int cfg_known; unsigned v = cfg_version(); D.presents++; - if (cfg_known && v != cfg_seen) setting_presents = SETTING_PRESENTS; - else if (setting_presents) setting_presents--; + if (cfg_known && v != cfg_seen) { + __atomic_store_n(&setting_presents, SETTING_PRESENTS, __ATOMIC_RELAXED); + } else { // down to 0, not past it: vk_dlaa_resized_take may clear it meanwhile + int s = __atomic_load_n(&setting_presents, __ATOMIC_RELAXED); + while (s > 0 && + !__atomic_compare_exchange_n(&setting_presents, &s, s - 1, 1, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {} + } cfg_seen = v, cfg_known = 1; vk_dlaa_swap_img = NULL, vk_dlaa_swap_view = VK_NULL_HANDLE; D.pre = D.comp = 0, D.comp_game = VK_NULL_HANDLE; // the frame before was recorded up to its present