feat(hdr): PsychoV35 scene tone mapper for Native HDR (RenoDX) #2481

Merged
Joey-Magnus merged 4 commits from feat/hdr-psychov35 into main 2026-11-11 22:07:58 +00:00
Joey-Magnus commented 2026-11-11 20:42:33 +00:00 (Migrated from github.com)

Native HDR currently keeps the game's own tone curve, which clips highlights at the same point the SDR picture does. This adds the scene tone mapper from ShortFuse's RenoDX mod for this game (PsychoV35, MIT) as an opt-in row, so highlights roll off into the display's peak instead.

What changes

  • The scene buffer in RGBA16F with PsychoV35 (first commit, narrowed in b485da0). The game picks R11G11B10F or RGBA16F for its scene buffer in f_80672260; nx2/oot_patches.py makes that pick follow hos_scene_rgba16f, which host/nn_vi.c sets from a new option, HOS_OPT_HDR_TONEMAP (47, scene_tonemap_on(): Native HDR with the row on PsychoV35). That gives the curve unclamped values to work with, as the RenoDX mod does. Native HDR on Vanilla keeps R11G11B10F, as on main, because the temporal upscalers only take that scene format.
  • The HDR tone mapper row (Graphics tab, shown only with HDR set to Native): Vanilla (the default) or PsychoV35. It is saved as hdr_tone_mapper.
  • ir_opt/scene_tonemap_pass.h finds the game's Narkowicz fit in the translated IR and replaces target 0's colour with PsychoV35. It matches on the full structure: three FPFma(x, 2.51, 0.03) whose input is the scene times one constant-buffer exposure, each channel identified by the luminance weight (0.2989, 0.5866, 0.1144) it meets downstream, and an output clamped under a peak word from the same bank. It reuses the game's own exposure and peak reads; anything that doesn't match is left unchanged, with the reason logged.
  • ir_opt/psychov35.h is RenoDX's PsychoV35 reduced to the path the mod takes at its defaults: midgray matched to the game's, the game's tone slope, hue-clip emulation at 50%, neutral grading, the exact BT.2020 fit. Both anchors are grey, so the cone response reduces to one logistic. It is written once over a small set of operations, so the pass emits it as IR and the tests run it on floats.
  • Keys: a fragment program whose code holds the fit's five constants gets its own program key while the row is on. Changing the row then retranslates only those programs (one or two in play) rather than the 38k-program cache, and the per-thread pick caches are cleared when it changes.

Testing

  • tests/test_psychov35.py: the header against RenoDX's scene_tonemap.slangi + psychov/test35.hlsl path ported line by line (log-domain response, residual normalization, matrices from their XYZ definitions): 3009 colours, worst relative difference 2.1e-6 in float32. It also checks the baked matrices against their definitions.
  • tests/test_scene_tonemap_pass.py: builds Eden's IR library, runs the pass on a synthetic scene-mapping program with the channels' fits emitted out of order (and one where red is written before the other fits are made, checking every operand is defined before its use), interprets the resulting IR, and compares it with the header. Programs with no luminance weights, or with different exposures per channel, are left untouched (instruction count and outputs unchanged). It also checks the wiring: key, translation, pick reset, and row visibility.
  • tests/run_all.py on Linux: same results as main on this machine (its 27 failures are missing headers and tools here, and fail on main too). seed_variant_cleanup_test.cpp's stubs were updated for graphics_key's new argument.
  • In game (Linux, RTX 4070 Ti, driver 595.99, in a private KWin 6.6 session, which offers an HDR10 surface; no HDR monitor here): the pass matched the game's scene program on first use (vk: program 672e593076ac390a: scene tone map PsychoV35). Frame rate is unchanged (about 105 fps in both, same scene). Decoding the PQ screenshots to nits at a 200-nit paper white and 1000-nit peak:
Scene Tone mapper Median p90 p99 Brightest
Kokiri Forest Vanilla 2.1 9.8 65.9 656 nits
Kokiri Forest PsychoV35 2.4 9.5 65.9 610 nits
Lost Woods Vanilla 2.0 33.3 59.1 353 nits
Lost Woods PsychoV35 2.1 30.2 60.5 505 nits

The mid-tones and exposure match the game's (midgray matching works), and the highlights move into the HDR range. No pixel exceeded the 1000-nit peak.

Screenshots

