feat(menu): a Quality preset "Auto (this PC)" that picks settings from the hardware and tunes them in play #2850

Open
github-sync wants to merge 17 commits from github/pr/2850 into main
Owner

@Joey-Magnus opened this pull request on GitHub · 2026-11-12 16:33 UTC · original

A first start today only looks at video memory: a small card gets Minimum or Low, and every other PC starts on the same plain defaults whether it's a 3060 or a 5090. This adds a Quality preset, Auto (this PC), that new players start on and that picks settings for the PC it runs on, then checks them against how the GPU actually does in play.

Draft until it has been tried on AMD, Intel, a 6-8 GB card and a Steam Deck (test request to follow).

What it does

On launch (gpu/vk/auto_settings.h, one rule table, no list of GPU models):

Memory tier Video memory picks the preset whose budgets and scaling apply: up to 4.5 GB Minimum, 6.5 GB Low, 11.5 GB Balanced (so 10-11 GB cards are not on High's 7.5 GB plus mirrors), 13 GB High, above that Ultra. An integrated GPU, a Steam Deck or a card without mesh shaders (by its feature query, not the Mesh shaders mode this run uses: a RADV card on Automatic runs them as compute and still gets its memory tier) starts at Minimum; under 12 GB of RAM is held at Low
Upscaler DLSS on an NVIDIA RTX card, FSR 4 on an AMD RX 9000, where the build has them; otherwise the game's own. DLSS/FSR 4 mode by tier: Performance (Low), Balanced (Balanced), Quality (High, Ultra)
Output resolution The screen's own size when the menu offers it and the tier has the memory for it (1080p/1440p from Balanced, 4K/5120x1440 from High, 5120x2880 at Ultra); else the game's 2048x1152
Left alone The frame rate cap (60), frame generation (off), the profile (Docked)

One log line says what it chose and why, e.g.
vk: Auto (this PC): first start: 12282 MB of video memory: tier 4; NVIDIA RTX: DLSS Quality; output 2560x1440 (the screen's) (NVIDIA GeForce RTX 4070 Ti, ..., screen 2560x1440 at 60 Hz, 30987 MB of RAM, 16 threads).
With another preset chosen it still logs what Auto would choose, for bug reports.

Each start it compares a fingerprint of the GPU, its memory, the screen and the build's upscalers with the one it chose for (auto_for= in hookshot_options.ini) and chooses again when they differ: a new card or monitor gets new settings. Changing any row yourself turns the preset into Custom, as with the other presets, and from then on Auto touches nothing. The rows no preset sets (Output resolution, VRAM budget, Mesh shaders) leave it on Auto; when Auto chooses again for new hardware, the Output resolution and VRAM budget you set there stay yours (auto_output= records the Output Auto set). Mesh shaders Auto never sets, and the row is not in the fingerprint.

In play (the first ~15-50 s of gameplay; menus and loading screens don't count), it measures over the replay's 120-frame windows and moves the Render resolution:

  • down a step only when frames are missed (under 95% of the cap) and the GPU is over 90% busy; a PC holding its cap keeps its picture
  • up a step when the cap holds and the GPU is under half busy, past its first choice up to DLSS/FSR Quality or 100% with the game's own, never DLAA or native AA
  • nothing when the frame rate is short but the GPU is under 90% of the cap's frame time and idle for a quarter of the frame shown (the CPU is the limit); that ends the tuning for this run only, and the next start measures again

When DLSS or FSR 4 was chosen but the game's own upscaler runs (the build has it, this PC's driver or files don't), the Render resolution goes back to the tier's own once in play, and back to the choice if the chosen one runs again; a step the tuning made is left alone.

At most two changes, never up after a down, then done for that PC (auto_tuned=). The steps so far are saved after each one (auto_steps=), so both rules hold across restarts. Not with frame generation on.

Switches: RX_VK_AUTO_PRESET=0 (a first start keeps the defaults), RX_VK_AUTO_PRESET_VRAM_MB=<MB> (pretend the card has that much memory), RX_VK_AUTO_TUNE=0 (no tuning in play). The Linux window bridge passes the desktop size (MKWVK_WINDOW_VERSION 3, desktop_size); MKWVK_API_VERSION is unchanged.

Tested

RTX 4070 Ti 12 GB, driver 595.99.02, Ubuntu 26.04, a private KWin virtual output at 2560x1440 (headless runs, scripted input, the test save to Kokiri Forest and Hyrule Field). Game and renderer built from this branch.

Case Result
First start (no options file) Auto: tier 4 (High), DLSS Quality, output 2560x1440
Same PC again "the settings chosen for this PC before", nothing changed
RX_VK_AUTO_PRESET_VRAM_MB=6144 chose again: Low, DLSS Performance, output 2048x1152 (1440p needs Balanced)
Back to the real 12 GB chose High again
Sharpness changed through the control bridge preset Custom; the next start only logs what Auto would choose
In play, 1440p at a 60 cap 60 fps, GPU 8.4 of 16.7 ms: kept
In play, 4K at a 90 cap (output set by hand) ~80 fps, GPU 10.6 of 11.1 ms: 67% -> 58% -> 50%, then done
In play, 8 GB faked at a 60 cap 60 fps, GPU 8.3 ms: Balanced (58%) -> Quality (67%), then done (the ceiling)

No validation, device-lost or "not made" line in any of the runs. tests/test_auto_settings.py covers the rule table (RTX/RDNA 4/8 GB on 4K/6 GB/iGPU/Deck/no mesh shaders/low RAM, the fingerprint) and the tuning (gather, down only on missed frames, up only with the cap held, the CPU-bound case, the ceiling, two steps, no seesaw), and since the review: a card with mesh shaders run as compute against one without, the 10/11/12 GB steps, the upscaler snap-back, the steps restored after a restart. tests/test_low_vram_texture_mips.py has Auto at tier 1 and at a higher tier. python -I -B tests/run_all.py on Linux: 428 passed; test_lift_cache fails here as on main (the system Python has no numpy).

The in-game runs above were made before the review fixes (c2c2b2b7); the 4070 Ti's and the faked 6 and 8 GB tiers are the same under the new steps. After them: python -I -B tests/run_all.py on Linux: 464 passed; test_lift_cache (no numpy) and test_updater_asset_digest (this PC's .NET has no packages to restore) fail here as on main.

Three checks that cut functions out of vk_menu.cpp got the new names in their stubs (test_framegen_preset, test_menu_keys, test_menu_env_overrides); none was loosened.

Not tested: AMD, Intel, any Windows run (the screen size comes from EnumDisplaySettingsW there), a real 6-8 GB card, a Steam Deck, frame generation users (the tuning skips them). Known limit: it measures once, at the start of play, so a heavier area later (Hyrule Field, Castle Town) can still dip below the cap.

Overlaps

Conflicts with this branch that they don't already have with main (git merge-tree against each open PR): #2676 (preset VRAM budgets), #2465, #2371, #2274, #1872, #1590. #2557 (Texture detail Auto) touches the same idea from the texture side.

Drafted by _trash.panda's agent; reviewed by _trash.panda.

🤖 Generated with Claude Code

> [@Joey-Magnus](https://github.com/Joey-Magnus) opened this pull request on GitHub · 2026-11-12 16:33 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850) A first start today only looks at video memory: a small card gets Minimum or Low, and every other PC starts on the same plain defaults whether it's a 3060 or a 5090. This adds a Quality preset, **Auto (this PC)**, that new players start on and that picks settings for the PC it runs on, then checks them against how the GPU actually does in play. Draft until it has been tried on AMD, Intel, a 6-8 GB card and a Steam Deck (test request to follow). ## What it does **On launch** (`gpu/vk/auto_settings.h`, one rule table, no list of GPU models): | | | |---|---| | Memory tier | Video memory picks the preset whose budgets and scaling apply: up to 4.5 GB Minimum, 6.5 GB Low, 11.5 GB Balanced (so 10-11 GB cards are not on High's 7.5 GB plus mirrors), 13 GB High, above that Ultra. An integrated GPU, a Steam Deck or a card without mesh shaders (by its feature query, not the Mesh shaders mode this run uses: a RADV card on Automatic runs them as compute and still gets its memory tier) starts at Minimum; under 12 GB of RAM is held at Low | | Upscaler | DLSS on an NVIDIA RTX card, FSR 4 on an AMD RX 9000, where the build has them; otherwise the game's own. DLSS/FSR 4 mode by tier: Performance (Low), Balanced (Balanced), Quality (High, Ultra) | | Output resolution | The screen's own size when the menu offers it and the tier has the memory for it (1080p/1440p from Balanced, 4K/5120x1440 from High, 5120x2880 at Ultra); else the game's 2048x1152 | | Left alone | The frame rate cap (60), frame generation (off), the profile (Docked) | One log line says what it chose and why, e.g. `vk: Auto (this PC): first start: 12282 MB of video memory: tier 4; NVIDIA RTX: DLSS Quality; output 2560x1440 (the screen's) (NVIDIA GeForce RTX 4070 Ti, ..., screen 2560x1440 at 60 Hz, 30987 MB of RAM, 16 threads)`. With another preset chosen it still logs what Auto would choose, for bug reports. **Each start** it compares a fingerprint of the GPU, its memory, the screen and the build's upscalers with the one it chose for (`auto_for=` in `hookshot_options.ini`) and chooses again when they differ: a new card or monitor gets new settings. Changing any row yourself turns the preset into Custom, as with the other presets, and from then on Auto touches nothing. The rows no preset sets (Output resolution, VRAM budget, Mesh shaders) leave it on Auto; when Auto chooses again for new hardware, the Output resolution and VRAM budget you set there stay yours (`auto_output=` records the Output Auto set). Mesh shaders Auto never sets, and the row is not in the fingerprint. **In play** (the first ~15-50 s of gameplay; menus and loading screens don't count), it measures over the replay's 120-frame windows and moves the Render resolution: - **down** a step only when frames are missed (under 95% of the cap) and the GPU is over 90% busy; a PC holding its cap keeps its picture - **up** a step when the cap holds and the GPU is under half busy, past its first choice up to DLSS/FSR Quality or 100% with the game's own, never DLAA or native AA - **nothing** when the frame rate is short but the GPU is under 90% of the cap's frame time and idle for a quarter of the frame shown (the CPU is the limit); that ends the tuning for this run only, and the next start measures again When DLSS or FSR 4 was chosen but the game's own upscaler runs (the build has it, this PC's driver or files don't), the Render resolution goes back to the tier's own once in play, and back to the choice if the chosen one runs again; a step the tuning made is left alone. At most two changes, never up after a down, then done for that PC (`auto_tuned=`). The steps so far are saved after each one (`auto_steps=`), so both rules hold across restarts. Not with frame generation on. Switches: `RX_VK_AUTO_PRESET=0` (a first start keeps the defaults), `RX_VK_AUTO_PRESET_VRAM_MB=<MB>` (pretend the card has that much memory), `RX_VK_AUTO_TUNE=0` (no tuning in play). The Linux window bridge passes the desktop size (`MKWVK_WINDOW_VERSION` 3, `desktop_size`); `MKWVK_API_VERSION` is unchanged. ## Tested RTX 4070 Ti 12 GB, driver 595.99.02, Ubuntu 26.04, a private KWin virtual output at 2560x1440 (headless runs, scripted input, the test save to Kokiri Forest and Hyrule Field). Game and renderer built from this branch. | Case | Result | |---|---| | First start (no options file) | Auto: tier 4 (High), DLSS Quality, output 2560x1440 | | Same PC again | "the settings chosen for this PC before", nothing changed | | `RX_VK_AUTO_PRESET_VRAM_MB=6144` | chose again: Low, DLSS Performance, output 2048x1152 (1440p needs Balanced) | | Back to the real 12 GB | chose High again | | Sharpness changed through the control bridge | preset Custom; the next start only logs what Auto would choose | | In play, 1440p at a 60 cap | 60 fps, GPU 8.4 of 16.7 ms: kept | | In play, 4K at a 90 cap (output set by hand) | ~80 fps, GPU 10.6 of 11.1 ms: 67% -> 58% -> 50%, then done | | In play, 8 GB faked at a 60 cap | 60 fps, GPU 8.3 ms: Balanced (58%) -> Quality (67%), then done (the ceiling) | No validation, device-lost or "not made" line in any of the runs. `tests/test_auto_settings.py` covers the rule table (RTX/RDNA 4/8 GB on 4K/6 GB/iGPU/Deck/no mesh shaders/low RAM, the fingerprint) and the tuning (gather, down only on missed frames, up only with the cap held, the CPU-bound case, the ceiling, two steps, no seesaw), and since the review: a card with mesh shaders run as compute against one without, the 10/11/12 GB steps, the upscaler snap-back, the steps restored after a restart. `tests/test_low_vram_texture_mips.py` has Auto at tier 1 and at a higher tier. `python -I -B tests/run_all.py` on Linux: 428 passed; `test_lift_cache` fails here as on main (the system Python has no numpy). The in-game runs above were made before the review fixes (c2c2b2b7); the 4070 Ti's and the faked 6 and 8 GB tiers are the same under the new steps. After them: `python -I -B tests/run_all.py` on Linux: 464 passed; `test_lift_cache` (no numpy) and `test_updater_asset_digest` (this PC's .NET has no packages to restore) fail here as on main. Three checks that cut functions out of `vk_menu.cpp` got the new names in their stubs (`test_framegen_preset`, `test_menu_keys`, `test_menu_env_overrides`); none was loosened. **Not tested:** AMD, Intel, any Windows run (the screen size comes from `EnumDisplaySettingsW` there), a real 6-8 GB card, a Steam Deck, frame generation users (the tuning skips them). Known limit: it measures once, at the start of play, so a heavier area later (Hyrule Field, Castle Town) can still dip below the cap. ## Overlaps Conflicts with this branch that they don't already have with main (`git merge-tree` against each open PR): #2676 (preset VRAM budgets), #2465, #2371, #2274, #1872, #1590. #2557 (Texture detail Auto) touches the same idea from the texture side. Drafted by _trash.panda's agent; reviewed by _trash.panda. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- github-sync:item:2850 -->
The first start chose Minimum or Low by video memory alone, and every larger card started on the
plain defaults whatever it could run. Auto picks from what the PC reports (gpu/vk/auto_settings.h):
the memory tier (integrated GPUs, handhelds and cards on the mesh fallback at Minimum), DLSS on
NVIDIA RTX or FSR 4 on AMD RDNA 4 where the build has them (else the game's own upscaler), and the
screen's own output size where the tier has the memory for it. Each start compares a fingerprint of
the card, its memory, the screen and the build's upscalers with the one the settings were chosen
for (auto_for= in the options file) and chooses again when it changed. Changing a row comes back to
Custom, as with the other presets. One log line says what was chosen and why.

The screen's size on Linux comes from the window bridge (MKWVK_WINDOW_VERSION 3, desktop_size).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
save() keeps the lines it doesn't write; auto_for wasn't on its list, so the old line stayed after
the new one, won at the next start, and Auto chose again every time. Found in the first windowed
run; the test now checks the key is on save()'s own list.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
In play (150 draws a frame or more), over 8 of the replay's 120-present windows: the GPU over 90%
of the cap's frame time takes the Render resolution one step lighter, under 50% one step heavier but
never past what Auto chose; a step down is never followed by a step up, at most two steps, then it
is done for this PC (auto_tuned= in the options file). A frame rate short of the cap with the GPU
idle a quarter of the frame is the CPU's: nothing changes, and the log says so. Not with frame
generation on. RX_VK_AUTO_TUNE=0: off.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The checks that cut hos_display_init out of the source (the display start-up and libdecor ones)
compile it with stubs; the size is read once the window exists, before the renderer starts, which
is when Auto reads it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Lowering on GPU load alone cost picture on PCs that held their cap (the GPU at 95% and 60 fps
was stepped down); raising was capped at what the profile chose, so it never rose at all, and an
8 GB card that could run DLSS Quality stayed on Balanced. Now a step down needs frames missed
(under 95% of the cap) with the GPU over 90% busy; a step up needs the cap held with the GPU
under half busy, up to the best mode that still upscales (Quality; 100% with the game's own),
never native. The guard rails stay: two changes at most, no step up after a step down, nothing
when the CPU holds the frame rate. The log gives the medians the verdict was made on.

In game (RTX 4070 Ti, 2560x1440): 4K at a 90 cap, frames missed: 67% -> 58% -> 50%. A faked 8 GB
card at the 60 cap, the GPU at 8.3 of 16.7 ms: Balanced (58%) -> Quality (67%), then settled.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Author
Owner

@hckrman101 commented on gpu/vk/vk_main.cpp line 1381 on GitHub · 2026-11-12 16:45 UTC · original

This is this run's mode, not the card's capability. On Linux, Automatic picks Compute for every AMD card (mesh_mode_used = 2 above, #2245), so every RADV card lands here as mesh_native = false and on Minimum. A player anywhere who sets Mesh shaders to Compute gets the same. I'd record whether the card has mesh shaders before the mode is applied and use that.

> [@hckrman101](https://github.com/hckrman101) commented on `gpu/vk/vk_main.cpp` line 1381 on GitHub · 2026-11-12 16:45 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553431069) This is this run's mode, not the card's capability. On Linux, Automatic picks Compute for every AMD card (`mesh_mode_used = 2` above, #2245), so every RADV card lands here as `mesh_native = false` and on Minimum. A player anywhere who sets Mesh shaders to Compute gets the same. I'd record whether the card has mesh shaders before the mode is applied and use that. <!-- github-sync:rc:4553431069 -->
Author
Owner

@hckrman101 commented on gpu/vk/auto_settings.h line 59 on GitHub · 2026-11-12 16:45 UTC · original

With High starting above 9 GB, a 10 GB card (RTX 3080, RX 6700) gets the High (12GB) preset: 7.5 GB of textures plus 2 GB of mirrors, 9.5 GB of its 10 before render targets and DLSS. 12 GB cards report about 12,000-12,288 MB, so a step near 11.5 GB would put 10-11 GB cards on Balanced. It's worth checking again once #2676's budgets are in.

> [@hckrman101](https://github.com/hckrman101) commented on `gpu/vk/auto_settings.h` line 59 on GitHub · 2026-11-12 16:45 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553431076) With High starting above 9 GB, a 10 GB card (RTX 3080, RX 6700) gets the High (12GB) preset: 7.5 GB of textures plus 2 GB of mirrors, 9.5 GB of its 10 before render targets and DLSS. 12 GB cards report about 12,000-12,288 MB, so a step near 11.5 GB would put 10-11 GB cards on Balanced. It's worth checking again once #2676's budgets are in. <!-- github-sync:rc:4553431076 -->
Author
Owner

@hckrman101 commented on gpu/vk/auto_settings.h line 96 on GitHub · 2026-11-12 16:45 UTC · original

dlss_built() says the build has DLSS, not that it runs on this PC. With an old driver, or a Linux install without the NGX libraries (#1748), temporal_mode() falls back to the game's own upscaler. The player then gets DLSS's share (50-67%) drawn by the game's own upscaler, lower than the tier would set for it. dlss_why() is optimistic until NGX answers, so checking it here may not help. Another option: the tuning, or the probe's answer, could snap back to the tier's own Render resolution when upscaler() isn't the upscaler Auto chose. The same applies to FSR 4.

> [@hckrman101](https://github.com/hckrman101) commented on `gpu/vk/auto_settings.h` line 96 on GitHub · 2026-11-12 16:45 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553431091) `dlss_built()` says the build has DLSS, not that it runs on this PC. With an old driver, or a Linux install without the NGX libraries (#1748), `temporal_mode()` falls back to the game's own upscaler. The player then gets DLSS's share (50-67%) drawn by the game's own upscaler, lower than the tier would set for it. `dlss_why()` is optimistic until NGX answers, so checking it here may not help. Another option: the tuning, or the probe's answer, could snap back to the tier's own Render resolution when `upscaler()` isn't the upscaler Auto chose. The same applies to FSR 4. <!-- github-sync:rc:4553431091 -->
Author
Owner

@hckrman101 commented on gpu/vk/auto_settings.h line 168 on GitHub · 2026-11-12 16:45 UTC · original

This runs before the step-down check and compares against the frame actually shown. At 40 fps under a 60 cap with the GPU at 17 ms, already over the 16.7 ms budget, it decides the CPU is the limit and finishes for good, because menu_auto_tune saves auto_tuned on CPU_BOUND. I'd only call it CPU-bound when the GPU is also under the budget (say g < 0.9f * budget). The verdict is also taken in the first minute of play, while pipelines may still be compiling and textures streaming in, so maybe CPU_BOUND should end this run without writing auto_tuned.

> [@hckrman101](https://github.com/hckrman101) commented on `gpu/vk/auto_settings.h` line 168 on GitHub · 2026-11-12 16:45 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553431107) This runs before the step-down check and compares against the frame actually shown. At 40 fps under a 60 cap with the GPU at 17 ms, already over the 16.7 ms budget, it decides the CPU is the limit and finishes for good, because `menu_auto_tune` saves `auto_tuned` on CPU_BOUND. I'd only call it CPU-bound when the GPU is also under the budget (say `g < 0.9f * budget`). The verdict is also taken in the first minute of play, while pipelines may still be compiling and textures streaming in, so maybe CPU_BOUND should end this run without writing `auto_tuned`. <!-- github-sync:rc:4553431107 -->
Author
Owner

@hckrman101 commented on gpu/vk/vk_menu.cpp line 4427 on GitHub · 2026-11-12 16:45 UTC · original

steps and went_down live in this run's static Tuning, and auto_tuned is only written here, on KEEP or CPU_BOUND. If the game is closed after a step and before the next verdict, the next start measures again from the new resolution and can step back up. So the no-seesaw rule holds within a run but not across runs. Saving that state, or writing auto_tuned after a step down, would fix it.

> [@hckrman101](https://github.com/hckrman101) commented on `gpu/vk/vk_menu.cpp` line 4427 on GitHub · 2026-11-12 16:45 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553431117) `steps` and `went_down` live in this run's static `Tuning`, and `auto_tuned` is only written here, on KEEP or CPU_BOUND. If the game is closed after a step and before the next verdict, the next start measures again from the new resolution and can step back up. So the no-seesaw rule holds within a run but not across runs. Saving that state, or writing `auto_tuned` after a step down, would fix it. <!-- github-sync:rc:4553431117 -->
Author
Owner

@hckrman101 commented on gpu/vk/vk_menu.cpp line 4180 on GitHub · 2026-11-12 16:45 UTC · original

The description says Auto touches nothing once you change a row, but changing a preset-exempt row doesn't make the preset Custom: Output resolution, the VRAM budget row, and Mesh shaders (which is in the fingerprint). A player who picks their own Output resolution stays on Auto. The next fingerprint change then applies the whole profile over their choice, and a different primary monitor, a laptop or Deck docked or undocked, or a build with or without DLSS all count. Two fixes: turn the preset to Custom when one of those rows changes under Auto, or keep the player's values for them here.

> [@hckrman101](https://github.com/hckrman101) commented on `gpu/vk/vk_menu.cpp` line 4180 on GitHub · 2026-11-12 16:45 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553431125) The description says Auto touches nothing once you change a row, but changing a preset-exempt row doesn't make the preset Custom: Output resolution, the VRAM budget row, and Mesh shaders (which is in the fingerprint). A player who picks their own Output resolution stays on Auto. The next fingerprint change then applies the whole profile over their choice, and a different primary monitor, a laptop or Deck docked or undocked, or a build with or without DLSS all count. Two fixes: turn the preset to Custom when one of those rows changes under Auto, or keep the player's values for them here. <!-- github-sync:rc:4553431125 -->
Author
Owner

@hckrman101 commented on gpu/vk/vk_menu.cpp line 937 on GitHub · 2026-11-12 16:45 UTC · original

Nit: apply_preset snaps the tier's Render resolution to the current upscaler() before opts.dlss is set below. When Auto chooses again and switches from DLSS to the game's own (say, a build without DLSS), Balanced's 100% is snapped to DLSS's 92% stop and stays there with the game's own upscaler. Setting opts.dlss before apply_preset avoids that.

> [@hckrman101](https://github.com/hckrman101) commented on `gpu/vk/vk_menu.cpp` line 937 on GitHub · 2026-11-12 16:45 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553431134) Nit: `apply_preset` snaps the tier's Render resolution to the current `upscaler()` before `opts.dlss` is set below. When Auto chooses again and switches from DLSS to the game's own (say, a build without DLSS), Balanced's 100% is snapped to DLSS's 92% stop and stays there with the game's own upscaler. Setting `opts.dlss` before `apply_preset` avoids that. <!-- github-sync:rc:4553431134 -->
Author
Owner

@hckrman101 commented on gpu/vk/vk_texture.cpp line 1906 on GitHub · 2026-11-12 16:45 UTC · original

This is why tests/test_low_vram_texture_mips.py fails in CI (no member named 'budget' in its fixture's opts). Please add budget to the stub, plus cases for Auto at tier 1 and at a higher tier. Nit: kAutoPreset rather than a bare 6, if it can be shared through vk_menu.h.

> [@hckrman101](https://github.com/hckrman101) commented on `gpu/vk/vk_texture.cpp` line 1906 on GitHub · 2026-11-12 16:45 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553431146) This is why `tests/test_low_vram_texture_mips.py` fails in CI (`no member named 'budget'` in its fixture's `opts`). Please add `budget` to the stub, plus cases for Auto at tier 1 and at a higher tier. Nit: `kAutoPreset` rather than a bare 6, if it can be shared through `vk_menu.h`. <!-- github-sync:rc:4553431146 -->
Author
Owner

@hckrman101 reviewed on GitHub · 2026-11-12 16:45 UTC · original

Thanks for this. The rule table is easy to follow, the log line will help with bug reports, and the tuning has the right shape: medians over 8 play windows, at most two steps. I reviewed it from the code. A few things need settling before it comes out of draft; the first two matter most.

1. Linux AMD cards start on Minimum. On Linux, Mesh shaders on Automatic picks Compute for every AMD card (the RADV hang workaround in vk_main.cpp, #2245). That leaves dev.mesh_shader false, so auto_profile reports mesh_native = false, and every RX 6000-9000 on RADV, which docs/workflow.md puts in Tier 1, starts on Minimum. That means a 50% Render resolution, the plain scale, the game's output, and no FSR 4 even on a 9070 XT. On main these cards start on the defaults. The same happens on any PC where the player sets Mesh shaders to Compute. mesh_native is also in the fingerprint, so changing that row makes Auto choose again. It should come from what the card supports (the feature query, before the mode is applied), not from the mode this run uses. Every Hardware in the C++ test leaves mesh_native at its default of true, so a case for this would help.

2. The presets' memory budgets (#2676). Auto moves every card over 6.5 GB off the renderer's Auto budget rule, which is what they get on main today, and onto a preset's budget: Balanced's 4.8 GB on 8 GB cards, High's 7.5 GB plus 2 GB of mirrors from 9 GB up, and Ultra's 11 GB on 16 GB. In #2676's table, Balanced's 4.8 GB paged an RTX 3060 Ti and an RTX 4050 (13-55 ms a frame), and Ultra's 11 GB is the fill-up from #105/#106. I'd land #2676 first and rebase this on it. The High step is a separate problem (inline): a 10 GB card gets the 12 GB preset.

3. Measurements (rule 3c). This changes the first start for every new player. The description needs before/after numbers on the same scene, main's first start against Auto, with fps and VRAM, on several tiers:

  • an 8 GB NVIDIA card
  • a 10-12 GB card at 4K output
  • an RX 6000/7000 on Windows and on Linux
  • a Steam Deck
  • an iGPU

The 4070 Ti runs are a good start, and you already list the rest as untested. These are the runs that take it out of draft. Another option is to land the row first with the first start unchanged, and make Auto the default once the numbers are in.

4. CI. tests/test_low_vram_texture_mips.py fails on Linux part 1/2 and Windows part 2/2, and this PR is the cause: its fixture's opts has no budget (inline).

The smaller points are inline:

  • the preset-exempt rows Auto overwrites when the fingerprint changes
  • DLSS chosen when the build has it, even where NGX may not run
  • the CPU-bound verdict checked before the step down
  • the tuning's state not kept across runs
  • the order of the snap in apply_auto

The rest looks fine:

  • API. vk_api.h only bumps MKWVK_WINDOW_VERSION, as the refresh_mhz addition did. MkwVkHost and MkwVkRenderer are unchanged, so MKWVK_API_VERSION can stay.
  • SDL. The display_sdl.c change is the Linux side of the desktop size (Windows reads it from the window handle) and uses the existing hos_sdl_desktop_size.
  • Options. There are no new option ids. auto_for and auto_tuned are new ini keys only.
  • Tests. The C++ test really exercises the rule table and tune(). It doesn't cover what feeds them (point 1) or the menu code around them (apply_auto, the step choice in menu_auto_tune).
  • Overlaps. #2676 should land before this (point 2). #2834's comment names menu_first_start_for_gpu, which this renames. #2557 also changes low_vram_texture_mips and its test, so whichever lands second updates the other's stub.

Nit: in vk_menu.h, the menu_auto_tune declaration carries the old trailing comment "(no options file: the preset for the card)", and the preset comment in Options still lists 0-5.

> [@hckrman101](https://github.com/hckrman101) reviewed on GitHub · 2026-11-12 16:45 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#pullrequestreview-5878392318) Thanks for this. The rule table is easy to follow, the log line will help with bug reports, and the tuning has the right shape: medians over 8 play windows, at most two steps. I reviewed it from the code. A few things need settling before it comes out of draft; the first two matter most. **1. Linux AMD cards start on Minimum.** On Linux, Mesh shaders on Automatic picks Compute for every AMD card (the RADV hang workaround in `vk_main.cpp`, #2245). That leaves `dev.mesh_shader` false, so `auto_profile` reports `mesh_native = false`, and every RX 6000-9000 on RADV, which docs/workflow.md puts in Tier 1, starts on Minimum. That means a 50% Render resolution, the plain scale, the game's output, and no FSR 4 even on a 9070 XT. On main these cards start on the defaults. The same happens on any PC where the player sets Mesh shaders to Compute. `mesh_native` is also in the fingerprint, so changing that row makes Auto choose again. It should come from what the card supports (the feature query, before the mode is applied), not from the mode this run uses. Every `Hardware` in the C++ test leaves `mesh_native` at its default of true, so a case for this would help. **2. The presets' memory budgets (#2676).** Auto moves every card over 6.5 GB off the renderer's Auto budget rule, which is what they get on main today, and onto a preset's budget: Balanced's 4.8 GB on 8 GB cards, High's 7.5 GB plus 2 GB of mirrors from 9 GB up, and Ultra's 11 GB on 16 GB. In #2676's table, Balanced's 4.8 GB paged an RTX 3060 Ti and an RTX 4050 (13-55 ms a frame), and Ultra's 11 GB is the fill-up from #105/#106. I'd land #2676 first and rebase this on it. The High step is a separate problem (inline): a 10 GB card gets the 12 GB preset. **3. Measurements (rule 3c).** This changes the first start for every new player. The description needs before/after numbers on the same scene, main's first start against Auto, with fps and VRAM, on several tiers: - an 8 GB NVIDIA card - a 10-12 GB card at 4K output - an RX 6000/7000 on Windows and on Linux - a Steam Deck - an iGPU The 4070 Ti runs are a good start, and you already list the rest as untested. These are the runs that take it out of draft. Another option is to land the row first with the first start unchanged, and make Auto the default once the numbers are in. **4. CI.** `tests/test_low_vram_texture_mips.py` fails on Linux part 1/2 and Windows part 2/2, and this PR is the cause: its fixture's `opts` has no `budget` (inline). The smaller points are inline: - the preset-exempt rows Auto overwrites when the fingerprint changes - DLSS chosen when the build has it, even where NGX may not run - the CPU-bound verdict checked before the step down - the tuning's state not kept across runs - the order of the snap in `apply_auto` The rest looks fine: - **API.** `vk_api.h` only bumps `MKWVK_WINDOW_VERSION`, as the `refresh_mhz` addition did. `MkwVkHost` and `MkwVkRenderer` are unchanged, so `MKWVK_API_VERSION` can stay. - **SDL.** The `display_sdl.c` change is the Linux side of the desktop size (Windows reads it from the window handle) and uses the existing `hos_sdl_desktop_size`. - **Options.** There are no new option ids. `auto_for` and `auto_tuned` are new ini keys only. - **Tests.** The C++ test really exercises the rule table and `tune()`. It doesn't cover what feeds them (point 1) or the menu code around them (`apply_auto`, the step choice in `menu_auto_tune`). - **Overlaps.** #2676 should land before this (point 2). #2834's comment names `menu_first_start_for_gpu`, which this renames. #2557 also changes `low_vram_texture_mips` and its test, so whichever lands second updates the other's stub. Nit: in `vk_menu.h`, the `menu_auto_tune` declaration carries the old trailing comment "(no options file: the preset for the card)", and the `preset` comment in `Options` still lists 0-5. <!-- github-sync:r:5878392318 -->
Two conflicts, both rows added next to each other:
- docs/development.md: main rewrote the RX_VK_VRAM_MB row (MiB, the 45% rule, the integrated GPU budgets, what a texture counts); kept main's row and this branch's two new rows after it (RX_VK_AUTO_PRESET, RX_VK_AUTO_TUNE).
- docs/tested-configurations.md: both sides added rows dated 2026-11-12 at the top of the table; kept all of them, this branch's row first.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: Cursor <[email protected]>
The review of #2850 (hckrman101):
- mesh_native comes from the card's feature query (create_device, before the Mesh shaders mode is applied: dev.mesh_capable), not from this run's mode. Linux AMD's Automatic runs every RADV card as compute (#2245), which put every RX 6000-9000 on Linux on Minimum. The run's mode is only noted in the log line and is not in the fingerprint.
- High starts above 11.5 GB: 10 and 11 GB cards (RTX 3080, RX 6700, RTX 2080 Ti) are on Balanced; 12 GB cards report 12,000-12,288 MB.
- When the upscaler that runs is not the one Auto chose (DLSS or FSR 4 in the build but not running on this PC), the Render resolution goes back to the tier's own, checked in play once the upscaler has run; and back to the choice when the chosen one runs again. A step the tuning made is left alone (auto_settings.h resolution_for).
- The CPU-bound verdict needs the GPU under 0.9 of the cap's frame time too, comes after the step-down check, and ends the tuning for this run only: no auto_tuned for it.
- The tuning's steps and whether one went down are saved after each step (auto_steps=) and restored at the start, so the two steps and the no-seesaw rule hold across restarts.
- When Auto chooses again for new hardware, the Output resolution and the VRAM budget the player set under Auto stay theirs (auto_output= records the Output Auto set; the VRAM budget row already leaves budget 0). Mesh shaders Auto never sets.
- apply_auto sets opts.dlss before apply_preset, so the tier's Render resolution is snapped to the upscaler Auto chooses.
- kAutoPreset is in vk_menu.h, and low_vram_texture_mips uses it; vk_menu.h's comments for menu_auto_tune and the preset values are fixed.

Tests: test_low_vram_texture_mips's fixture has opts.budget (the CI failure), with Auto at tier 1, at tier 1 on a large budget, at tier 3, and with the player's VRAM budget. test_auto_settings.cpp: no mesh shaders against mesh shaders run as compute (tier and fingerprint), the 10/11/11.5/12 GB steps, resolution_for, the CPU-bound case at 40 fps with the GPU over budget, and the restored steps. test_auto_settings.py checks the wiring (mesh_capable read before the mode, opts.dlss before apply_preset, no auto_tuned on CPU-bound, kTierResolution against apply_preset's cases). test_menu_keys, test_menu_env_overrides and test_framegen_preset got the new names in their stubs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: Cursor <[email protected]>
# Conflicts:
#	docs/tested-configurations.md
#	readme.md
#2566 made the Output resolution's choices the screen's resolutions and the common sizes, so a choice's
number no longer names a fixed size: Auto's index into the old list (1 for 2560x1440) picked whatever
stood there. Auto now chooses a size, the screen's own where the tier carries its pixels (up to the
game's 2048x1152 any tier, up to 2560x1600 Balanced, up to 4K's pixels High, past them Ultra), and the
menu finds it among its choices; a size it does not offer is the game's own (logged), never the
player's custom size. Screens smaller than the game's own (1080p, 1920x1200, a Steam Deck's 1280x800)
now get their own size at any tier.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Author
Owner

@Joey-Magnus commented on gpu/vk/vk_main.cpp line 1381 on GitHub · 2026-11-12 17:04 UTC · original

Fixed in c2c2b2b7: create_device now reads the card's own task and mesh shader features (dev.mesh_capable) before the Mesh shaders mode is applied, and auto_profile uses that. A RADV card on Automatic, or any card with the row on Compute, gets its memory tier. The log line notes that this run uses compute, and the run's mode is not in the fingerprint. test_auto_settings.cpp has an RX 6800 run as compute (Ultra, and the same fingerprint as native) against an RX 5700 with the same memory and no mesh shaders (Minimum). The Python check makes sure mesh_capable is read before the mode turns mesh_shader off.

> [@Joey-Magnus](https://github.com/Joey-Magnus) commented on `gpu/vk/vk_main.cpp` line 1381 on GitHub · 2026-11-12 17:04 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553662666) Fixed in c2c2b2b7: create_device now reads the card's own task and mesh shader features (dev.mesh_capable) before the Mesh shaders mode is applied, and auto_profile uses that. A RADV card on Automatic, or any card with the row on Compute, gets its memory tier. The log line notes that this run uses compute, and the run's mode is not in the fingerprint. test_auto_settings.cpp has an RX 6800 run as compute (Ultra, and the same fingerprint as native) against an RX 5700 with the same memory and no mesh shaders (Minimum). The Python check makes sure mesh_capable is read before the mode turns mesh_shader off. <!-- github-sync:rc:4553662666 -->
Author
Owner

@Joey-Magnus commented on gpu/vk/auto_settings.h line 59 on GitHub · 2026-11-12 17:04 UTC · original

Fixed in c2c2b2b7: High now starts above 11,776 MB (11.5 GB), so 10 and 11 GB cards land on Balanced and 12 GB cards (12,000-12,288 MB) stay on High. The test has 9216, 10240, 11264 and 11776 MB on Balanced and 12000 on High. I'll check it again against #2676's budgets when this is rebased on it.

> [@Joey-Magnus](https://github.com/Joey-Magnus) commented on `gpu/vk/auto_settings.h` line 59 on GitHub · 2026-11-12 17:04 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553662932) Fixed in c2c2b2b7: High now starts above 11,776 MB (11.5 GB), so 10 and 11 GB cards land on Balanced and 12 GB cards (12,000-12,288 MB) stay on High. The test has 9216, 10240, 11264 and 11776 MB on Balanced and 12000 on High. I'll check it again against #2676's budgets when this is rebased on it. <!-- github-sync:rc:4553662932 -->
Author
Owner

@Joey-Magnus commented on gpu/vk/auto_settings.h line 96 on GitHub · 2026-11-12 17:04 UTC · original

Fixed in c2c2b2b7 with the cheap version of your second option. menu_auto_tune now checks in play, once the upscaler has run and NGX has answered, whether the engine running is the one Auto chose. If DLSS or FSR 4 was chosen and the game's own runs, the Render resolution goes back to the tier's own (Balanced's 100%, Low's 75%). If the chosen one runs again later (after a driver update, say), it goes back to the choice. This only happens while the resolution is still one of those two values, so a step the tuning made is left alone. The rule is auto_settings.h resolution_for and has C++ cases. kTierResolution is checked against apply_preset's cases.

> [@Joey-Magnus](https://github.com/Joey-Magnus) commented on `gpu/vk/auto_settings.h` line 96 on GitHub · 2026-11-12 17:04 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553663321) Fixed in c2c2b2b7 with the cheap version of your second option. menu_auto_tune now checks in play, once the upscaler has run and NGX has answered, whether the engine running is the one Auto chose. If DLSS or FSR 4 was chosen and the game's own runs, the Render resolution goes back to the tier's own (Balanced's 100%, Low's 75%). If the chosen one runs again later (after a driver update, say), it goes back to the choice. This only happens while the resolution is still one of those two values, so a step the tuning made is left alone. The rule is auto_settings.h resolution_for and has C++ cases. kTierResolution is checked against apply_preset's cases. <!-- github-sync:rc:4553663321 -->
Author
Owner

@Joey-Magnus commented on gpu/vk/auto_settings.h line 168 on GitHub · 2026-11-12 17:04 UTC · original

Fixed in c2c2b2b7: CPU_BOUND now also needs the GPU under 0.9 of the cap's frame time, and the check comes after the step-down. Your 40 fps case with the GPU at 17 ms now steps down, and that's a test case. CPU_BOUND ends the tuning for this run only: menu_auto_tune writes no auto_tuned for it, so the next start measures again. The Python check holds that branch to it.

> [@Joey-Magnus](https://github.com/Joey-Magnus) commented on `gpu/vk/auto_settings.h` line 168 on GitHub · 2026-11-12 17:04 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553663626) Fixed in c2c2b2b7: CPU_BOUND now also needs the GPU under 0.9 of the cap's frame time, and the check comes after the step-down. Your 40 fps case with the GPU at 17 ms now steps down, and that's a test case. CPU_BOUND ends the tuning for this run only: menu_auto_tune writes no auto_tuned for it, so the next start measures again. The Python check holds that branch to it. <!-- github-sync:rc:4553663626 -->
Author
Owner

@Joey-Magnus commented on gpu/vk/vk_menu.cpp line 4427 on GitHub · 2026-11-12 17:04 UTC · original

Fixed in c2c2b2b7: the tuning's state (steps, and whether one went down) is saved after every step as auto_steps=, and menu_init restores it. A restart after a step down can't step back up, and the two-step limit holds across runs. Choosing again for new hardware clears it. The C++ test restores a Tuning after a step down (no step up) and one at two steps (no third).

> [@Joey-Magnus](https://github.com/Joey-Magnus) commented on `gpu/vk/vk_menu.cpp` line 4427 on GitHub · 2026-11-12 17:04 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553664028) Fixed in c2c2b2b7: the tuning's state (steps, and whether one went down) is saved after every step as auto_steps=, and menu_init restores it. A restart after a step down can't step back up, and the two-step limit holds across runs. Choosing again for new hardware clears it. The C++ test restores a Tuning after a step down (no step up) and one at two steps (no third). <!-- github-sync:rc:4553664028 -->
Author
Owner

@Joey-Magnus commented on gpu/vk/vk_menu.cpp line 4180 on GitHub · 2026-11-12 17:04 UTC · original

Fixed in c2c2b2b7: I kept the player's values rather than switching to Custom. The exempt rows exist so that a player can set them without leaving a preset, and someone who sets their own Output resolution on Auto still wants the rest chosen for their card. When Auto chooses again for new hardware (apply_auto(keep_own)), an Output resolution different from the one Auto set stays the player's. Auto records the one it set as auto_output=. A VRAM budget set on the row also stays, since that row already leaves budget at 0. Choosing Auto on the preset row still applies all of it, as any preset does. Auto never sets Mesh shaders, and with the fix above the row is no longer in the fingerprint, so changing it doesn't make Auto choose again.

> [@Joey-Magnus](https://github.com/Joey-Magnus) commented on `gpu/vk/vk_menu.cpp` line 4180 on GitHub · 2026-11-12 17:04 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553664356) Fixed in c2c2b2b7: I kept the player's values rather than switching to Custom. The exempt rows exist so that a player can set them without leaving a preset, and someone who sets their own Output resolution on Auto still wants the rest chosen for their card. When Auto chooses again for new hardware (apply_auto(keep_own)), an Output resolution different from the one Auto set stays the player's. Auto records the one it set as auto_output=. A VRAM budget set on the row also stays, since that row already leaves budget at 0. Choosing Auto on the preset row still applies all of it, as any preset does. Auto never sets Mesh shaders, and with the fix above the row is no longer in the fingerprint, so changing it doesn't make Auto choose again. <!-- github-sync:rc:4553664356 -->
Author
Owner

@Joey-Magnus commented on gpu/vk/vk_menu.cpp line 937 on GitHub · 2026-11-12 17:04 UTC · original

Fixed in c2c2b2b7: apply_auto sets opts.dlss before apply_preset, so the tier's Render resolution is snapped to the upscaler Auto chooses. The Python check holds that order.

> [@Joey-Magnus](https://github.com/Joey-Magnus) commented on `gpu/vk/vk_menu.cpp` line 937 on GitHub · 2026-11-12 17:04 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553664641) Fixed in c2c2b2b7: apply_auto sets opts.dlss before apply_preset, so the tier's Render resolution is snapped to the upscaler Auto chooses. The Python check holds that order. <!-- github-sync:rc:4553664641 -->
Author
Owner

@Joey-Magnus commented on gpu/vk/vk_texture.cpp line 1906 on GitHub · 2026-11-12 17:04 UTC · original

Fixed in c2c2b2b7: the fixture's opts now has budget, and the Python side passes it. There are four new cases: Auto at tier 1 (trimmed), Auto at tier 1 with a large budget (not trimmed), Auto at tier 3 (not trimmed) and Auto with the player's VRAM budget (budget 0, not trimmed). kAutoPreset is now in vk_menu.h, and both vk_menu.cpp and low_vram_texture_mips use it.

> [@Joey-Magnus](https://github.com/Joey-Magnus) commented on `gpu/vk/vk_texture.cpp` line 1906 on GitHub · 2026-11-12 17:04 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#discussion_r4553664901) Fixed in c2c2b2b7: the fixture's opts now has budget, and the Python side passes it. There are four new cases: Auto at tier 1 (trimmed), Auto at tier 1 with a large budget (not trimmed), Auto at tier 3 (not trimmed) and Auto with the player's VRAM budget (budget 0, not trimmed). kAutoPreset is now in vk_menu.h, and both vk_menu.cpp and low_vram_texture_mips use it. <!-- github-sync:rc:4553664901 -->
Author
Owner

@Joey-Magnus commented on GitHub · 2026-11-12 17:05 UTC · original

Thanks for the review, hckrman101. I merged main in and fixed what can be fixed in code. Points 2 and 3 are still open.

Main merged (38809f64). There were two conflicts, both rows added next to each other. In docs/development.md main rewrote the RX_VK_VRAM_MB row; I kept main's row and put this branch's RX_VK_AUTO_PRESET and RX_VK_AUTO_TUNE rows after it. In docs/tested-configurations.md both sides added rows dated 2026-11-12; I kept all of them. #2834, #2557 and #2676 are all still open, so I made no changes for them.

1. Linux AMD on Minimum: fixed in c2c2b2b7. create_device reads the card's task and mesh shader features before the Mesh shaders mode is applied (dev.mesh_capable), and Auto uses that. A RADV card on Automatic, or any card with the row on Compute, now gets its memory tier. The log line notes that this run uses compute, and the run's mode is no longer in the fingerprint. The C++ test compares a card with mesh shaders run as compute against one without.

2. The presets' memory budgets (#2676): still open. I agree #2676 should land first, and I'll rebase this onto its budgets then; I didn't pull it in here. The High step (inline) is fixed separately: High now starts above 11.5 GB.

3. Measurements: still open. The before/after runs on an 8 GB NVIDIA card, a 10-12 GB card at 4K, an RX 6000/7000 on Windows and Linux, a Steam Deck and an iGPU need that hardware. That's what keeps this in draft. Landing the row with the first start unchanged is a good fallback if the numbers are slow to come.

4. CI: fixed in c2c2b2b7. The fixture in test_low_vram_texture_mips.py has budget now. The new cases are Auto at tier 1, at tier 1 with a large budget, at tier 3, and with the player's VRAM budget. kAutoPreset is now in vk_menu.h.

Inline points, all in c2c2b2b7:

  • High step: 10 and 11 GB cards are now on Balanced; 12 GB cards stay on High.
  • DLSS or FSR 4 in the build but not running here: once the upscaler has run in play, the Render resolution goes back to the tier's own, and back to the choice if the chosen upscaler runs again. A step the tuning made is left alone.
  • CPU-bound: it now also needs the GPU under 0.9 of the budget, and it's checked after the step down. It ends the tuning for this run only, with no auto_tuned.
  • Tuning state: the steps, and whether one went down, are saved after each step (auto_steps=), so the no-seesaw rule and the two-step limit hold across restarts.
  • Exempt rows: I kept the player's values rather than switching to Custom. When Auto chooses again for new hardware, an Output resolution or VRAM budget the player set stays theirs. Mesh shaders isn't set by Auto and isn't in the fingerprint any more.
  • opts.dlss is set before apply_preset.
  • The vk_menu.h comment nits are fixed.

Tested. I ran test_auto_settings (new C++ cases and new source checks), test_low_vram_texture_mips, and the stub updates in test_menu_keys, test_menu_env_overrides and test_framegen_preset. The full python -I -B tests/run_all.py on Linux: 464 passed. Two tests fail on this PC the same way without this change: test_lift_cache (no numpy here) and test_updater_asset_digest (.NET can't restore its packages here). vk_main.cpp, vk_menu.cpp and vk_texture.cpp compile on Linux. I haven't run the game since these fixes; the in-game runs in the description were made before them.

(_trash.panda's agent)

> [@Joey-Magnus](https://github.com/Joey-Magnus) commented on GitHub · 2026-11-12 17:05 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#issuecomment-6830364158) Thanks for the review, hckrman101. I merged main in and fixed what can be fixed in code. Points 2 and 3 are still open. **Main merged (38809f64).** There were two conflicts, both rows added next to each other. In `docs/development.md` main rewrote the `RX_VK_VRAM_MB` row; I kept main's row and put this branch's `RX_VK_AUTO_PRESET` and `RX_VK_AUTO_TUNE` rows after it. In `docs/tested-configurations.md` both sides added rows dated 2026-11-12; I kept all of them. #2834, #2557 and #2676 are all still open, so I made no changes for them. **1. Linux AMD on Minimum: fixed in c2c2b2b7.** `create_device` reads the card's task and mesh shader features before the Mesh shaders mode is applied (`dev.mesh_capable`), and Auto uses that. A RADV card on Automatic, or any card with the row on Compute, now gets its memory tier. The log line notes that this run uses compute, and the run's mode is no longer in the fingerprint. The C++ test compares a card with mesh shaders run as compute against one without. **2. The presets' memory budgets (#2676): still open.** I agree #2676 should land first, and I'll rebase this onto its budgets then; I didn't pull it in here. The High step (inline) is fixed separately: High now starts above 11.5 GB. **3. Measurements: still open.** The before/after runs on an 8 GB NVIDIA card, a 10-12 GB card at 4K, an RX 6000/7000 on Windows and Linux, a Steam Deck and an iGPU need that hardware. That's what keeps this in draft. Landing the row with the first start unchanged is a good fallback if the numbers are slow to come. **4. CI: fixed in c2c2b2b7.** The fixture in `test_low_vram_texture_mips.py` has `budget` now. The new cases are Auto at tier 1, at tier 1 with a large budget, at tier 3, and with the player's VRAM budget. `kAutoPreset` is now in `vk_menu.h`. **Inline points, all in c2c2b2b7:** - High step: 10 and 11 GB cards are now on Balanced; 12 GB cards stay on High. - DLSS or FSR 4 in the build but not running here: once the upscaler has run in play, the Render resolution goes back to the tier's own, and back to the choice if the chosen upscaler runs again. A step the tuning made is left alone. - CPU-bound: it now also needs the GPU under 0.9 of the budget, and it's checked after the step down. It ends the tuning for this run only, with no `auto_tuned`. - Tuning state: the steps, and whether one went down, are saved after each step (`auto_steps=`), so the no-seesaw rule and the two-step limit hold across restarts. - Exempt rows: I kept the player's values rather than switching to Custom. When Auto chooses again for new hardware, an Output resolution or VRAM budget the player set stays theirs. Mesh shaders isn't set by Auto and isn't in the fingerprint any more. - `opts.dlss` is set before `apply_preset`. - The `vk_menu.h` comment nits are fixed. **Tested.** I ran `test_auto_settings` (new C++ cases and new source checks), `test_low_vram_texture_mips`, and the stub updates in `test_menu_keys`, `test_menu_env_overrides` and `test_framegen_preset`. The full `python -I -B tests/run_all.py` on Linux: 464 passed. Two tests fail on this PC the same way without this change: `test_lift_cache` (no numpy here) and `test_updater_asset_digest` (.NET can't restore its packages here). `vk_main.cpp`, `vk_menu.cpp` and `vk_texture.cpp` compile on Linux. I haven't run the game since these fixes; the in-game runs in the description were made before them. (_trash.panda's agent) <!-- github-sync:c:6830364158 -->
Author
Owner

@Joey-Magnus commented on GitHub · 2026-11-12 17:26 UTC · original

Merged main again (327ac12e) and fixed one thing it broke: #2566 made the Output resolution's choices the screen's own resolutions, so Auto's old index into the fixed list (1 = 2560x1440) would have picked whatever sits at that position now. a1507649 makes Auto choose a size (the screen's, where the tier carries its pixels: up to 2048x1152 any tier, up to 2560x1600 Balanced, up to 4K's pixels High, past that Ultra) and look it up among the menu's choices; a size the menu doesn't offer falls back to 2048x1152 and is logged, never taken as a custom size. Side effect: 1080p, 1920x1200 and the Deck's 1280x800 now get their own size at any tier. Tests updated; the full suite passes here apart from the two environment-only failures. (_trash.panda's agent)

> [@Joey-Magnus](https://github.com/Joey-Magnus) commented on GitHub · 2026-11-12 17:26 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#issuecomment-6830899756) Merged main again (327ac12e) and fixed one thing it broke: #2566 made the Output resolution's choices the screen's own resolutions, so Auto's old index into the fixed list (1 = 2560x1440) would have picked whatever sits at that position now. a1507649 makes Auto choose a size (the screen's, where the tier carries its pixels: up to 2048x1152 any tier, up to 2560x1600 Balanced, up to 4K's pixels High, past that Ultra) and look it up among the menu's choices; a size the menu doesn't offer falls back to 2048x1152 and is logged, never taken as a custom size. Side effect: 1080p, 1920x1200 and the Deck's 1280x800 now get their own size at any tier. Tests updated; the full suite passes here apart from the two environment-only failures. (_trash.panda's agent) <!-- github-sync:c:6830899756 -->
Author
Owner

@hckrman101 approved on GitHub · 2026-11-12 17:32 UTC · original

Thanks, Joey. c2c2b2b7 and a1507649 answer the review, and I've merged current main in on top so this can land.

On top of your head:

  • cbd02b9c merges main (#2793 and #2710 since your merge, no conflicts).
  • ba361a91: test_menu_keys' Auto stubs are [[maybe_unused]]. Newer clang fails the fixture on set-but-unused globals.
  • 4f4c33d5: auto_output= is saved as the size (auto_output=2560x1440), not the choice's number. Since #2566 the numbers follow the screen's modes, so after a monitor change (the moment Auto chooses again) the old number pointed at another size, and Auto took its own earlier output for the player's. test_auto_settings.py checks the wiring.

Review points:

  • 1. Linux AMD on Minimum: fixed. mesh_native comes from dev.mesh_capable, which is read before the Mesh shaders mode is applied, and the C++ test covers RADV running mesh shaders as compute.
  • 4. CI (test_low_vram_texture_mips): fixed. The stub has budget, and the test passes locally with the Vulkan headers.
  • The inline points are handled too: the exempt rows are kept, the CPU-bound verdict, the tuning's steps across restarts, opts.dlss before apply_preset, High from 11.5 GB, and kAutoPreset.

Follow-ups, not blocking this:

  • Per-tier measurements (point 3): an 8 GB NVIDIA card, a 10-12 GB card at 4K, RX 6000/7000 on Windows and Linux, a Steam Deck, and an iGPU.
  • DLSS or FSR 4 in the build but not running: resolution_for now puts the tier's own Render resolution back in play. Choosing the game's own upscaler from the start when NGX won't run is still open.
  • The preset budgets against #2676: the tiers stay as written until #2676 lands, then get rebased on it.

Tests: the Auto, menu, display-mode, output-resolution, VRAM-budget and stick tests pass, and so does the full suite apart from main's own failures (test_readme_menu_rows's "Skip opening" row) and local tool gaps. hookshot_vk.so builds and its ABI check passes.

> [@hckrman101](https://github.com/hckrman101) approved on GitHub · 2026-11-12 17:32 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#pullrequestreview-5879108927) Thanks, Joey. c2c2b2b7 and a1507649 answer the review, and I've merged current main in on top so this can land. On top of your head: - cbd02b9c merges main (#2793 and #2710 since your merge, no conflicts). - ba361a91: test_menu_keys' Auto stubs are `[[maybe_unused]]`. Newer clang fails the fixture on set-but-unused globals. - 4f4c33d5: `auto_output=` is saved as the size (`auto_output=2560x1440`), not the choice's number. Since #2566 the numbers follow the screen's modes, so after a monitor change (the moment Auto chooses again) the old number pointed at another size, and Auto took its own earlier output for the player's. test_auto_settings.py checks the wiring. Review points: - **1. Linux AMD on Minimum:** fixed. `mesh_native` comes from `dev.mesh_capable`, which is read before the Mesh shaders mode is applied, and the C++ test covers RADV running mesh shaders as compute. - **4. CI (`test_low_vram_texture_mips`):** fixed. The stub has `budget`, and the test passes locally with the Vulkan headers. - The inline points are handled too: the exempt rows are kept, the CPU-bound verdict, the tuning's steps across restarts, `opts.dlss` before `apply_preset`, High from 11.5 GB, and `kAutoPreset`. Follow-ups, not blocking this: - Per-tier measurements (point 3): an 8 GB NVIDIA card, a 10-12 GB card at 4K, RX 6000/7000 on Windows and Linux, a Steam Deck, and an iGPU. - DLSS or FSR 4 in the build but not running: `resolution_for` now puts the tier's own Render resolution back in play. Choosing the game's own upscaler from the start when NGX won't run is still open. - The preset budgets against #2676: the tiers stay as written until #2676 lands, then get rebased on it. Tests: the Auto, menu, display-mode, output-resolution, VRAM-budget and stick tests pass, and so does the full suite apart from main's own failures (`test_readme_menu_rows`'s "Skip opening" row) and local tool gaps. `hookshot_vk.so` builds and its ABI check passes. <!-- github-sync:r:5879108927 -->
Author
Owner

@hckrman101 approved on GitHub · 2026-11-12 17:34 UTC · original

Re-approving at the new head. 2387a36f merges main again (#2709 and #2684 landed in the meantime). The only conflict was the tested-configurations table: both rows are kept. The Auto, menu and output tests pass again, and so does the vk build with its ABI check.

> [@hckrman101](https://github.com/hckrman101) approved on GitHub · 2026-11-12 17:34 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#pullrequestreview-5879126086) Re-approving at the new head. 2387a36f merges main again (#2709 and #2684 landed in the meantime). The only conflict was the tested-configurations table: both rows are kept. The Auto, menu and output tests pass again, and so does the vk build with its ABI check. <!-- github-sync:r:5879126086 -->
Author
Owner

@hckrman101 approved on GitHub · 2026-11-12 18:38 UTC · original

Re-approving at 905c35e4, which merges main again (#2881, #2649, #2788 and others). #2881 took the Mesh shaders row out of the menu. The conflicts were its comment in vk_main.cpp and the save() key list in vk_menu.cpp, and both sides are kept: mesh_capable is still read before the mode, and the mesh_shaders line is still dropped. Auto's comments now name RX_VK_MESH_SHADERS instead of the row. The related tests and the vk build with its ABI check pass.

> [@hckrman101](https://github.com/hckrman101) approved on GitHub · 2026-11-12 18:38 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2850#pullrequestreview-5880023014) Re-approving at 905c35e4, which merges main again (#2881, #2649, #2788 and others). #2881 took the Mesh shaders row out of the menu. The conflicts were its comment in `vk_main.cpp` and the save() key list in `vk_menu.cpp`, and both sides are kept: `mesh_capable` is still read before the mode, and the `mesh_shaders` line is still dropped. Auto's comments now name `RX_VK_MESH_SHADERS` instead of the row. The related tests and the vk build with its ABI check pass. <!-- github-sync:r:5880023014 -->
Commenting is not possible because the repository is archived.
No description provided.