perf(vk): Descriptor reuse on by default (options_version 4) #1625

Merged
NotRexed merged 1 commit from perf/descriptor-reuse-default into main 2026-11-10 17:01:18 +00:00
NotRexed commented 2026-11-10 11:27:58 +00:00 (Migrated from github.com)

Turns on Descriptor reuse by default (RX_VK_DESC_SLOTGEN, the row on the Debug tab). With it, a draw keeps its descriptors while the texture slots it samples are unchanged, so texture streaming elsewhere no longer rebuilds every descriptor set each frame.

Measured (RTX, 2560x1440 docked, Hyrule Field walk, four runs interleaved off/on/off/on on one build)

Run Median p99 1% low Stutters (> 2x median)
off 1 19.7 ms (51 fps) 58.3 9.3 fps 1449 (3.38%)
on 1 18.9 ms (53 fps) 46.9 11.2 fps 1038 (2.28%)
off 2 19.8 ms (50 fps) 67.7 7.9 fps 1662 (3.97%)
on 2 19.9 ms (50 fps) 52.9 9.7 fps 1225 (2.84%)

That's 25–30% fewer stutters, a better 1% low and p99, and the same median fps.

Flash checks (every frame shot, reuse on)

  • Kokiri Forest: 5837 frames, no bright or dark one-frame flashes, no crash lines.
  • Hyrule Field: 5411 frames, no bright flashes and one dark frame, no crash lines. The dark frame is the known rare one-frame black 3D scene with the HUD still drawn, about 1 in 10k frames. It was also seen with reuse off: the v2.0.12 barrier check in Kokiri Forest, and the write-back diff A/B. A same-route off/on/off A/B is queued to confirm. Don't merge before that result.

Options migration

  • Files before options_version 4 always wrote descriptor_reuse=0, because off was the default then. They're moved to on, and the next save writes options_version=4.
  • From version 4 on, a saved choice (off or on) is kept.
  • RX_VK_DESC_SLOTGEN still decides over both.
  • menu_env_on, which reads the row before the menu loads, follows the same rule; it now takes a default and the version that default holds from. Sampler cache's behaviour is unchanged.
  • host/nn_hid.c checks version < 3, so version 4 doesn't affect it.

Tests

  • New tests/test_menu_env_on.py compiles the real menu_env_on and covers: no file, no line, v3 with 0, v4 with 0 and with 1, and the env override.
  • test_audio_output_menu, test_menu_restart and test_menu_keys pass locally (llvm-mingw clang++).

🤖 Generated with Claude Code

Turns on Descriptor reuse by default (`RX_VK_DESC_SLOTGEN`, the row on the Debug tab). With it, a draw keeps its descriptors while the texture slots it samples are unchanged, so texture streaming elsewhere no longer rebuilds every descriptor set each frame. ## Measured (RTX, 2560x1440 docked, Hyrule Field walk, four runs interleaved off/on/off/on on one build) | Run | Median | p99 | 1% low | Stutters (> 2x median) | |---|---|---|---|---| | off 1 | 19.7 ms (51 fps) | 58.3 | 9.3 fps | 1449 (3.38%) | | **on 1** | 18.9 ms (53 fps) | 46.9 | 11.2 fps | **1038 (2.28%)** | | off 2 | 19.8 ms (50 fps) | 67.7 | 7.9 fps | 1662 (3.97%) | | **on 2** | 19.9 ms (50 fps) | 52.9 | 9.7 fps | **1225 (2.84%)** | That's 25–30% fewer stutters, a better 1% low and p99, and the same median fps. ## Flash checks (every frame shot, reuse on) - Kokiri Forest: 5837 frames, no bright or dark one-frame flashes, no crash lines. - Hyrule Field: 5411 frames, no bright flashes and one dark frame, no crash lines. The dark frame is the known rare one-frame black 3D scene with the HUD still drawn, about 1 in 10k frames. It was also seen with reuse off: the v2.0.12 barrier check in Kokiri Forest, and the write-back diff A/B. A same-route off/on/off A/B is queued to confirm. **Don't merge before that result.** ## Options migration - Files before `options_version` 4 always wrote `descriptor_reuse=0`, because off was the default then. They're moved to on, and the next save writes `options_version=4`. - From version 4 on, a saved choice (off or on) is kept. - `RX_VK_DESC_SLOTGEN` still decides over both. - `menu_env_on`, which reads the row before the menu loads, follows the same rule; it now takes a default and the version that default holds from. Sampler cache's behaviour is unchanged. - `host/nn_hid.c` checks `version < 3`, so version 4 doesn't affect it. ## Tests - New `tests/test_menu_env_on.py` compiles the real `menu_env_on` and covers: no file, no line, v3 with 0, v4 with 0 and with 1, and the env override. - `test_audio_output_menu`, `test_menu_restart` and `test_menu_keys` pass locally (llvm-mingw clang++). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jarynclouatre commented 2026-11-10 14:44:48 +00:00 (Migrated from github.com)

