feat(dlss): DLSS for the game's native HDR, given linear light #2243

Closed
j64esatxzdrhfg wants to merge 3 commits from j64esatxzdrhfg/feat/dlss-native-hdr into main
j64esatxzdrhfg commented 2026-11-11 10:40:02 +00:00 (Migrated from github.com)

With HDR on Native and DLSS as the upscaler, every highlight is cut at the paper white: DLSS's feature is made for LDR colour and the chain's inputs pass clamps the scene to 1 before DLSS sees it. The hole of light in the Deku Tree turns into a flat disc.

Builds on #1942 (which builds on #1941): this PR is the last commit. It is the DLSS-for-Native part that was split out of #1940, reworked as reviewed there: keyed on what the game presents, and given linear light.

What changed

  • vk_dlss.cpp, vk_temporal.cpp/.h: while present_native_pq says the game presents its own PQ picture, DLSS's feature is made with IsHDR | AutoExposure and made again when that changes. The gate is the presented picture, not the option and an HDR10 swapchain; with Native picked and an SDR picture shown, the scene is LDR. The engine reports it through TemporalEngine::hdr; FSR 4 has none and keeps the LDR pair. present_native_pq moves out of vk_replay.cpp's anonymous namespace for that.
  • The chain then runs a second pair of its passes:
    • dlss_prep.comp's kSquare (constant 1) squares the square-root encoded scene into linear light, which NVIDIA documents for IsHDR and from which AutoExposure works out its exposure.
    • Its kColorMax (constant 0) is 16384 in place of 1, well inside the colour image's RGBA16F.
    • dlss_out.comp's kSquare (constant 0) is off for it: the network's picture is linear already and is written as it is, at the same output scale as the LDR pair.
  • shaders/dlss_prep_comp_spv.h, dlss_out_comp_spv.h: glslang 16.6.0, --target-env vulkan1.3 (the command tests/test_dlss_actor.py checks them with).
  • RX_VK_DLSS_HDR=0 keeps the LDR feature and the clamped colour with Native too, for comparisons (docs/development.md).
  • Tests: test_hdr_encode.py checks the two constants in each shader, the HDR pipelines and their specialization data, that temporal_upscale binds the HDR pair for an engine whose hdr() says so, and that prepare keys the feature on present_native_pq (and the switch), not on the option.

Testing

  • In game: RTX 5080, 3840x2160 HDR10, 800 nits peak, 330 nits paper white, Native at 100% contrast, DLSS Balanced, in a build of main (d7a7306) with #1941, #1942 and this PR. Same spot, the Deku Tree's light shaft:
    • With RX_VK_DLSS_HDR=0, as on main, the brightest light was 328 nits, which is the paper white. The 99.9th percentile was 328.
    • With this change it reached 749 nits (99.9th percentile 654), and the light's structure is back.
    • The log says HDR colour, linear (the game's native HDR) when the feature is made.
    • This answers the review's question on #1940 about the largest scene value: in this scene the game's grade does go well past the paper white once DLSS stops clamping.
  • vk_dlss.cpp compiles with and without NGX; the touched files compile with the build's clang. tests/run_all.py with hdr_encode, dlss_actor, dlss_checks, upscale_independent, framegen_resources, present_, temporal_history, status_line and menu pass, except recorder_sync and present_semaphores, which fail the same way on main (this PC's toolchain).

Not tested: other NVIDIA generations, DLAA with Native, displays with another peak.

Screenshots (Native at 100%, DLSS Balanced; HDR10 captures tone-mapped to SDR for this page, paper white to white, highlights rolled off up to 800 nits)

Before (RX_VK_DLSS_HDR=0, as on main): 328 nits at most After: 749 nits at most
DLSS with Native HDR before: the light clipped at the paper white DLSS with Native HDR after: the light graded up to 749 nits

The hole of light at full size, before and after:

The light shaft: a flat disc before, its structure after

🤖 Generated with Claude Code