Native HDR, the PQ shots decoded to nits and shown through one fixed SDR mapping (an SDR page cannot show the HDR range itself); Vanilla on the left, PsychoV35 on the right.

Kokiri Forest, Vanilla vs PsychoV35
Lost Woods, Vanilla vs PsychoV35

Reach and what is not here

  • Nothing changes unless HDR is set to Native and the new row is set to PsychoV35. Native HDR on Vanilla is exactly as on main, scene buffer format included.
  • DLSS/DLAA, DLSS-G, FSR 4, FSR 3.1 and TAAU don't combine with PsychoV35 yet. Their gates take only the B10G11R11 scene, so with PsychoV35 on the game's own upscale runs, which is what @futondestroyer's log showed. The readme row says so. Teaching those gates RGBA16F is a follow-up that has to agree with #2243 (DLSS for Native HDR). The translator stamp changes (as with any translator change), so every player's program cache is rebuilt once.
  • The seed precompile does not prebuild the PsychoV35 variants; the one or two scene programs are translated on their first frame in play.
  • RenoDX's grading sliders (exposure, highlights, shadows, contrast, saturation, eye adaptation) are fixed at the mod's defaults; exposing them would need a runtime uniform in place of baked constants.
  • Colours outside BT.709 are clamped at 0 on output, because the game's later passes on the scene buffer do not take negative values (the RenoDX mod changes those passes too).
  • @futondestroyer tested c46f8bc on a real HDR display (Windows 11, RTX 4070, 400-nit peak), including switching between Vanilla and PsychoV35 during play: it looked right, with no flicker or black screens. Their log is also what showed the upscalers falling back under Native, which b485da0 fixes for Vanilla. b485da0 itself has not been run in game yet.
Native HDR currently keeps the game's own tone curve, which clips highlights at the same point the SDR picture does. This adds the scene tone mapper from ShortFuse's [RenoDX mod for this game](https://github.com/clshortfuse/renodx/tree/main/src/games/zelda-oot-oz) (PsychoV35, MIT) as an opt-in row, so highlights roll off into the display's peak instead. ## What changes - **The scene buffer in RGBA16F with PsychoV35** (first commit, narrowed in b485da0). The game picks R11G11B10F or RGBA16F for its scene buffer in `f_80672260`; `nx2/oot_patches.py` makes that pick follow `hos_scene_rgba16f`, which `host/nn_vi.c` sets from a new option, `HOS_OPT_HDR_TONEMAP` (47, `scene_tonemap_on()`: Native HDR with the row on PsychoV35). That gives the curve unclamped values to work with, as the RenoDX mod does. Native HDR on Vanilla keeps R11G11B10F, as on main, because the temporal upscalers only take that scene format. - **The HDR tone mapper row** (Graphics tab, shown only with HDR set to Native): Vanilla (the default) or PsychoV35. It is saved as `hdr_tone_mapper`. - **`ir_opt/scene_tonemap_pass.h`** finds the game's Narkowicz fit in the translated IR and replaces target 0's colour with PsychoV35. It matches on the full structure: three `FPFma(x, 2.51, 0.03)` whose input is the scene times one constant-buffer exposure, each channel identified by the luminance weight (0.2989, 0.5866, 0.1144) it meets downstream, and an output clamped under a peak word from the same bank. It reuses the game's own exposure and peak reads; anything that doesn't match is left unchanged, with the reason logged. - **`ir_opt/psychov35.h`** is RenoDX's PsychoV35 reduced to the path the mod takes at its defaults: midgray matched to the game's, the game's tone slope, hue-clip emulation at 50%, neutral grading, the exact BT.2020 fit. Both anchors are grey, so the cone response reduces to one logistic. It is written once over a small set of operations, so the pass emits it as IR and the tests run it on floats. - **Keys**: a fragment program whose code holds the fit's five constants gets its own program key while the row is on. Changing the row then retranslates only those programs (one or two in play) rather than the 38k-program cache, and the per-thread pick caches are cleared when it changes. ## Testing - `tests/test_psychov35.py`: the header against RenoDX's `scene_tonemap.slangi` + `psychov/test35.hlsl` path ported line by line (log-domain response, residual normalization, matrices from their XYZ definitions): 3009 colours, worst relative difference 2.1e-6 in float32. It also checks the baked matrices against their definitions. - `tests/test_scene_tonemap_pass.py`: builds Eden's IR library, runs the pass on a synthetic scene-mapping program with the channels' fits emitted out of order (and one where red is written before the other fits are made, checking every operand is defined before its use), interprets the resulting IR, and compares it with the header. Programs with no luminance weights, or with different exposures per channel, are left untouched (instruction count and outputs unchanged). It also checks the wiring: key, translation, pick reset, and row visibility. - `tests/run_all.py` on Linux: same results as `main` on this machine (its 27 failures are missing headers and tools here, and fail on `main` too). `seed_variant_cleanup_test.cpp`'s stubs were updated for `graphics_key`'s new argument. - **In game** (Linux, RTX 4070 Ti, driver 595.99, in a private KWin 6.6 session, which offers an HDR10 surface; no HDR monitor here): the pass matched the game's scene program on first use (`vk: program 672e593076ac390a: scene tone map PsychoV35`). Frame rate is unchanged (about 105 fps in both, same scene). Decoding the PQ screenshots to nits at a 200-nit paper white and 1000-nit peak: | Scene | Tone mapper | Median | p90 | p99 | Brightest | |---|---|---|---|---|---| | Kokiri Forest | Vanilla | 2.1 | 9.8 | 65.9 | 656 nits | | Kokiri Forest | PsychoV35 | 2.4 | 9.5 | 65.9 | 610 nits | | Lost Woods | Vanilla | 2.0 | 33.3 | 59.1 | 353 nits | | Lost Woods | PsychoV35 | 2.1 | 30.2 | 60.5 | 505 nits | The mid-tones and exposure match the game's (midgray matching works), and the highlights move into the HDR range. No pixel exceeded the 1000-nit peak. ## Screenshots Native HDR, the PQ shots decoded to nits and shown through one fixed SDR mapping (an SDR page cannot show the HDR range itself); Vanilla on the left, PsychoV35 on the right. ![Kokiri Forest, Vanilla vs PsychoV35](https://raw.githubusercontent.com/Joey-Magnus/Hookshot/pr-assets/hdr-psychov35/kokiri-vanilla-vs-psychov35.png) ![Lost Woods, Vanilla vs PsychoV35](https://raw.githubusercontent.com/Joey-Magnus/Hookshot/pr-assets/hdr-psychov35/lostwoods-vanilla-vs-psychov35.png) ## Reach and what is not here - Nothing changes unless HDR is set to Native **and** the new row is set to PsychoV35. Native HDR on Vanilla is exactly as on main, scene buffer format included. - **DLSS/DLAA, DLSS-G, FSR 4, FSR 3.1 and TAAU don't combine with PsychoV35 yet.** Their gates take only the B10G11R11 scene, so with PsychoV35 on the game's own upscale runs, which is what @futondestroyer's log showed. The readme row says so. Teaching those gates RGBA16F is a follow-up that has to agree with #2243 (DLSS for Native HDR). The translator stamp changes (as with any translator change), so every player's program cache is rebuilt once. - The seed precompile does not prebuild the PsychoV35 variants; the one or two scene programs are translated on their first frame in play. - RenoDX's grading sliders (exposure, highlights, shadows, contrast, saturation, eye adaptation) are fixed at the mod's defaults; exposing them would need a runtime uniform in place of baked constants. - Colours outside BT.709 are clamped at 0 on output, because the game's later passes on the scene buffer do not take negative values (the RenoDX mod changes those passes too). - @futondestroyer tested c46f8bc on a real HDR display (Windows 11, RTX 4070, 400-nit peak), including switching between Vanilla and PsychoV35 during play: it looked right, with no flicker or black screens. Their log is also what showed the upscalers falling back under Native, which b485da0 fixes for Vanilla. b485da0 itself has not been run in game yet.
hckrman101 (Migrated from github.com) reviewed 2026-11-11 20:51:37 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks, this is a careful piece of work. The pass matches on structure rather than a hash: three FPFma(x, 2.51, 0.03), the luminance weights, a single cbuf exposure, and a peak from the same bank. Anything else is left alone, with the reason logged. The math header is shared between the IR emitter and the float test, which is a nice way to keep the two honest. With HDR off I see nothing that changes for players. scene_tonemap_on() is false, so the key isn't salted, and hos_scene_rgba16f stays 0, so the lifted pick is the original 0x1a. Apart from the translator stamp's one-time rebuild, that path is identical to main.

Licence. RenoDX is MIT, which is fine inside a GPL-3.0 repo. psychov35.h keeps Carlos Lopez's copyright line and the MIT SPDX tag, and PROVENANCE.md credits both source files. Nit: tests/test_psychov35.py is a line-by-line port too, so it should carry the same copyright/SPDX line. It would also be worth putting the MIT permission text in the tree, as third_party/fidelityfx/LICENSE.md does for AMD's headers.

The scene buffer format is the one thing I'd hold this for (inline on nn_vi.c). hos_scene_rgba16f follows Native HDR alone, not the new row. So every Native HDR player gets an RGBA16F scene buffer even with the tone mapper on Vanilla, and the "Nothing changes unless HDR is set to Native and the new row is set to PsychoV35" in the description isn't quite right. That matters because the temporal upscalers recognise the scene by its format:

  • temporal_note_scene_draw (vk_temporal.cpp) and skin_check (vk_draw.cpp) require the scene target to be B10G11R11.
  • temporal_upscale requires that format for an unknown network's input and output.
  • FSR 3.1 (vk_fsr3_inputs.cpp, vk_fsr3.cpp) and the TAAU replacement (vk_draw.cpp) gate on it too.

If the buffer f_80672260 makes is the scene target or the network's input, DLSS, FSR 4, FSR 3.1 and TAAU will quietly fall back to the game's network under Native HDR. The PR's in-game numbers don't say which upscaler was on. Could you run Native HDR with DLSS (or FSR 4) and check that the log still shows the upscaler taking the gameplay network? If it doesn't, there are two ways out: tie the format to the PsychoV35 row and say upscalers don't combine with it, or teach those gates RGBA16F. Either way, please state the Vanilla/Native change in the description. #2243 (DLSS for Native HDR, open) works in the same area, so the two will need to agree.

IR pass.

  • One robustness point, inline.
  • NaN/inf look handled. Zero cones give -inf logs that resolve to response 0 / deficit 1, every division by zero is behind a Select, and the pass runs before GuardFragmentOutputs, so anything left is guarded (the wiring test checks that order).

Keys/caches. The salted graphics_key keeps the PsychoV35 variants in their own disk-cache entries, so toggling the row can't pick a stale pipeline. Clearing the thread-local picks/prog_fast on a change is right too. Settings use the ini key hdr_tone_mapper with no new numeric option id, so nothing collides with other PRs' ids.

Tests. The three new tests are declared with RUN_ALL_ARGS, need no game, and the scene-format fixture uses made-up lift text. CI is still queued (no runs have finished yet).

Leaving this as a comment for now. Once the upscaler question is answered and the description matches, I'd be happy with it.

Thanks, this is a careful piece of work. The pass matches on structure rather than a hash: three `FPFma(x, 2.51, 0.03)`, the luminance weights, a single cbuf exposure, and a peak from the same bank. Anything else is left alone, with the reason logged. The math header is shared between the IR emitter and the float test, which is a nice way to keep the two honest. With HDR off I see nothing that changes for players. `scene_tonemap_on()` is false, so the key isn't salted, and `hos_scene_rgba16f` stays 0, so the lifted pick is the original 0x1a. Apart from the translator stamp's one-time rebuild, that path is identical to main. **Licence.** RenoDX is MIT, which is fine inside a GPL-3.0 repo. `psychov35.h` keeps Carlos Lopez's copyright line and the MIT SPDX tag, and PROVENANCE.md credits both source files. Nit: `tests/test_psychov35.py` is a line-by-line port too, so it should carry the same copyright/SPDX line. It would also be worth putting the MIT permission text in the tree, as `third_party/fidelityfx/LICENSE.md` does for AMD's headers. **The scene buffer format is the one thing I'd hold this for (inline on nn_vi.c).** `hos_scene_rgba16f` follows Native HDR alone, not the new row. So every Native HDR player gets an RGBA16F scene buffer even with the tone mapper on Vanilla, and the "Nothing changes unless HDR is set to Native and the new row is set to PsychoV35" in the description isn't quite right. That matters because the temporal upscalers recognise the scene by its format: - `temporal_note_scene_draw` (`vk_temporal.cpp`) and `skin_check` (`vk_draw.cpp`) require the scene target to be `B10G11R11`. - `temporal_upscale` requires that format for an unknown network's input and output. - FSR 3.1 (`vk_fsr3_inputs.cpp`, `vk_fsr3.cpp`) and the TAAU replacement (`vk_draw.cpp`) gate on it too. If the buffer f_80672260 makes is the scene target or the network's input, DLSS, FSR 4, FSR 3.1 and TAAU will quietly fall back to the game's network under Native HDR. The PR's in-game numbers don't say which upscaler was on. Could you run Native HDR with DLSS (or FSR 4) and check that the log still shows the upscaler taking the gameplay network? If it doesn't, there are two ways out: tie the format to the PsychoV35 row and say upscalers don't combine with it, or teach those gates RGBA16F. Either way, please state the Vanilla/Native change in the description. #2243 (DLSS for Native HDR, open) works in the same area, so the two will need to agree. **IR pass.** - One robustness point, inline. - NaN/inf look handled. Zero cones give -inf logs that resolve to response 0 / deficit 1, every division by zero is behind a `Select`, and the pass runs before `GuardFragmentOutputs`, so anything left is guarded (the wiring test checks that order). **Keys/caches.** The salted `graphics_key` keeps the PsychoV35 variants in their own disk-cache entries, so toggling the row can't pick a stale pipeline. Clearing the thread-local `picks`/`prog_fast` on a change is right too. Settings use the ini key `hdr_tone_mapper` with no new numeric option id, so nothing collides with other PRs' ids. **Tests.** The three new tests are declared with `RUN_ALL_ARGS`, need no game, and the scene-format fixture uses made-up lift text. CI is still queued (no runs have finished yet). Leaving this as a comment for now. Once the upscaler question is answered and the description matches, I'd be happy with it.
@ -0,0 +1,204 @@
// SPDX-License-Identifier: GPL-3.0-or-later
hckrman101 (Migrated from github.com) commented 2026-11-11 20:51:37 +00:00

Minor robustness point: this inserts PsychoV35 before the first write, but it uses scene[2]/gain from the other channels' fits. MadeOf only checks that each write comes from its own channel's fit, so a program that writes R before computing the B fit would get a use-before-def. The game's shape is safe because each write goes through the luminance of all three fits. Inserting before the last of the three writes (all in one block, already checked) would be safe for any order, since every write's operands dominate it.

Minor robustness point: this inserts PsychoV35 before the *first* write, but it uses `scene[2]`/`gain` from the other channels' fits. `MadeOf` only checks that each write comes from its own channel's fit, so a program that writes R before computing the B fit would get a use-before-def. The game's shape is safe because each write goes through the luminance of all three fits. Inserting before the *last* of the three writes (all in one block, already checked) would be safe for any order, since every write's operands dominate it.
hckrman101 (Migrated from github.com) commented 2026-11-11 20:51:37 +00:00

This follows HOS_OPT_HDR_NATIVE only, so Native HDR with the row on Vanilla also gets the RGBA16F scene buffer (see the main comment about the upscalers' B10G11R11 gates). If the format is only needed for PsychoV35, HOS_OPT_HDR_NATIVE && hdr_tone_mapper == 1 would keep Vanilla Native exactly as on main. That would need a host-visible option for the row. If it's meant for every Native HDR player (less banding), please say so in the description and confirm the upscalers still engage.

This follows `HOS_OPT_HDR_NATIVE` only, so Native HDR with the row on Vanilla also gets the RGBA16F scene buffer (see the main comment about the upscalers' `B10G11R11` gates). If the format is only needed for PsychoV35, `HOS_OPT_HDR_NATIVE && hdr_tone_mapper == 1` would keep Vanilla Native exactly as on main. That would need a host-visible option for the row. If it's meant for every Native HDR player (less banding), please say so in the description and confirm the upscalers still engage.
futondestroyer commented 2026-11-11 21:40:49 +00:00 (Migrated from github.com)

Tested PR #2481 at commit c46f8bc1ae on a real HDR display.

Windows 11, i7-13700F, RTX 4070 12GB, Gigabyte G34WQC A at 3440×1440. Native HDR, 200-nit paper white, 400-nit peak.

PsychoV35 looked great during gameplay. Tested with DLSS, frame generation, and switching between Vanilla and PsychoV35 during play. Everything looked good in each case, with no observed flickering or black screens.

This resolves the Native HDR flickering I experienced on v2.0.14 in the scenarios I tested.

hookshot_log.txt

Tested PR #2481 at commit c46f8bc1ae7e692763192dd55a610cb97b6af2ef on a real HDR display. Windows 11, i7-13700F, RTX 4070 12GB, Gigabyte G34WQC A at 3440×1440. Native HDR, 200-nit paper white, 400-nit peak. PsychoV35 looked great during gameplay. Tested with DLSS, frame generation, and switching between Vanilla and PsychoV35 during play. Everything looked good in each case, with no observed flickering or black screens. This resolves the Native HDR flickering I experienced on v2.0.14 in the scenarios I tested. [hookshot_log.txt](https://github.com/user-attachments/files/34713114/hookshot_log.txt)
hckrman101 (Migrated from github.com) reviewed 2026-11-11 21:55:51 +00:00
hckrman101 (Migrated from github.com) left a comment

I merged main into the branch (2df79c0) so this is mergeable again. The only conflict was nx2/oot_patches.py: main moved the per-file patching into patch_file() / report() (#2442), so the scene-format hook now sits in patch_file() right after the free camera one, counting into counts["scene format"], and report() starts done["scene format"] with the others. Nothing else of the PR changed (same 16 files, +1098/-7 against main). The game-free suite passes locally apart from two Tk-only checks that fail the same way on main here.

Thanks @futondestroyer for testing on a real HDR screen. Your log actually answers the upscaler question from my last review, and not in the PR's favour yet:

  • With HDR on Native, DLAA never takes the frame. Every few seconds it logs NVIDIA DLAA: the game's upscale runs (unexpected output) (earlier (no scene camera this frame)), and the DLSS-G summaries all say 0 made by DLSS-G ... 600 without new inputs (no DLSS/DLAA this frame).
  • The game's upscale network's input and output show up as format 97 (R16G16B16A16_SFLOAT) under Native, where the upscalers expect the B10G11R11 scene.
  • The only time DLSS made its feature was after HDR was switched to 1 (SDR expanded to HDR10), where the scene buffer stays at the old format.

So what looked right in play was the game's own upscale plus the renderer's own frame generation, not DLSS/DLAA or DLSS-G. That's the format gate I described on nn_vi.c: hos_scene_rgba16f follows Native alone, so this also affects Native players who keep the tone mapper on Vanilla.

Still COMMENT for now. Either of the two ways out from before works: tie the RGBA16F pick to the PsychoV35 row and say upscalers don't combine with it yet, or teach the temporal/FSR/TAAU gates RGBA16F (and check with #2243). Then a run with DLSS on under Native should show a feature ... made line and DLSS-G frames. The licence nits and the small IR ordering point from the first review are still open.

I merged main into the branch (2df79c0) so this is mergeable again. The only conflict was `nx2/oot_patches.py`: main moved the per-file patching into `patch_file()` / `report()` (#2442), so the scene-format hook now sits in `patch_file()` right after the free camera one, counting into `counts["scene format"]`, and `report()` starts `done["scene format"]` with the others. Nothing else of the PR changed (same 16 files, +1098/-7 against main). The game-free suite passes locally apart from two Tk-only checks that fail the same way on main here. Thanks @futondestroyer for testing on a real HDR screen. Your log actually answers the upscaler question from my last review, and not in the PR's favour yet: - With HDR on Native, DLAA never takes the frame. Every few seconds it logs `NVIDIA DLAA: the game's upscale runs (unexpected output)` (earlier `(no scene camera this frame)`), and the DLSS-G summaries all say `0 made by DLSS-G ... 600 without new inputs (no DLSS/DLAA this frame)`. - The game's upscale network's input and output show up as format 97 (`R16G16B16A16_SFLOAT`) under Native, where the upscalers expect the B10G11R11 scene. - The only time DLSS made its feature was after HDR was switched to 1 (SDR expanded to HDR10), where the scene buffer stays at the old format. So what looked right in play was the game's own upscale plus the renderer's own frame generation, not DLSS/DLAA or DLSS-G. That's the format gate I described on `nn_vi.c`: `hos_scene_rgba16f` follows Native alone, so this also affects Native players who keep the tone mapper on Vanilla. Still COMMENT for now. Either of the two ways out from before works: tie the RGBA16F pick to the PsychoV35 row and say upscalers don't combine with it yet, or teach the temporal/FSR/TAAU gates RGBA16F (and check with #2243). Then a run with DLSS on under Native should show a `feature ... made` line and DLSS-G frames. The licence nits and the small IR ordering point from the first review are still open.
Joey-Magnus commented 2026-11-11 22:06:47 +00:00 (Migrated from github.com)

@hckrman101 b485da0 addresses all three open points.

Scene buffer format. I took your first option. host/nn_vi.c now sets hos_scene_rgba16f from a new option, HOS_OPT_HDR_TONEMAP (47), which the renderer answers with scene_tonemap_on(). So the buffer is RGBA16F only with Native HDR and PsychoV35. Native HDR on Vanilla keeps R11G11B10F, as on main, and the B10G11R11 gates (DLSS/DLAA, DLSS-G, FSR 4, FSR 3.1, TAAU) see the scene they expect. With PsychoV35 on, those upscalers don't engage yet. The readme row and the description now say so. Teaching the gates RGBA16F is a follow-up that has to agree with #2243. I checked every open PR's vk_api.h: 47 isn't claimed (#2345 has 49/50, #2357 has 51, #2261 has 60). test_scene_format.py now checks the host reads 47, the renderer answers it with scene_tonemap_on(), and the fixture is labelled for PsychoV35.

IR ordering. The pass now emits PsychoV35 before the last of the three writes. Inserting there alone wasn't enough: the earlier two writes would then read values made after them, so they're moved down to just before the last write (same Insts, spliced in the intrusive list). scene_tonemap_ir.cpp has a new shape where red is written before blue's and green's fits exist. It checks that every operand in the block is defined before its use, for that shape and the game's. The old first-write insertion fails it on the new shape, and the new code passes both.

Licence. RenoDX's MIT text is in ir_opt/LICENSE-renodx.md, and PROVENANCE.md points to it. tests/test_psychov35.py carries the MIT SPDX tag and the copyright line. I also corrected psychov35.h's line to match the upstream licence: "Copyright (c) 2025 Carlos Lopez Jr." (it said 2026).

test_scene_format, test_scene_tonemap_pass and test_psychov35 pass. run_all gives 384 passed. The 1 failure is test_lift_cache, which needs numpy, missing on this machine. vk_replay.cpp also builds without errors. I haven't run b485da0 in game yet. @futondestroyer, if you have time, a Native HDR run with the row on Vanilla and DLSS on should now show the feature ... made line and DLSS-G frames.

@hckrman101 b485da0 addresses all three open points. **Scene buffer format.** I took your first option. `host/nn_vi.c` now sets `hos_scene_rgba16f` from a new option, `HOS_OPT_HDR_TONEMAP` (47), which the renderer answers with `scene_tonemap_on()`. So the buffer is RGBA16F only with Native HDR **and** PsychoV35. Native HDR on Vanilla keeps R11G11B10F, as on main, and the B10G11R11 gates (DLSS/DLAA, DLSS-G, FSR 4, FSR 3.1, TAAU) see the scene they expect. With PsychoV35 on, those upscalers don't engage yet. The readme row and the description now say so. Teaching the gates RGBA16F is a follow-up that has to agree with #2243. I checked every open PR's `vk_api.h`: 47 isn't claimed (#2345 has 49/50, #2357 has 51, #2261 has 60). `test_scene_format.py` now checks the host reads 47, the renderer answers it with `scene_tonemap_on()`, and the fixture is labelled for PsychoV35. **IR ordering.** The pass now emits PsychoV35 before the *last* of the three writes. Inserting there alone wasn't enough: the earlier two writes would then read values made after them, so they're moved down to just before the last write (same `Inst`s, spliced in the intrusive list). `scene_tonemap_ir.cpp` has a new shape where red is written before blue's and green's fits exist. It checks that every operand in the block is defined before its use, for that shape and the game's. The old first-write insertion fails it on the new shape, and the new code passes both. **Licence.** RenoDX's MIT text is in `ir_opt/LICENSE-renodx.md`, and `PROVENANCE.md` points to it. `tests/test_psychov35.py` carries the MIT SPDX tag and the copyright line. I also corrected `psychov35.h`'s line to match the upstream licence: "Copyright (c) 2025 Carlos Lopez Jr." (it said 2026). `test_scene_format`, `test_scene_tonemap_pass` and `test_psychov35` pass. `run_all` gives 384 passed. The 1 failure is `test_lift_cache`, which needs numpy, missing on this machine. `vk_replay.cpp` also builds without errors. I haven't run b485da0 in game yet. @futondestroyer, if you have time, a Native HDR run with the row on Vanilla and DLSS on should now show the `feature ... made` line and DLSS-G frames.
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 22:06:48 +00:00
Joey-Magnus (Migrated from github.com) commented 2026-11-11 22:06:48 +00:00

Done in b485da0: it's rx_option(HOS_OPT_HDR_TONEMAP, 0) now (option 47, answered by scene_tonemap_on()), so Native HDR on Vanilla keeps R11G11B10F.

Done in b485da0: it's `rx_option(HOS_OPT_HDR_TONEMAP, 0)` now (option 47, answered by `scene_tonemap_on()`), so Native HDR on Vanilla keeps R11G11B10F.
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 22:06:50 +00:00
@ -0,0 +1,204 @@
// SPDX-License-Identifier: GPL-3.0-or-later
Joey-Magnus (Migrated from github.com) commented 2026-11-11 22:06:50 +00:00

Done in b485da0: PsychoV35 is emitted before the last of the three writes, and the other two move down to it. The new red-first shape in scene_tonemap_ir.cpp checks every operand is defined before its use.

Done in b485da0: PsychoV35 is emitted before the last of the three writes, and the other two move down to it. The new red-first shape in `scene_tonemap_ir.cpp` checks every operand is defined before its use.
futondestroyer commented 2026-11-11 23:08:04 +00:00 (Migrated from github.com)

Follow-up test on b485da0, same RTX 4070 / Windows 11 system. During one session I cycled through HDR Off, Expanded, and Native with both Vanilla and PsychoV35. DLAA and FG were set to On throughout.

The log shows DLAA recreating its feature as Native HDR turns off/on, correctly reporting HDR linear-light input when Native resumes. It records a full 600-frame interval with DLAA processing every frame. Later unexpected output fallbacks are followed by NVIDIA DLSS draws the upscale again, confirming re-engagement during the cycle.

The tone-mapper changes aren't explicitly marked in the log, so I can't associate every fallback/resumption with an exact switch.

DLSS-G remains unsuccessful. Its feature initializes, but evaluation repeatedly returns 0xbad00005, and all summaries report zero frames made by DLSS-G.

hookshot_log.txt

Follow-up test on b485da0, same RTX 4070 / Windows 11 system. During one session I cycled through HDR Off, Expanded, and Native with both Vanilla and PsychoV35. DLAA and FG were set to On throughout. The log shows DLAA recreating its feature as Native HDR turns off/on, correctly reporting HDR linear-light input when Native resumes. It records a full 600-frame interval with DLAA processing every frame. Later `unexpected output` fallbacks are followed by `NVIDIA DLSS draws the upscale again`, confirming re-engagement during the cycle. The tone-mapper changes aren't explicitly marked in the log, so I can't associate every fallback/resumption with an exact switch. DLSS-G remains unsuccessful. Its feature initializes, but evaluation repeatedly returns `0xbad00005`, and all summaries report zero frames made by DLSS-G. [hookshot_log.txt](https://github.com/user-attachments/files/34719512/hookshot_log.txt)
Joey-Magnus commented 2026-11-11 23:11:55 +00:00 (Migrated from github.com)

Thanks @futondestroyer, that's what b485da0 was meant to do: DLAA makes its HDR feature each time Native turns on and comes back after the fallbacks.

The DLSS-G 0xbad00005 refusals aren't from this PR: they happen with HDR set to Off too (the summary at HDR 0 has 600 of 600 refused), where #2481 changes nothing. Your build predates #2118 (DLSS-G through NGX, merged today), so it'd be worth retrying on current main and, if it still refuses, opening an issue for @pmwork-personal with this log.

Thanks @futondestroyer, that's what b485da0 was meant to do: DLAA makes its HDR feature each time Native turns on and comes back after the fallbacks. The DLSS-G `0xbad00005` refusals aren't from this PR: they happen with HDR set to Off too (the summary at HDR 0 has 600 of 600 refused), where #2481 changes nothing. Your build predates #2118 (DLSS-G through NGX, merged today), so it'd be worth retrying on current main and, if it still refuses, opening an issue for @pmwork-personal with this log.
Commenting is not possible because the repository is archived.
No description provided.