Some numbers from another setup, in case they help: RTX 4070 Ti Super, driver 621.14, Windows 11, Ryzen 7 9800X3D. The scripted 10-minute first-visit route from #1429 (boot, Hyrule Field, Kakariko, Market, Lost Woods, Gerudo Valley), each run from the same warmed folder state, on 7d1562d with #1586, #1627 and #1714 underneath. RX_VK_DESC_SLOTGEN off for three runs, on for two:

off on
time lost against 60 fps, s 18.0, 17.1, 17.5 16.8, 18.2
frames over 22 ms 1384, 1054, 1267 996, 1276
frames over 40 ms 98, 92, 82 87, 91

No change I can tell apart from run-to-run noise, and nothing broke. This route mostly measures first-visit hitches and keeps few descriptors through other slots' registrations (0 to 4 draws a frame, 0 in most windows), so it doesn't exercise what your Hyrule Field walk does. I only have screenshots every 60 presents, not every frame, so this says nothing about the one-frame flashes.

Some numbers from another setup, in case they help: RTX 4070 Ti Super, driver 621.14, Windows 11, Ryzen 7 9800X3D. The scripted 10-minute first-visit route from #1429 (boot, Hyrule Field, Kakariko, Market, Lost Woods, Gerudo Valley), each run from the same warmed folder state, on 7d1562d with #1586, #1627 and #1714 underneath. RX_VK_DESC_SLOTGEN off for three runs, on for two: | | off | on | |---|---|---| | time lost against 60 fps, s | 18.0, 17.1, 17.5 | 16.8, 18.2 | | frames over 22 ms | 1384, 1054, 1267 | 996, 1276 | | frames over 40 ms | 98, 92, 82 | 87, 91 | No change I can tell apart from run-to-run noise, and nothing broke. This route mostly measures first-visit hitches and keeps few descriptors through other slots' registrations (0 to 4 draws a frame, 0 in most windows), so it doesn't exercise what your Hyrule Field walk does. I only have screenshots every 60 presents, not every frame, so this says nothing about the one-frame flashes.
hckrman101 (Migrated from github.com) approved these changes 2026-11-10 16:39:30 +00:00
hckrman101 (Migrated from github.com) left a comment

Looks good. The migration reads right: menu_init forces descriptor_reuse=1 for files below options_version 4, keeps the saved choice from 4 on, and save() writes 4. menu_env_on reads the same rule before the menu loads (no file, no line, or version below since gives the default; the env var still wins). The new test is registered in run_all.py, and CI is green.

One thing outside the code: the description says not to merge before the same-route off/on/off A/B for the rare dark frame comes back, so I'd hold for that.

Looks good. The migration reads right: menu_init forces descriptor_reuse=1 for files below options_version 4, keeps the saved choice from 4 on, and save() writes 4. menu_env_on reads the same rule before the menu loads (no file, no line, or version below `since` gives the default; the env var still wins). The new test is registered in run_all.py, and CI is green. One thing outside the code: the description says not to merge before the same-route off/on/off A/B for the rare dark frame comes back, so I'd hold for that.
Commenting is not possible because the repository is archived.
No description provided.