With HDR on Native and DLSS as the upscaler, every highlight is cut at the paper white: DLSS's feature is made for LDR colour and the chain's inputs pass clamps the scene to 1 before DLSS sees it. The hole of light in the Deku Tree turns into a flat disc. > Builds on #1942 (which builds on #1941): this PR is the last commit. It is the DLSS-for-Native part that was split out of #1940, reworked as reviewed there: keyed on what the game presents, and given linear light. **What changed** - `vk_dlss.cpp`, `vk_temporal.cpp/.h`: while `present_native_pq` says the game presents its own PQ picture, DLSS's feature is made with `IsHDR | AutoExposure` and made again when that changes. The gate is the presented picture, not the option and an HDR10 swapchain; with Native picked and an SDR picture shown, the scene is LDR. The engine reports it through `TemporalEngine::hdr`; FSR 4 has none and keeps the LDR pair. `present_native_pq` moves out of `vk_replay.cpp`'s anonymous namespace for that. - The chain then runs a second pair of its passes: - `dlss_prep.comp`'s `kSquare` (constant 1) squares the square-root encoded scene into linear light, which NVIDIA documents for `IsHDR` and from which `AutoExposure` works out its exposure. - Its `kColorMax` (constant 0) is 16384 in place of 1, well inside the colour image's RGBA16F. - `dlss_out.comp`'s `kSquare` (constant 0) is off for it: the network's picture is linear already and is written as it is, at the same output scale as the LDR pair. - `shaders/dlss_prep_comp_spv.h`, `dlss_out_comp_spv.h`: glslang 16.6.0, `--target-env vulkan1.3` (the command `tests/test_dlss_actor.py` checks them with). - `RX_VK_DLSS_HDR=0` keeps the LDR feature and the clamped colour with Native too, for comparisons (docs/development.md). - Tests: `test_hdr_encode.py` checks the two constants in each shader, the HDR pipelines and their specialization data, that `temporal_upscale` binds the HDR pair for an engine whose `hdr()` says so, and that `prepare` keys the feature on `present_native_pq` (and the switch), not on the option. **Testing** - In game: RTX 5080, 3840x2160 HDR10, 800 nits peak, 330 nits paper white, Native at 100% contrast, DLSS Balanced, in a build of main (d7a7306) with #1941, #1942 and this PR. Same spot, the Deku Tree's light shaft: - With `RX_VK_DLSS_HDR=0`, as on main, the brightest light was **328 nits**, which is the paper white. The 99.9th percentile was 328. - With this change it reached **749 nits** (99.9th percentile 654), and the light's structure is back. - The log says `HDR colour, linear (the game's native HDR)` when the feature is made. - This answers the review's question on #1940 about the largest scene value: in this scene the game's grade does go well past the paper white once DLSS stops clamping. - `vk_dlss.cpp` compiles with and without NGX; the touched files compile with the build's clang. `tests/run_all.py` with `hdr_encode`, `dlss_actor`, `dlss_checks`, `upscale_independent`, `framegen_resources`, `present_`, `temporal_history`, `status_line` and `menu` pass, except `recorder_sync` and `present_semaphores`, which fail the same way on main (this PC's toolchain). **Not tested:** other NVIDIA generations, DLAA with Native, displays with another peak. **Screenshots** (Native at 100%, DLSS Balanced; HDR10 captures tone-mapped to SDR for this page, paper white to white, highlights rolled off up to 800 nits) | Before (`RX_VK_DLSS_HDR=0`, as on main): 328 nits at most | After: 749 nits at most | |---|---| | ![DLSS with Native HDR before: the light clipped at the paper white](https://raw.githubusercontent.com/j64esatxzdrhfg/Hookshot/pr-screenshots/dlss-native/before.jpg) | ![DLSS with Native HDR after: the light graded up to 749 nits](https://raw.githubusercontent.com/j64esatxzdrhfg/Hookshot/pr-screenshots/dlss-native/after.jpg) | The hole of light at full size, before and after: ![The light shaft: a flat disc before, its structure after](https://raw.githubusercontent.com/j64esatxzdrhfg/Hookshot/pr-screenshots/dlss-native/light-before-after.jpg) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 20:07:25 +00:00
Joey-Magnus (Migrated from github.com) left a comment

I think this one is already merged, by another route. #2342 (FSR 4 for the game's native HDR) was stacked on this PR, and its squash merge, a0dfd28b on main (11 Nov), carried the whole stack: #1941, #1942 and this commit. I checked at this PR's head against main:

  • the shaders and their SPIR-V headers (dlss_prep, dlss_out, hdr_encode, fg_scale, the new fg_scale_pq_comp_spv.h) and vk_temporal.h are byte-identical on main;
  • the changes this PR makes to vk_dlss.cpp, vk_temporal.cpp, vk_replay.cpp and vk_framegen.cpp are exactly the ones a0dfd28b made;
  • tests/test_hdr_encode.py differs only by #2342's own check_fsr4 additions;
  • RX_VK_DLSS_HDR=0 and the present_native_pq gate in prepare are on main (gpu/vk/vk_dlss.cpp:308-310).

That is also why GitHub shows conflicts in vk_replay.cpp, docs/development.md and tests/test_hdr_encode.py: main has the same lines plus what came after. Unless there is something in this branch I missed that isn't in a0dfd28b, this PR can be closed as merged through #2342, and #1941 and #1942 probably too (worth a quick check of each before closing). If a follow-up is still needed, a fresh branch from main is easier than rebasing this stack.

