fix(vk): wait for the native scene when switching to DLAA #2145

Closed
Arkex-ai wants to merge 1 commit from Arkex-ai/dlss/native-transition-review-main into main
Arkex-ai commented 2026-11-11 04:58:43 +00:00 (Migrated from github.com)

Summary

Switching from scaled DLSS to Native (DLAA) can create an AA feature for a queued small scene, then recreate it when the native scene arrives. Keep DLSS for up to three scene frames during the transition; if the scene remains smaller than the output, log the fallback and use DLAA at that scene size. Fixes #2144.

Technical changes

  • Bound the native-scene transition by texture-frame number. Repeated calls within one frame do not consume the budget; returning to DLSS resets it.
  • Preserve both HDR and native-AA compatibility when reusing the DLSS feature. Pass AA intent through the internal temporal callback, adapting the FSR 3.1 and FSR 4 callbacks without changing their behavior or the public renderer ABI.
  • Extend the extracted-source regression with a permanently undersized scene, same-frame calls, cancellation/re-entry and HDR on/off feature recreation.

Validation

Current head 278bea75, rebased on 51da0464:

Earlier-head local evidence and limits

At 8c5ea102, the bounded-wait regression passes on Windows and Linux and fails on the rebased unbounded parent. Its temporal/DLSS/fixture input hashes are unchanged on the final head; the final follow-up adapts the FSR 3.1 callback signature.

Full suites at 8c5ea102: Windows 451 passed, 7 failed, 38 skipped; Linux 442 passed, 6 failed, 48 skipped. Subsequent controls reran the failed checks on candidate and clean 51da0464 with the same configuration: Windows 4 passed, 3 failed on each, with matching failure signatures; Linux 6 passed, 0 failed on each. These partial reruns are not a replacement full-suite result.

Earlier gameplay at 2ec8aa66 verified Performance-to-Native and return from inventory with one native DLAA feature creation. This predates the bounded-wait and HDR rebase changes. No universal stall reduction, other-GPU, Linux GPU, campaign or input-latency claim.

## Summary Switching from scaled DLSS to Native (DLAA) can create an AA feature for a queued small scene, then recreate it when the native scene arrives. Keep DLSS for up to three scene frames during the transition; if the scene remains smaller than the output, log the fallback and use DLAA at that scene size. Fixes #2144. ## Technical changes - Bound the native-scene transition by texture-frame number. Repeated calls within one frame do not consume the budget; returning to DLSS resets it. - Preserve both HDR and native-AA compatibility when reusing the DLSS feature. Pass AA intent through the internal temporal callback, adapting the FSR 3.1 and FSR 4 callbacks without changing their behavior or the public renderer ABI. - Extend the extracted-source regression with a permanently undersized scene, same-frame calls, cancellation/re-entry and HDR on/off feature recreation. ## Validation Current head **278bea75**, rebased on **51da0464**: - NVIDIA-enabled Windows Vulkan backend compiles and links with zero failures. - Focused DLSS/temporal/FSR 3.1 suite: **12 passed, 0 failed, 1 skipped**. - `git diff --check` passes. Gameplay was not rerun on this head. - **Exact-head CI is green: 12 successful checks, 2 expected release-publication skips.** [Source regressions](https://github.com/NotRexed/Hookshot/actions/runs/43237654892), [Windows builder](https://github.com/NotRexed/Hookshot/actions/runs/43237654815), [NVIDIA Vulkan backend](https://github.com/NotRexed/Hookshot/actions/runs/43237654823), [Linux AppImage](https://github.com/NotRexed/Hookshot/actions/runs/43237654987), [PR title](https://github.com/NotRexed/Hookshot/actions/runs/43237933461). <details> <summary>Earlier-head local evidence and limits</summary> At **8c5ea102**, the bounded-wait regression passes on Windows and Linux and fails on the rebased unbounded parent. Its temporal/DLSS/fixture input hashes are unchanged on the final head; the final follow-up adapts the FSR 3.1 callback signature. Full suites at **8c5ea102**: Windows **451 passed, 7 failed, 38 skipped**; Linux **442 passed, 6 failed, 48 skipped**. Subsequent controls reran the failed checks on candidate and clean **51da0464** with the same configuration: Windows **4 passed, 3 failed** on each, with matching failure signatures; Linux **6 passed, 0 failed** on each. These partial reruns are not a replacement full-suite result. Earlier gameplay at **2ec8aa66** verified Performance-to-Native and return from inventory with one native DLAA feature creation. This predates the bounded-wait and HDR rebase changes. No universal stall reduction, other-GPU, Linux GPU, campaign or input-latency claim. </details>
hckrman101 (Migrated from github.com) reviewed 2026-11-11 16:31:02 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks, the diagnosis is clear and the fix is small. On the question of stalls: there's no actual wait here, just a per-frame choice of which mode to draw with, made under the s.mu lock temporal_upscale already takes, with no new lock and nothing that blocks on the render thread. So I don't see a deadlock or a stalled frame. The feature-reuse change in vk_dlss.cpp (native_aa / feature_aa) also looks right: the one recreation happens when the full-size scene arrives, and an already-DLAA feature is reused.

