Skip to content

fix(vk): a DLSS feature that fails to be made is made again at the next DLSS change, not never - #2550

Merged
NotRexed merged 3 commits into
NotRexed:mainfrom
SirenityK:fix/dlss-recover-failed-feature
Nov 12, 2026
Merged

NotRexed merged 3 commits into
NotRexed:mainfrom
SirenityK:fix/dlss-recover-failed-feature

Conversation

@SirenityK

Copy link
Copy Markdown
Contributor

Switching the DLSS model quickly in the menu can turn DLSS off for the rest of the run: the picture falls back to the game's own upscale (sharp, aliased hair and edges), going back to the first model does nothing, and only a restart brings DLSS back, with no message saying so.

Why it happens

Each DLSS model change makes a new NGX feature; the one it replaces is let go 4 frames later (temporal_later). Switching faster than that keeps several alive at once: DLSS's own memory went from 143 MB to 1173 MB and VRAM to 7.6 of 6.8 GB, and NGX then refused the next one:

vk: NVIDIA DLSS: feature 19 made (preset L, was M (the DLSS model row); frame 129670, 3.7 ms of CPU): ... preset L
vk: NVIDIA DLSS: its feature was not made (0xbad00002) for 2560x1440 -> 2560x1440
vk: NVIDIA DLSS: the game's upscale runs (its feature was not made)
vk: NVIDIA DLSS: the game's upscale runs (it failed earlier)
vk: NVIDIA DLSS: the game's upscale runs (it failed earlier)

(0xbad00002 is NVSDK_NGX_Result_FAIL_PlatformError.) prepare returned -1 for a feature that was not made, and the chain (vk_temporal.cpp) takes -1 as a failed engine: engine.failed stays set for the run.

What changed

gpu/vk/vk_dlss.cpp prepare: a feature that is not made returns 0 (the game's upscale this frame) instead of -1, and remembers the preset, output and HDR it failed for. It is not tried again every frame (each try is a ~30 ms creation), but as soon as any of them changes, e.g. another DLSS model picked in the menu, it is made again. A success clears it.

tests/test_dlss_checks.py: checks that prepare no longer returns -1 for it and keeps the retry key.

Not in this PR: letting the replaced feature go sooner, so quick switching does not pile up video memory in the first place.

Screenshots

2560x1440, DLAA, after quick DLSS model switches. Before: the game's upscale for the rest of the run, whichever model is picked. After: DLSS back on the next model change.

Before After
Before After

Testing

RTX 3060 Ti LHR (8 GB), driver 32.0.16.1062, Windows 11, 2560x1440, DLAA, Kokiri Forest. The same change built onto v2.1.0's renderer (RX_VK_ONLY=1, the installed game's interface version), switching the DLSS model quickly in play:

vk: NVIDIA DLSS: feature 13 made (preset 0, was L (the DLSS model row); frame 16186, 29.2 ms of CPU): ...
vk: NVIDIA DLSS: its feature was not made (0xbad00002) for 2560x1440 -> 2560x1440
vk: NVIDIA DLAA: the game's upscale runs (its feature was not made)
vk: NVIDIA DLSS: feature 14 made (again after it was let go; frame 16197, 3.9 ms of CPU): ... preset M
vk: NVIDIA DLSS: feature 15 made (preset K, was M (the DLSS model row); frame 16199, 29.1 ms of CPU): ... preset K

DLSS came back on the next model change, without a restart, and kept working through feature 25. python -I -B tests/test_dlss_checks.py passes. tests/run_all.py on Windows: 387 passed, 5 failed (test_fpx_sticky, test_prepare_at_build, nx2/test_nup_paths, test_bake_portability, test_vram_budget_row), the same 5 failing on main without this change on this PC.

…xt DLSS change, not never

Switching the DLSS model quickly in the menu can run the GPU out of video memory while the replaced
features wait to be let go; NGX then refuses the new one (0xbad00002, NVSDK_NGX_Result_FAIL_PlatformError).
prepare returned -1 for that, which the chain takes as a failed engine for the rest of the run: the
game's upscale from then on, with no message, until a restart. It now returns 0 (the game's upscale
this frame) and remembers the preset and output that failed, so it is not tried every frame but is
made again once either changes.
@NotRexed
NotRexed merged commit 6770b11 into NotRexed:main Nov 12, 2026
1 check passed
notquiteog added a commit to notquiteog/Hookshot that referenced this pull request Nov 12, 2026
…after one refusal, AMD's history

Found by a review of the merged tree, each confirmed in the code; the game was run
after them (RTX 5060 Ti, 615.71.09, Linux): DLSS at every Render resolution step and
model, DLSS Frame Generation 2x to 5x, the renderer's own, FSR 3.1's interpolation and
upscaler, 42 quick steps of the multiplier, no device lost.

- framegen_record took the multiplier from the row again after ensure_size had made
  the sets: a step up in the menu (the display thread) in between copied the game's
  frame into an image that was never made. It takes fg.mult.
- DLSS: scene_size_fits takes a third rounded down (853x480 for 2560x1440, NotRexed#2574) and
  no less; (w + 2) * 3 let mode_for widen Ultra Performance below a third, which
  upstream's own check turns away.
- DLSS: a feature that was not made (NotRexed#2550) was asked for again only when the output,
  preset or HDR changed. The quality mode is in the key, release() clears it, and the
  rows that call temporal_reset_failure clear it (dlss_retry): Render resolution,
  DLAA, and the Upscaler away and back bring DLSS back.
- DLSS: with retry (NotRexed#2574) a failing evaluation logged on every frame for good. Its
  line is spaced as the chain's reason is, and after 30 in a row the feature is made
  again once.
- Frame generation by: a saved AMD method that does not run here shows as Auto, and
  DLSS Frame Generation now runs at it (fg_asked read the raw value).
- AMD FSR 4 frame generation (Windows; not run here, syntax-checked for mingw): a
  present it did not make is a reset, where its consecutive frame ids hid the gap; a
  context that could not be made no longer leaves the row at AMD FSR 4.
- The row's AMD FSR 3.1 keeps the scene's inputs only at 2x, the one set it makes.
- The four upscaler checks red on upstream's main pass (test_dlss_checks,
  test_hdr_encode, test_framegen_preset, test_rumble_menu).

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
dazemc added a commit to dazemc/Hookshot-rs that referenced this pull request Nov 12, 2026
Port of upstream NotRexed#2550. Quick DLSS model switches can leave several NGX
features alive; a creation then fails out of video memory and prepare()
returned -1, which the temporal chain took as DLSS failing for the whole
run. It now returns the game's upscale for that frame and remembers the
preset, output and HDR it failed for, so the next menu change makes the
feature again instead of never.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants