Skip to content

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

Merged
hckrman101 merged 4 commits into
NotRexed:mainfrom
Joey-Magnus:feat/hdr-psychov35
Nov 11, 2026
Merged

hckrman101 merged 4 commits into
NotRexed:mainfrom
Joey-Magnus:feat/hdr-psychov35

Conversation

@Joey-Magnus

@Joey-Magnus Joey-Magnus commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

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 feat(dlss): DLSS for the game's native HDR, given linear light #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.

Joey Magnus and others added 2 commits November 11, 2026 15:41
With HDR set to Native, f_80672260's R11G11B10F choice for the scene buffer the game tone maps into
is RGBA16F, as RenoDX's zelda-oot-oz mod patches main+0x65234C (github.com/clshortfuse/renodx, MIT).
A runtime switch (hos_scene_rgba16f, set from the HDR row at each present), so the game's own pick
stays with HDR off; the game makes the buffer again when the format it wants changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
With HDR set to Native, the HDR tone mapper row (Vanilla, PsychoV35) replaces the game's Narkowicz fit
in its scene mapping programs with PsychoV35 from ShortFuse's RenoDX zelda-oot-oz mod (MIT), at the
mod's defaults: midgray matched to the game's, its tone slope, hue clip emulation at 50%.

ir_opt/scene_tonemap_pass.h finds the fit in the IR (three FMAs of 2.51 and 0.03 on the scene times a
constant buffer exposure, each channel named by its luminance weight, target 0 under the peak of that
bank) and writes target 0's colour from psychov35.h, the mod's math reduced to the path its defaults
take. Programs whose fragment code holds the fit's constants get their own key while the row is on,
so a change translates only those; the per-thread picks are dropped when it changes.

Tests: test_psychov35.py checks the header against RenoDX's HLSL path ported line by line (3009
colours, 2e-6); test_scene_tonemap_pass.py runs the pass on synthetic IR and interprets the result.

Co-authored-by: Cursor <cursoragent@cursor.com>

@hckrman101 hckrman101 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread host/nn_vi.c Outdated
hos_text_scale = text_scale(env, rx_option(5, 0));
}
hos_camera_scale = camera_scale(rx_option(HOS_OPT_CAMERA_SPEED, 100));
hos_scene_rgba16f = rx_option(HOS_OPT_HDR_NATIVE, 0) != 0;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

first = &inst;
break;
}
IR::IREmitter ir{*block, block->Instructions().iterator_to(*first)};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

futondestroyer commented Nov 11, 2026 •

Copy link
Copy Markdown

Tested PR #2481 at commit c46f8bc 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

@hckrman101 hckrman101 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

…safe for any write order

Native HDR on Vanilla gave the game an RGBA16F scene buffer, which the
temporal upscalers' B10G11R11 gates refuse: DLSS/DLAA, FSR and TAAU fell
back to the game's own upscale. The host now picks the buffer from a new
option, HOS_OPT_HDR_TONEMAP (47), answered by scene_tonemap_on(), so
Native HDR on Vanilla is as on main.

The scene tone map pass emits PsychoV35 before the last of target 0's
three writes and moves the other two there, so a program writing a
channel before another's fit is made gets no use before its value; the
IR test covers that order and checks every operand is made before use.

RenoDX's MIT licence is beside psychov35.h, whose copyright line now
matches it, and tests/test_psychov35.py's port carries the same notice.
@Joey-Magnus

Copy link
Copy Markdown
Contributor Author

@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
hckrman101 merged commit ddb156d into NotRexed:main Nov 11, 2026
10 of 11 checks passed
pmwork-personal added a commit to pmwork-personal/Hookshot-pr that referenced this pull request Nov 11, 2026
…and NotRexed#2496)

NotRexed#2119 renamed the Screen section's Ultrawide row to Aspect ratio and changed
Ultrawide menus' help; NotRexed#2481 added HDR tone mapper after HDR contrast. Both
changed rows inside the block this PR moves into sections: the sections keep
main's definitions word for word (every one of main's 81 rows matches), with
HDR tone mapper last in HDR. The readme's table takes main's Aspect ratio and
HDR tone mapper lines in the section order. test_menu_sections.py also pins
Profile, Aspect ratio, Ultrawide menus and HDR tone mapper in their sections.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hckrman101 pushed a commit that referenced this pull request Nov 11, 2026
The Graphics tab is one list of 24 settings, with the window, HDR,
upscaling, frame generation and GPU options mixed
together, and about ten more are on their way in open PRs. This puts its
rows under section titles, so the upscaler, frame
generation or HDR can be found without scrolling the whole list. It is
the first step of a menu organization laid out below
for discussion.

**What changed**
- The Graphics rows in six sections, under heading rows like the Debug
tab's Developer (a gold title, never selected,
  nothing to click):
  - Quality preset (first: it sets rows in every section)
- **Resolution and upscaling:** Output resolution, Profile, Upscaler,
DLSS model, Render resolution, Sharpness
- **Frame generation and latency:** Frame generation, Frame pacing, Game
frame rate cap, NVIDIA Reflex, AMD Anti-Lag 2
- **Screen:** Fullscreen, V-Sync, Window scaling filter, Aspect ratio
(#2119), Ultrawide menus
- **HDR:** HDR, HDR paper white, HDR peak brightness, HDR contrast
(#2342), HDR tone mapper (#2481)
  - **Detail:** Anisotropic filtering
- **GPU and memory:** GPU device, VRAM budget (#1859), Mesh shaders
(Robust buffers is on Debug ▸ Developer since #2042)
- Moves only: every row's definition is unchanged, word for word; no row
is renamed and no value or saved key changes, so
options files carry over, and #1864's tables (keyed by the English text)
still find every row. They need the six
  headings.
- No new drawing or input code: the menu already draws heading rows on
any tab, and the keys, the pad and the mouse step
  over them.
- `readme.md`: a sentence on the sections, and the option table in the
new order. (Ultrawide menus and Mesh shaders have
no line yet; #2105 adds them.) The **Frame generation by** row from
#2118 gets its line in the table here too: main's
readme has none yet. So do #1859's **VRAM budget** (under GPU and
memory) and #2342's **HDR contrast** (under HDR).
- `tests/test_upscaler_rows.py`: its order list follows the picture's
path (output, upscaler, render size, sharpening,
then the frame rows, then the window). New
`tests/test_menu_sections.py`: the Graphics sections and which rows they
hold,
and on every tab no section that `row_shown` could leave empty (a title
over nothing on a GPU without Reflex, say).

**The plan this is step 1 of (for discussion)**

Each tab answers one question; a section groups the rows you set
together; a row that depends on another follows it; vendor
rows sit beside their counterpart and hide where they can't run (no
vendor sections, so nothing is left empty on AMD); rows
to touch only when something is wrong go last; moves only, no renames.

| Tab | Holds | Sections |
|---|---|---|
| **Game** (was Gameplay; first, where the menu opens) | how the game
behaves and reads, and Restart and Quit, as a pause menu | flat; Restart
and Quit last |
| Graphics | how the picture is made, and what it costs | Quality preset
· Resolution and upscaling · Frame generation and latency · Detail · GPU
and memory |
| **Display** (new) | how it gets to your screen, and what is drawn over
it | Screen · HDR · Overlays (FPS counter, Menu size, from Game) |
| Audio | sound out and in | + Microphone (from Game: it picks the input
device) |
| Controls | how you play | Camera · Controller · Buttons (optional) |
| Debug | tools and developer switches | Robust buffers in Developer
(#2042, merged) |

Next PRs, one topic each: (2) a Display tab, taking the Screen and HDR
sections over whole and adding Overlays; (3) the
Gameplay tab titled Game, first in the strip, where the menu opens; (4)
Microphone to Audio; (5) optional, sections on
Controls. If a sixth tab is a no, this PR stands on its own.

Open questions:
1. A sixth tab (Display)?
2. V-Sync on Display (the PC convention) or with frame generation (it
changes with it)?
3. Sharpness with the upscaling rows (where players look for it) or with
the window filter (where it runs)?
4. DLSS 5 (#1590) and #1834's Neural rows as a page opened from a
Graphics row, like Motion, rather than a seventh tab?

<details><summary>Where open PRs' rows would go</summary>

- Resolution and upscaling: Sharpening method (#1979), DLSS 5 page
(#1590), Native 3D resolution (#2017)
- Frame generation and latency: Frame generation by (#2118, merged: in
this section now), Frame generation method (#1834)
- Detail: Shadow resolution (#1892), Screen filter and Filter strength
(#1488)
- GPU and memory: Texture streaming cache (#2017), Refresh memory
(#888); VRAM budget (#1859, merged) is in this section now
- Game (was Gameplay): Game language and Menu language (#1864), Skip
title screen (#1560), Amiibo (#1521)
- Controls: Camera speed (#1945)
- #2042 (merged) moved Robust buffers to Debug ▸ Developer.
- #2105: its readme check would read the heading rows as rows; it needs
to skip `{TAB_..., "...", nullptr, 0, 0, 0, ...,
  true}` headings, or this PR adds that if it lands first.

Whichever PR lands second moves one row on rebase; happy to do it for
anyone.

</details>

**Testing**
- `tests/run_all.py` on Windows 11 (llvm-mingw clang, 8 at once) after
merging main at 2dfe901 (with #2119's Aspect ratio, #2481's HDR tone
mapper, #2118's Frame generation by,
#2342's HDR contrast and #1859's VRAM budget): 422 passed, 33 skipped, 2
failed, both outside this PR:
`test_vram_budget_row.py` (fails the same on main's own sources here)
and `test_portable_builder_startup.py` (its
`dotnet publish` needs packages this PC does not have). A build of the
branch compiles. All 81 of main's rows keep
main's definitions word for word; the only additions are the six
headings.
`test_menu_sections.py` fails on main as it stands (no sections) and
passes here; a layout with a section of Reflex and
  Anti-Lag 2 alone fails it.
- A build of this branch (Windows 11, RTX 5090), headless with scripted
menu keys at Small, Medium and Large: 12 Downs
from the Quality preset land on V-Sync and 13 Ups come back past it to
the last row, so every heading is stepped over
both ways and Up from the first row wraps to the last; the help box
always shows the selected row (at the first head,
when Robust buffers was the last row). Again at this head (25feb16,
after #2042, #1859 and #2119): Up from the Quality preset
wraps to Mesh shaders, the last row now, under VRAM budget (the bottom
screenshot).

**Not tested**: in a window with a pad, the mouse wheel and a click just
under a heading (the headless runs drive
the keys); values kept across a restart (no row's saved key changes);
AMD and Intel (their rows hide through `row_shown` as before; the new
check makes sure no section can end up
empty), Linux.

**Screenshots**

The Graphics tab at 1920x1080, Medium menu size: the top, then scrolled
with the keys to V-Sync and to the last row
(the selection steps over every heading; Up from the first row wraps to
the last row). The bottom one is retaken at this
head (25feb16, a 16:9 picture: `HOS_ASPECT=1.7778`, as on a 16:9
monitor); the others are from the first head (634ef65).

![Graphics tab, top: Quality preset, then Resolution and upscaling and
Frame generation and
latency](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/menu-sections/graphics-sections-medium-1.webp)

![Graphics tab, middle: Frame generation and latency, then Screen,
V-Sync
selected](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/menu-sections/graphics-sections-medium-2.webp)

![Graphics tab, bottom: Screen with Aspect ratio, HDR, Detail, then GPU
and memory with GPU device, VRAM budget and Mesh shaders, Mesh shaders
selected](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/menu-sections/graphics-sections-medium-3c.webp)

Small and Large:

![Graphics tab at Small menu
size](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/menu-sections/graphics-sections-small.webp)

![Graphics tab at Large menu
size](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/menu-sections/graphics-sections-large.webp)

_Taken from headless runs of this branch (`HOS_HEADLESS=1`,
`RX_VK_SHOT_MENU=1`, the keys from `RX_VK_MENU_SCRIPT`), RTX
5090. Headless, the list never scrolls on main either:
`menu_record_image` leaves `ease_step` at 0, so the build behind
the scrolled shots had one more line there (`ease_step = 1.0f`, hence
"-dirty" in the first head's build label), not part of
this PR.
The windowed menu eases as before._

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@futondestroyer

Copy link
Copy Markdown

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

@Joey-Magnus

Copy link
Copy Markdown
Contributor Author

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.

pmwork-personal added a commit to pmwork-personal/Hookshot-pr that referenced this pull request Nov 11, 2026
…2119, NotRexed#2197, NotRexed#2481, NotRexed#2496)

NotRexed#2177 landed (squashed), so this takes main's sections as they shipped and
keeps this PR's change on top: the Screen, HDR and Overlays rows on the
Display tab. Main's newer rows go with them, word for word: NotRexed#2119's Aspect
ratio and Ultrawide menus text and NotRexed#2481's HDR tone mapper (last in Display's
HDR). Every one of main's 88 rows keeps main's definition; the only changes
are the moved rows' tab and the Overlays heading. draw_options keeps main's
Save my pipeline list line (NotRexed#2447) with the restart line on Display too. The
readme takes main's Menu shortcut text (NotRexed#2197) with six tabs, NotRexed#2177's VRAM
budget line, and main's HDR tone mapper and Field of view lines.
test_menu_sections.py pins Aspect ratio by name and HDR tone mapper in
Display's HDR; test_scene_tonemap_pass.py (NotRexed#2481) finds the tone mapper row on Display.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pmwork-personal added a commit to pmwork-personal/Hookshot-pr that referenced this pull request Nov 11, 2026
NotRexed#2197, NotRexed#2481, NotRexed#2496) into feat/menu-game-tab

Takes NotRexed#2178 as merged with main (cf90c14f) and keeps this PR's change on
top: the Game tab first, where the menu opens. Main's new Gameplay row,
Field of view, goes with the Game rows at the table's top, word for word,
after Game speed. Every one of main's 88 rows keeps main's definition; the
only changes are the moved rows' tab (Display) and the Overlays heading.
The readme takes main's Menu shortcut text (NotRexed#2197) with this PR's tab
sentence, and Field of view among Game's rows. The tab enum, the strip's
titles and the control bridge's names follow the same Game-first order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

3 participants