One thing I'd like closed before this goes in (inline): the wait can latch for good. It only ends when rw == ow && rh == oh, and drawn_mode stays DLSS while it waits, so if the full-scale scene is ever smaller than the gameplay output, a DLSS -> DLAA switch keeps drawing DLSS indefinitely and nothing says so in the log. The native path clearly supports that case ("its picture then scaled to the output's by dlss_out.comp", and your fixture's startup case 5 draws DLAA at 1720x720 into 3440x1440). Bounding the wait by a few frames from the switch, plus a log line when it gives up, would make it safe whatever the sizes turn out to be.

Overlaps: #2193 rewrites the mode selection right below the why chain you add a line to, and #2118 touches temporal_upscale next to the prepare call and the s.last_frame = now line. Expect small textual conflicts with whichever merges first. I don't see a semantic clash: with #2193 mode is still DLAA when aa, so feature_aa keeps working.

CI is green on 99ad842e, and the new test is picked up by run_all.py through RUN_ALL_ARGS.

Thanks, the diagnosis is clear and the fix is small. On the question of stalls: there's no actual wait here, just a per-frame choice of which mode to draw with, made under the `s.mu` lock `temporal_upscale` already takes, with no new lock and nothing that blocks on the render thread. So I don't see a deadlock or a stalled frame. The feature-reuse change in `vk_dlss.cpp` (`native_aa` / `feature_aa`) also looks right: the one recreation happens when the full-size scene arrives, and an already-DLAA feature is reused. One thing I'd like closed before this goes in (inline): the wait can latch for good. It only ends when `rw == ow && rh == oh`, and `drawn_mode` stays `DLSS` while it waits, so if the full-scale scene is ever smaller than the gameplay output, a DLSS -> DLAA switch keeps drawing DLSS indefinitely and nothing says so in the log. The native path clearly supports that case ("its picture then scaled to the output's by dlss_out.comp", and your fixture's startup case 5 draws DLAA at 1720x720 into 3440x1440). Bounding the wait by a few frames from the switch, plus a log line when it gives up, would make it safe whatever the sizes turn out to be. Overlaps: #2193 rewrites the mode selection right below the `why` chain you add a line to, and #2118 touches `temporal_upscale` next to the `prepare` call and the `s.last_frame = now` line. Expect small textual conflicts with whichever merges first. I don't see a semantic clash: with #2193 `mode` is still DLAA when `aa`, so `feature_aa` keeps working. CI is green on 99ad842e, and the new test is picked up by `run_all.py` through `RUN_ALL_ARGS`.
hckrman101 (Migrated from github.com) commented 2026-11-11 16:31:02 +00:00