One note for the changelog: the release notes take their line from the squash title, so the DLSS part shipped under feat(fsr4): .... If DLSS for native HDR should appear in the release notes, it needs a line added there by hand.

I think this one is already merged, by another route. #2342 (FSR 4 for the game's native HDR) was stacked on this PR, and its squash merge, a0dfd28b on main (11 Nov), carried the whole stack: #1941, #1942 and this commit. I checked at this PR's head against main: - the shaders and their SPIR-V headers (`dlss_prep`, `dlss_out`, `hdr_encode`, `fg_scale`, the new `fg_scale_pq_comp_spv.h`) and `vk_temporal.h` are byte-identical on main; - the changes this PR makes to `vk_dlss.cpp`, `vk_temporal.cpp`, `vk_replay.cpp` and `vk_framegen.cpp` are exactly the ones a0dfd28b made; - `tests/test_hdr_encode.py` differs only by #2342's own `check_fsr4` additions; - `RX_VK_DLSS_HDR=0` and the `present_native_pq` gate in `prepare` are on main (`gpu/vk/vk_dlss.cpp:308-310`). That is also why GitHub shows conflicts in `vk_replay.cpp`, `docs/development.md` and `tests/test_hdr_encode.py`: main has the same lines plus what came after. Unless there is something in this branch I missed that isn't in a0dfd28b, this PR can be closed as merged through #2342, and #1941 and #1942 probably too (worth a quick check of each before closing). If a follow-up is still needed, a fresh branch from main is easier than rebasing this stack. One note for the changelog: the release notes take their line from the squash title, so the DLSS part shipped under `feat(fsr4): ...`. If DLSS for native HDR should appear in the release notes, it needs a line added there by hand.
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 21:24:29 +00:00
Joey-Magnus (Migrated from github.com) left a comment

This looks already merged: #2342's squash (a0dfd28b) shipped it. All nine shader and SPIR-V header files and vk_temporal.h are identical to main, and a test merge leaves no difference in the files that merged cleanly. I think this can be closed. Since it went out under a feat(fsr4) title, DLSS with native HDR probably needs a hand-written line in the next release notes.

This looks already merged: #2342's squash (`a0dfd28b`) shipped it. All nine shader and SPIR-V header files and `vk_temporal.h` are identical to main, and a test merge leaves no difference in the files that merged cleanly. I think this can be closed. Since it went out under a `feat(fsr4)` title, DLSS with native HDR probably needs a hand-written line in the next release notes.
hckrman101 commented 2026-11-11 22:55:46 +00:00 (Migrated from github.com)

This now conflicts with main in docs/development.md, gpu/vk/shaders/dlss_prep.comp, gpu/vk/shaders/dlss_prep_comp_spv.h, gpu/vk/vk_menu.cpp, gpu/vk/vk_menu.h, gpu/vk/vk_replay.cpp, gpu/vk/vk_temporal.cpp, gpu/vk/vk_temporal.h, tests/test_hdr_encode.py. Could you merge main into the branch (or rebase) when you get a chance? I'll take another look once it's up to date.

This now conflicts with main in `docs/development.md`, `gpu/vk/shaders/dlss_prep.comp`, `gpu/vk/shaders/dlss_prep_comp_spv.h`, `gpu/vk/vk_menu.cpp`, `gpu/vk/vk_menu.h`, `gpu/vk/vk_replay.cpp`, `gpu/vk/vk_temporal.cpp`, `gpu/vk/vk_temporal.h`, `tests/test_hdr_encode.py`. Could you merge main into the branch (or rebase) when you get a chance? I'll take another look once it's up to date.
github-sync closed this pull request 2026-11-12 09:32:10 +00:00
Owner

@j64esatxzdrhfg commented on GitHub · 2026-11-12 08:33 UTC · original

Closing as landed: #2342's squash merge (a0dfd28) carried this commit, RX_VK_DLSS_HDR included. Thanks @Joey-Magnus for checking. Agreed on the release notes: since it went out under a feat(fsr4) title, "DLSS with Native HDR gets the game's HDR colour as linear light (highlights no longer clipped at the paper white)" deserves its own line.

> [@j64esatxzdrhfg](https://github.com/j64esatxzdrhfg) commented on GitHub · 2026-11-12 08:33 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2243#issuecomment-6818775868) Closing as landed: #2342's squash merge (a0dfd28) carried this commit, `RX_VK_DLSS_HDR` included. Thanks @Joey-Magnus for checking. Agreed on the release notes: since it went out under a `feat(fsr4)` title, "DLSS with Native HDR gets the game's HDR colour as linear light (highlights no longer clipped at the paper white)" deserves its own line. <!-- github-sync:c:6818775868 -->
Commenting is not possible because the repository is archived.
No description provided.