Skip to content

fix(vk): resolve DLSS permanent failure latching and improve dynamic resolution handling - #2574

Merged
NotRexed merged 1 commit into
NotRexed:mainfrom
dani10gl:fix/dlss-dynamic-res-and-recovery
Nov 12, 2026
Merged

NotRexed merged 1 commit into
NotRexed:mainfrom
dani10gl:fix/dlss-dynamic-res-and-recovery

Conversation

@dani10gl

Copy link
Copy Markdown
Contributor

Summary

This PR addresses several issues in the Vulkan temporal upscaler pipeline (vk_temporal.cpp, vk_dlss.cpp, vk_menu.cpp, dlss_checks.h) where NVIDIA DLSS would either fail to instantiate during dynamic resolution scaling (DRS) or permanently latch into a disabled state (engine.failed = true), silently falling back to bilinear upscaling without any user-facing recovery path.


Problem Description & Root Causes

  1. Permanent Failure Latching in vk_temporal.cpp:

    • When a transient evaluation error occurs (e.g. during screen transition, window resize, or dynamic resolution shift), vk_temporal.cpp unconditionally sets engine.failed = true.
    • Once latched, temporal_upscale() perpetually rejects all future frames with "it failed earlier".
    • Changing options in the Insert menu (switching between presets, render resolutions, or upscaler modes) never cleared this state, leaving DLSS dead for the remainder of the application lifecycle.
  2. Off-by-One Rejection in dlss_checks.h:

    • In scene_size_fits(), (uint64_t)w * 3 >= ow fails for fractional roundings at 33% Ultra Performance scaling (e.g., $853 \times 3 = 2559 < 2560$).
    • A tolerance margin of 2 pixels prevents false rejections of valid render targets.
  3. retry flag left false for DLSS in vk_dlss.cpp:

    • TemporalEngine contains a retry boolean intended for transient evaluation drops. Setting this to true allows DLSS to retry on subsequent frames if a single frame evaluation fails, rather than permanently falling back.

Changes

  • gpu/vk/dlss_checks.h:

    • Adjusted scene_size_fits() to (uint64_t)(w + 2) * 3 >= ow && (uint64_t)(h + 2) * 3 >= oh to safely handle rounding in fractional 3x downscaling.
  • gpu/vk/vk_dlss.cpp:

    • Set retry = true in TemporalEngine kEngine for DLSS to ensure transient evaluation failures reset history on the next frame instead of shutting down the engine permanently.
  • gpu/vk/vk_temporal.h & gpu/vk/vk_temporal.cpp:

    • Implemented void temporal_reset_failure() to clear engine.failed across active engines, reset s.reason, and allow clean reinitialization.
  • gpu/vk/vk_menu.cpp:

    • Hooked temporal_reset_failure() into set_row() when modifying opts.dlss, opts.resolution, or opts.dlss_model. Modifying scaling settings in the Insert menu now cleanly clears previous errors and hot-reloads the upscaler.

Verification

  • Tested with Hookshot v2.1.0 on Vulkan with an NVIDIA RTX GPU using DLSS runtime nvngx_dlss.dll (v310.9.16).
  • Verified seamless switching between:
    • Dynamic Resolution ("Auto") and fixed targets (33%, 50%, 58%, 67%, 75%, 83%, 100%).
    • DLSS models (Presets K, M, L, Default).
    • Switching between FSR 3.1 and DLSS without image degradation or permanent fallback to bilinear scaling.
  • Compiled clean on LLVM-MinGW x86_64 with zero warnings or errors.

@dani10gl dani10gl changed the title vk: fix DLSS permanent failure latching and improve dynamic resolution handling fix(vk): resolve DLSS permanent failure latching and improve dynamic resolution handling Nov 12, 2026
@NotRexed
NotRexed merged commit cd3d504 into NotRexed:main Nov 12, 2026
1 of 2 checks passed
unixman69 added a commit to unixman69/Hookshot that referenced this pull request Nov 12, 2026
NotRexed#2574 gave vk_dlss.cpp's engine its fsr_depth and retry fields positionally after hdr, so
test_hdr_encode's check for "..., memory, hdr}" failed on main. It now takes hdr followed by
either the brace or a comma, and still fails when the engine leaves hdr out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
NotRexed added a commit that referenced this pull request Nov 12, 2026
`test_hdr_encode` still fails on main: DLSS's `TemporalEngine` gained
two fields after `hdr` (#2574), and the check looked for the list ending
there. It now matches the fields it means, whatever follows them. The
rest of #2660 landed separately (#2651, #2652, #2654), so #2660 is
closed in favour of this.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
dazemc added a commit to dazemc/Hookshot-rs that referenced this pull request Nov 12, 2026
Port of upstream NotRexed#2574. A failed DLSS evaluation now drops the history for
that frame instead of failing for the rest of the run, the 3x
downscaling check tolerates two pixels of rounding, and changing the
Upscaler, Render resolution or DLSS model clears a latched failure so the
upscaler can come back without a restart.
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