This only ends when the scene is exactly the output's size. Because s.drawn_mode = draw_mode stores DLSS while waiting, entering_dlaa stays true on the next frame, so the wait has no other way out. If the full-scale scene is smaller than the output (a case the native path handles, see fixture case 5), DLAA never engages after a switch from DLSS, while starting directly in DLAA would use it. Could you cap it, e.g. record the frame the switch was first seen and stop waiting after 2 or 3 frames, with a logf when it gives up? A fixture step for 'DLSS -> DLAA where the scene stays below the output' would cover it.

This only ends when the scene is exactly the output's size. Because `s.drawn_mode = draw_mode` stores `DLSS` while waiting, `entering_dlaa` stays true on the next frame, so the wait has no other way out. If the full-scale scene is smaller than the output (a case the native path handles, see fixture case 5), DLAA never engages after a switch from DLSS, while starting directly in DLAA would use it. Could you cap it, e.g. record the frame the switch was first seen and stop waiting after 2 or 3 frames, with a `logf` when it gives up? A fixture step for 'DLSS -> DLAA where the scene stays below the output' would cover it.
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 21:25:47 +00:00
Joey-Magnus (Migrated from github.com) left a comment

Needs a rebase: there are three conflicts in vk_dlss.cpp with #2342 (s.feature_hdr against s.feature_aa). Keep both: s.feature_hdr == hdr && (!native_aa || s.feature_aa). tests/test_dlss_native_transition.py cuts that condition out by text, so it will need stubs for hdr/s.feature_hdr.

The earlier blocking point is unfixed: waiting_for_native (vk_temporal.cpp:883) only ends when the render size equals the output size, and drawn_mode stays DLSS while it waits (:1000). If the full-scale scene is ever smaller than the output, DLAA never takes effect and nothing is logged. Please cap the wait at a few frames and log when it gives up.

Needs a rebase: there are three conflicts in `vk_dlss.cpp` with #2342 (`s.feature_hdr` against `s.feature_aa`). Keep both: `s.feature_hdr == hdr && (!native_aa || s.feature_aa)`. `tests/test_dlss_native_transition.py` cuts that condition out by text, so it will need stubs for `hdr`/`s.feature_hdr`. The earlier blocking point is unfixed: `waiting_for_native` (`vk_temporal.cpp:883`) only ends when the render size equals the output size, and `drawn_mode` stays DLSS while it waits (`:1000`). If the full-scale scene is ever smaller than the output, DLAA never takes effect and nothing is logged. Please cap the wait at a few frames and log when it gives up.
Owner

@Arkex-ai commented on gpu/vk/vk_temporal.cpp line 883 on GitHub · 2026-11-12 14:30 UTC · original

Fixed in 278bea75: the transition now allows three texture frames, then logs the fallback and uses DLAA at the available scene size. Same-frame calls do not consume the budget, and cancelling/re-entering starts a fresh budget. The permanently undersized-scene regression fails without the bound and passes with it on Windows/Linux.

Rebased on 51da0464, preserving both feature_hdr == hdr and native-AA reuse checks, with HDR on/off coverage in the fixture. FSR 3.1/4 internal callbacks are adapted too. The NVIDIA-enabled backend links and the focused suite passes; exact-head CI is running.

> [@Arkex-ai](https://github.com/Arkex-ai) commented on `gpu/vk/vk_temporal.cpp` line 883 on GitHub · 2026-11-12 14:30 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2145#discussion_r4551767869) Fixed in 278bea75: the transition now allows three texture frames, then logs the fallback and uses DLAA at the available scene size. Same-frame calls do not consume the budget, and cancelling/re-entering starts a fresh budget. The permanently undersized-scene regression fails without the bound and passes with it on Windows/Linux. Rebased on 51da0464, preserving both `feature_hdr == hdr` and native-AA reuse checks, with HDR on/off coverage in the fixture. FSR 3.1/4 internal callbacks are adapted too. The NVIDIA-enabled backend links and the focused suite passes; exact-head CI is running. <!-- github-sync:rc:4551767869 -->
github-sync closed this pull request 2026-11-12 16:54:50 +00:00
Commenting is not possible because the repository is archived.
No description provided.