perf(vk): scan pending uniform updates in place #472

Open
zeldaootr wants to merge 3 commits from zeldaootr/Longshot:scan-uniform-updates into main
Contributor

Type: perf · Risk: low · Tested on: Steam Deck OLED, SteamOS 3.9.5, RADV (ours and a tester's runs); Linux x86-64 (game-free) · Needs: Windows, any GPU

Why

Each uncached lookup for pending uniform-buffer writes copies two arrays of addresses and then walks them again. Both descriptor reads and uniform-buffer copies pay for this work on the replay thread, which sets the frame rate wherever the GPU is not the limit.

What changed

The lookup now scans the pending updates where they are instead of copying their addresses into two arrays first: the same answer and the same cached gap, with less work per query.

  • vk replay: the pending uniform-update gap lookup (ub_gap, ub_try_gap) in gpu/vk/vk_replay.cpp scans the existing ub_updates deque over the requested iterator range. It stops on the first overlap and otherwise caches the same gap; the epoch check still guards publication of a cached answer. A background thread (ub_limit == 0, an empty range) builds no iterator into the deque at all, as before: it must not read even its start while the replay thread pushes and prunes. The elements read are the ones the copy read.
  • tests: tests/test_ub_gap.py follows the new scan and checks the empty range with no deque behind it.
  • docs/tested: the earlier instrumented-backport runs, and the Deck runs of this branch against main (the four main records are the same files in #468 and #469: one shared main arm, which merges as one).

Measurements

Verdict: no measurable change in the game: on a GPU-bound Steam Deck Field scene 20.60 against 20.75 fps (inside the spread), and on a tester's CPU-bound Deck view 0.2-0.8% fewer instructions a frame, no larger than the spread between two starts; the lookup itself drops from 424 to 110 ns a query in a Deck microbenchmark. No fault anywhere.

Hardware: Steam Deck OLED, AMD Custom APU 0932 (RADV VANGOGH), Mesa 26.2.0-devel (git-5c65d4fc31), 16 GB shared, SteamOS 3.9.5 (kernel 7.2.7-valve2)
Scene: Hyrule Field (child), arriving from Kokiri Forest by teleport to the Lost Woods bridge entrance, day; 1280×720 output, render resolution option 4, no frame generation, precompile off, 1536 MiB texture budget, 256 MiB mirror budget, headless
Method: one scripts/dev_packs.py base at main 9c9d69ed (release optimisation, x86-64-v3); main is the base's runtime with no pack, this PR a renderer pack (longshot_vk.so only; the host binary is byte-identical). One warm-up pass of main made the caches, copied fresh into every run. Each run: child Kokiri save settled 20 s, teleport to HyruleField_Child, 20 s warm-up, 45 s stationary sample. Four rounds in one session, interleaved main, #468, #469, #472. Round 3's run of this PR was cut by a 7-second Deck suspend (a keep-awake handover) and is left out; round 4 ran after the resume, with main's round-4 run (20.64 FPS) inside main's earlier spread and the GPU at 1040 MHz again. Medians of the 4 runs of main and the 3 valid runs of this PR. FPS and present intervals from HOS_PRESENT_TRACE in the 45 s window; GPU clock and busy from sysfs every 0.5 s.
Rebased since: the branch is now on main aa95d35f, the change at e70dc7fa (the same change as 8372892d, which 00ef9f68 carries, git patch-id). Not measured again by us on aa95d35f. Since 9c9d69ed main changed gpu/vk/vk_replay.cpp with a render thread for the replay (#588, Auto: on from 12 logical processors, so off on a Deck) and the Arc occlusion copy (#761), neither in the lookup this PR changes; p0lux's run below is on main 9e6f231d, which has #588, and agrees.
How to reproduce: build a base at main and a pack of this branch (python -I scripts/dev_packs.py base|pack|rt, docs/development.md "Testing a change against a base"), start each runtime with HOS_CONTROL and a child save at Kokiri spawn, then longshot_ctl.py teleport HyruleField_Child, wait 20 s and sample 45 s.

Before: main @ 9c9d69ed After: this PR @ 00ef9f68
FPS, Field sample (median) 20.60 (4 runs) 20.75 (3 runs)
Present interval median (ms) 48.56 48.22
Present interval p95 (ms) 51.79 51.52
GPU clock median (MHz) 1040 1040
GPU busy, median of the sample (%) 99 99
Device faults, run memory guard, crashes none in 4 runs none in 3 runs

Per run, FPS: main 20.56, 20.54, 20.69, 20.64; this PR 20.32, 20.77, 20.75. The PR's runs straddle main's: no frame-rate change, as expected while the Deck is GPU-bound here (the GPU 99% busy at its 1040 MHz clock, about 48 ms a frame).

main-r2:   [hos host] longshot v3.0.0-rc.3-75-g9c9d69ed (source 6b76ded3)
           vk: 5160 presents, 20.6 fps; a frame: 1176 draws (0 skipped), 145 dispatches, 155 passes, ... 80 ub updates (56 overlays)
pr472-r2:  renderer v3.0.0-rc.3-77-g00ef9f68, host as main's
           vk: 5040 presents, 20.9 fps; a frame: 1169 draws (0 skipped), 145 dispatches, 155 passes, ... 80 ub updates (56 overlays)

A tester's run where the replay thread sets the frame rate: p0lux, 2026-11-15 (#472 (comment)). Steam Deck OLED (RADV VANGOGH, SteamOS 3.9.5, 16 GB), the compute fallback, headless; a still heavy view loaded from a save, about 2,150 draws a frame, the replay about 25 ms of a 30 to 32 ms frame. The render thread's user-space instructions and cycles a frame from perf stat over 720 presents. The two builds are the renderer alone, swapped in one game folder built from main 9e6f231d, without and with this PR and #551 together (18243506, d4c9070b and e8faf57e cherry-picked), on top of other open PRs that tester plays with, the same in both; two starts a build, in both orders.

p0lux's Deck without with #472 and #551
instructions a frame, pair 1 51.44 M 51.32 M (-0.2%)
the same, pair 2 (other order) 51.92 M 51.50 M (-0.8%)
cycles a frame 64.43 / 65.58 M 63.78 / 65.07 M (-1.0%, -0.8%)
fps 33.41 / 30.82 33.40 / 31.16

The same sign on both pairs, but no larger than the spread between two starts of one build there (about 0.6% on instructions): no cost, and no gain the tester can state. No fatal line and no GPU timeout in the kernel log on the four starts. Its limits: built with #551, and with the committed play profile, under which a changed function is compiled without its profile until the next recording, which can hide a small gain.

A differential benchmark compares answers and cache contents across 100,000 randomized spans and explicit boundaries. On the Deck OLED, in a mixed workload of 80 pending updates, the two-pass calculation took 424 ns per query and the direct scan 110 ns (medians of five interleaved samples per arm); the inputs deliberately exercise the slow path. Earlier, an instrumented v3.0.0-rc.3 renderer (665aef4f4718) with a test switch choosing the scan (RX_VK_TEST_UNIFORM_GAP, not in this PR) gave 20.19 FPS both ways over two interleaved runs each, about 63 scans per present (docs/tested/2026-11-14-field-renderer-zeldaootr-scan-*.md).

Flags and settings

None

Testing

Tested: On the Steam Deck OLED above, this PR interleaved with main through the Kokiri Forest to Hyrule Field transition: Field reached every time (status map HyruleField_Child, 915-935 presents in each 45 s sample), no device fault, no VK_ERROR, no crash record, the run's low-memory guard never tripped, every run exited 0. p0lux's still-view Deck run (in Measurements): no fatal line, no GPU timeout. Source-extracted original and candidate agree for overflow, empty queries, subranges, boundary equality and overlapping writes. Game-free, on this branch rebased onto main aa95d35f (57723ffb): tests/test_ub_gap.py passes with g++ 13.3 and with clang 23.1.5; at the tip, the 88 checks that read gpu/vk/vk_replay.cpp or the changed test: 85 passed, and 3 failed that fail or are skipped by OS on main aa95d35f too (test_readback_late, test_walk_key, and test_present_aspect_vulkan, which needs a GPU and was run here by name). With #468 and #469, as rebased, the branch merges cleanly (git merge-tree).

Not tested: The replay thread's CPU time in our own runs (the counters that measured it were in the instrumented build only; p0lux's perf stat is the in-game number); the lookup on #588's render thread (off on a Deck); Windows and other GPUs and drivers in the game.

Must be tested:

  • Windows 10/11, any GPU: build this PR (with llvm-mingw), stand at the Hyrule Field entrance for 1-2 minutes, then turn the camera and walk, expecting fps like main at the same spot and no missing, stale or flickering objects or wrong matrices (by hand, test request T-NTAP: post the fps of main and of the PR, and the GPU, driver and OS)

Risks

What could break: a false gap would let a descriptor read stale data. The test exercises exact boundaries and restricted update ranges, and the differential benchmark compares the answers of both versions.

Turning it off: revert the commit

Compatibility: Nothing changes

Issues

None filed; found by profiling Hyrule Field on the Steam Deck. The GPU timing breakdown of this scene is in the Deck evidence PR, #470.

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main. Onto main aa95d35f, no conflict; the three commits are the same changes as before (git patch-id).
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named). The full suite on main aa95d35f (Linux, g++ 13.3, the Vulkan headers set): 528 passed, 15 failed, 50 skipped; on this branch the changed test and the 88 checks that read the changed files ran, with no failure that main does not have (see Testing). The 15 that fail on main: test_appimage_player_build, test_bake_portability, test_cpm_discovery, test_crash_dialog, test_env_on, test_lift_cache, test_lift_cleanup_error, test_make_game_cache, test_mods_size_table, test_options_budget_once, test_pipeline_tour, test_readback_late, test_vk_pgo, test_walk_key and nx2/test_texture_init_barrier.py.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

When it applies

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • A file in docs/tested/ for each new measured run (format in docs/tested-configurations.md).
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Before the rebase, built on Linux for both: RX_VK_ONLY=1 RX_RELEASE=1 RX_TARGET=windows with llvm-mingw 20261104 linked longshot_vk.dll, and the same without RX_TARGET linked longshot_vk.so; after the rebase, gpu/vk/vk_replay.cpp at 57723ffb checked whole (-fsyntax-only -Wall -Wextra) with clang 23.1.5 for Linux and llvm-mingw 20261104 for Windows: the same diagnostics as main aa95d35f's (60 and 60), none new. The diff has no path handling.
**Type:** perf · **Risk:** low · **Tested on:** Steam Deck OLED, SteamOS 3.9.5, RADV (ours and a tester's runs); Linux x86-64 (game-free) · **Needs:** Windows, any GPU ## Why Each uncached lookup for pending uniform-buffer writes copies two arrays of addresses and then walks them again. Both descriptor reads and uniform-buffer copies pay for this work on the replay thread, which sets the frame rate wherever the GPU is not the limit. ## What changed The lookup now scans the pending updates where they are instead of copying their addresses into two arrays first: the same answer and the same cached gap, with less work per query. - vk replay: the pending uniform-update gap lookup (`ub_gap`, `ub_try_gap`) in `gpu/vk/vk_replay.cpp` scans the existing `ub_updates` deque over the requested iterator range. It stops on the first overlap and otherwise caches the same gap; the epoch check still guards publication of a cached answer. A background thread (`ub_limit == 0`, an empty range) builds no iterator into the deque at all, as before: it must not read even its start while the replay thread pushes and prunes. The elements read are the ones the copy read. - tests: `tests/test_ub_gap.py` follows the new scan and checks the empty range with no deque behind it. - docs/tested: the earlier instrumented-backport runs, and the Deck runs of this branch against main (the four main records are the same files in https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/468 and https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/469: one shared main arm, which merges as one). ## Measurements **Verdict:** no measurable change in the game: on a GPU-bound Steam Deck Field scene 20.60 against 20.75 fps (inside the spread), and on a tester's CPU-bound Deck view 0.2-0.8% fewer instructions a frame, no larger than the spread between two starts; the lookup itself drops from 424 to 110 ns a query in a Deck microbenchmark. No fault anywhere. Hardware: Steam Deck OLED, AMD Custom APU 0932 (RADV VANGOGH), Mesa 26.2.0-devel (git-5c65d4fc31), 16 GB shared, SteamOS 3.9.5 (kernel 7.2.7-valve2) Scene: Hyrule Field (child), arriving from Kokiri Forest by teleport to the Lost Woods bridge entrance, day; 1280×720 output, render resolution option 4, no frame generation, precompile off, 1536 MiB texture budget, 256 MiB mirror budget, headless Method: one `scripts/dev_packs.py` base at main 9c9d69ed (release optimisation, x86-64-v3); main is the base's runtime with no pack, this PR a renderer pack (`longshot_vk.so` only; the host binary is byte-identical). One warm-up pass of main made the caches, copied fresh into every run. Each run: child Kokiri save settled 20 s, teleport to HyruleField_Child, 20 s warm-up, 45 s stationary sample. Four rounds in one session, interleaved main, #468, #469, #472. Round 3's run of this PR was cut by a 7-second Deck suspend (a keep-awake handover) and is left out; round 4 ran after the resume, with main's round-4 run (20.64 FPS) inside main's earlier spread and the GPU at 1040 MHz again. Medians of the 4 runs of main and the 3 valid runs of this PR. FPS and present intervals from `HOS_PRESENT_TRACE` in the 45 s window; GPU clock and busy from sysfs every 0.5 s. Rebased since: the branch is now on main aa95d35f, the change at e70dc7fa (the same change as 8372892d, which 00ef9f68 carries, `git patch-id`). Not measured again by us on aa95d35f. Since 9c9d69ed `main` changed `gpu/vk/vk_replay.cpp` with a render thread for the replay (#588, Auto: on from 12 logical processors, so off on a Deck) and the Arc occlusion copy (#761), neither in the lookup this PR changes; p0lux's run below is on main 9e6f231d, which has #588, and agrees. How to reproduce: build a base at main and a pack of this branch (`python -I scripts/dev_packs.py base|pack|rt`, docs/development.md "Testing a change against a base"), start each runtime with `HOS_CONTROL` and a child save at Kokiri spawn, then `longshot_ctl.py teleport HyruleField_Child`, wait 20 s and sample 45 s. | | Before: main @ 9c9d69ed | After: this PR @ 00ef9f68 | |---|---|---| | FPS, Field sample (median) | 20.60 (4 runs) | 20.75 (3 runs) | | Present interval median (ms) | 48.56 | 48.22 | | Present interval p95 (ms) | 51.79 | 51.52 | | GPU clock median (MHz) | 1040 | 1040 | | GPU busy, median of the sample (%) | 99 | 99 | | Device faults, run memory guard, crashes | none in 4 runs | none in 3 runs | Per run, FPS: main 20.56, 20.54, 20.69, 20.64; this PR 20.32, 20.77, 20.75. The PR's runs straddle main's: no frame-rate change, as expected while the Deck is GPU-bound here (the GPU 99% busy at its 1040 MHz clock, about 48 ms a frame). ``` main-r2: [hos host] longshot v3.0.0-rc.3-75-g9c9d69ed (source 6b76ded3) vk: 5160 presents, 20.6 fps; a frame: 1176 draws (0 skipped), 145 dispatches, 155 passes, ... 80 ub updates (56 overlays) pr472-r2: renderer v3.0.0-rc.3-77-g00ef9f68, host as main's vk: 5040 presents, 20.9 fps; a frame: 1169 draws (0 skipped), 145 dispatches, 155 passes, ... 80 ub updates (56 overlays) ``` A tester's run where the replay thread sets the frame rate: p0lux, 2026-11-15 (https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/472#issuecomment-24174). Steam Deck OLED (RADV VANGOGH, SteamOS 3.9.5, 16 GB), the compute fallback, headless; a still heavy view loaded from a save, about 2,150 draws a frame, the replay about 25 ms of a 30 to 32 ms frame. The render thread's user-space instructions and cycles a frame from `perf stat` over 720 presents. The two builds are the renderer alone, swapped in one game folder built from main 9e6f231d, without and with this PR and https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/551 together (18243506, d4c9070b and e8faf57e cherry-picked), on top of other open PRs that tester plays with, the same in both; two starts a build, in both orders. | p0lux's Deck | without | with #472 and #551 | |---|---|---| | instructions a frame, pair 1 | 51.44 M | 51.32 M (-0.2%) | | the same, pair 2 (other order) | 51.92 M | 51.50 M (-0.8%) | | cycles a frame | 64.43 / 65.58 M | 63.78 / 65.07 M (-1.0%, -0.8%) | | fps | 33.41 / 30.82 | 33.40 / 31.16 | The same sign on both pairs, but no larger than the spread between two starts of one build there (about 0.6% on instructions): no cost, and no gain the tester can state. No fatal line and no GPU timeout in the kernel log on the four starts. Its limits: built with #551, and with the committed play profile, under which a changed function is compiled without its profile until the next recording, which can hide a small gain. A differential benchmark compares answers and cache contents across 100,000 randomized spans and explicit boundaries. On the Deck OLED, in a mixed workload of 80 pending updates, the two-pass calculation took 424 ns per query and the direct scan 110 ns (medians of five interleaved samples per arm); the inputs deliberately exercise the slow path. Earlier, an instrumented v3.0.0-rc.3 renderer (665aef4f4718) with a test switch choosing the scan (`RX_VK_TEST_UNIFORM_GAP`, not in this PR) gave 20.19 FPS both ways over two interleaved runs each, about 63 scans per present (`docs/tested/2026-11-14-field-renderer-zeldaootr-scan-*.md`). ### Flags and settings None ## Testing **Tested:** On the Steam Deck OLED above, this PR interleaved with main through the Kokiri Forest to Hyrule Field transition: Field reached every time (status map `HyruleField_Child`, 915-935 presents in each 45 s sample), no device fault, no `VK_ERROR`, no crash record, the run's low-memory guard never tripped, every run exited 0. p0lux's still-view Deck run (in Measurements): no fatal line, no GPU timeout. Source-extracted original and candidate agree for overflow, empty queries, subranges, boundary equality and overlapping writes. Game-free, on this branch rebased onto `main` aa95d35f (57723ffb): `tests/test_ub_gap.py` passes with g++ 13.3 and with clang 23.1.5; at the tip, the 88 checks that read `gpu/vk/vk_replay.cpp` or the changed test: 85 passed, and 3 failed that fail or are skipped by OS on `main` aa95d35f too (`test_readback_late`, `test_walk_key`, and `test_present_aspect_vulkan`, which needs a GPU and was run here by name). With https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/468 and https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/469, as rebased, the branch merges cleanly (`git merge-tree`). **Not tested:** The replay thread's CPU time in our own runs (the counters that measured it were in the instrumented build only; p0lux's `perf stat` is the in-game number); the lookup on #588's render thread (off on a Deck); Windows and other GPUs and drivers in the game. **Must be tested:** - Windows 10/11, any GPU: build this PR (with llvm-mingw), stand at the Hyrule Field entrance for 1-2 minutes, then turn the camera and walk, expecting fps like main at the same spot and no missing, stale or flickering objects or wrong matrices (by hand, test request T-NTAP: post the fps of main and of the PR, and the GPU, driver and OS) ## Risks **What could break:** a false gap would let a descriptor read stale data. The test exercises exact boundaries and restricted update ranges, and the differential benchmark compares the answers of both versions. **Turning it off:** revert the commit **Compatibility:** Nothing changes ## Issues None filed; found by profiling Hyrule Field on the Steam Deck. The GPU timing breakdown of this scene is in the Deck evidence PR, https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/470. ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [x] Rebased on current `main`. Onto main aa95d35f, no conflict; the three commits are the same changes as before (`git patch-id`). - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). The full suite on main aa95d35f (Linux, g++ 13.3, the Vulkan headers set): 528 passed, 15 failed, 50 skipped; on this branch the changed test and the 88 checks that read the changed files ran, with no failure that `main` does not have (see Testing). The 15 that fail on main: `test_appimage_player_build`, `test_bake_portability`, `test_cpm_discovery`, `test_crash_dialog`, `test_env_on`, `test_lift_cache`, `test_lift_cleanup_error`, `test_make_game_cache`, `test_mods_size_table`, `test_options_budget_once`, `test_pipeline_tour`, `test_readback_late`, `test_vk_pgo`, `test_walk_key` and `nx2/test_texture_init_barrier.py`. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### When it applies - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] A file in `docs/tested/` for each new measured run (format in `docs/tested-configurations.md`). - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Before the rebase, built on Linux for both: `RX_VK_ONLY=1 RX_RELEASE=1 RX_TARGET=windows` with llvm-mingw 20261104 linked `longshot_vk.dll`, and the same without `RX_TARGET` linked `longshot_vk.so`; after the rebase, `gpu/vk/vk_replay.cpp` at 57723ffb checked whole (`-fsyntax-only -Wall -Wextra`) with clang 23.1.5 for Linux and llvm-mingw 20261104 for Windows: the same diagnostics as `main` aa95d35f's (60 and 60), none new. The diff has no path handling.
Assisted-by: OpenAI Codex
Assisted-by: OpenAI Codex
Assisted-by: OpenAI Codex
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
move the Field GPU timing row to the Deck evidence PR
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 17s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
6a0727c9ae
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
zeldaootr force-pushed scan-uniform-updates from 6a0727c9ae
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 17s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
to a2b24e4baf
All checks were successful
beanstalk/approval Test farm not requested
PR description / Description follows the template (pull_request_target) Successful in 4s
2026-11-14 18:57:58 +00:00
Compare
Author
Contributor

Rebased onto main 1be60eae and rewritten: the code change is one commit, the Field GPU timing record that belonged to another PR is gone from the history, and the run records are renamed to the tester handle. Measured on a Steam Deck OLED against main: three valid rounds here (a fourth was interrupted by a suspend and is left out), medians 20.60 fps on main and 20.75 here: no change beyond the spread, as expected in a GPU-bound scene. Still WIP: not built for Windows yet; the test request asks for that.

Rebased onto main 1be60eae and rewritten: the code change is one commit, the Field GPU timing record that belonged to another PR is gone from the history, and the run records are renamed to the tester handle. Measured on a Steam Deck OLED against main: three valid rounds here (a fourth was interrupted by a suspend and is left out), medians 20.60 fps on main and 20.75 here: no change beyond the spread, as expected in a GPU-bound scene. Still WIP: not built for Windows yet; the test request asks for that.
Author
Contributor

Built for Windows from Linux with llvm-mingw 20261104 (the renderer alone, RX_VK_ONLY, as the CI job does) and for Linux: both link, and the changed file has no new warning under -Wall -Wextra on either. The Windows box is ticked; the Windows test request stays open for a run on real hardware.

Built for Windows from Linux with llvm-mingw 20261104 (the renderer alone, RX_VK_ONLY, as the CI job does) and for Linux: both link, and the changed file has no new warning under -Wall -Wextra on either. The Windows box is ticked; the Windows test request stays open for a run on real hardware.
zeldaootr changed title from WIP: perf(vk): scan pending uniform updates in place to perf(vk): scan pending uniform updates in place 2026-11-14 19:24:36 +00:00
Contributor

A Steam Deck run in a view where the replay thread sets the frame rate, to go with the GPU-bound Field numbers.

Setup: Steam Deck OLED (RADV VANGOGH, SteamOS 3.9.5, 16 GB), the compute fallback, headless. A still heavy view loaded from a save: about 2,150 draws a frame, the replay about 25 ms of a 30 to 32 ms frame. The render thread's user-space instructions and cycles a frame come from perf stat over 720 presents. The two builds are the renderer alone, swapped in one game folder built from main 9e6f231d.

before with #472 and #551
instructions a frame, pair 1 51.44 M 51.32 M (-0.2%)
the same, pair 2 (other order) 51.92 M 51.50 M (-0.8%)
cycles a frame 64.43 / 65.58 M 63.78 / 65.07 M (-1.0%, -0.8%)
fps 33.41 / 30.82 33.40 / 31.16

The same sign on both pairs, but no larger than the spread between two starts of one build here (about 0.6% on instructions): no cost, and no gain I can state. No fatal line and no GPU timeout in the kernel log on the four starts.

Limits: the two builds differ by this PR and #551 together (18243506, d4c9070b and e8faf57e cherry-picked), on top of main 9e6f231d plus the open PRs I play with (#223, #384, #387, #391, #567, #568, #590 and a local change), the same in both. Both use the committed play profile, and here a changed function is compiled without its profile until the next recording, which can hide a small gain. Two starts a build.

A Steam Deck run in a view where the replay thread sets the frame rate, to go with the GPU-bound Field numbers. **Setup:** Steam Deck OLED (RADV VANGOGH, SteamOS 3.9.5, 16 GB), the compute fallback, headless. A still heavy view loaded from a save: about 2,150 draws a frame, the replay about 25 ms of a 30 to 32 ms frame. The render thread's user-space instructions and cycles a frame come from `perf stat` over 720 presents. The two builds are the renderer alone, swapped in one game folder built from main 9e6f231d. | | before | with #472 and #551 | |---|---|---| | instructions a frame, pair 1 | 51.44 M | 51.32 M (-0.2%) | | the same, pair 2 (other order) | 51.92 M | 51.50 M (-0.8%) | | cycles a frame | 64.43 / 65.58 M | 63.78 / 65.07 M (-1.0%, -0.8%) | | fps | 33.41 / 30.82 | 33.40 / 31.16 | The same sign on both pairs, but no larger than the spread between two starts of one build here (about 0.6% on instructions): no cost, and no gain I can state. No fatal line and no GPU timeout in the kernel log on the four starts. **Limits:** the two builds differ by this PR and #551 together (18243506, d4c9070b and e8faf57e cherry-picked), on top of main 9e6f231d plus the open PRs I play with (#223, #384, #387, #391, #567, #568, #590 and a local change), the same in both. Both use the committed play profile, and here a changed function is compiled without its profile until the next recording, which can hide a small gain. Two starts a build.
Author
Contributor

Thank you, p0lux, for the CPU-bound Deck run: it is in the description as you reported it (0.2-0.8% fewer instructions a frame, inside the start-to-start spread, fps unchanged, built together with #551), beside our GPU-bound Field numbers. Rebased onto main aa95d35f with no conflict (the same three changes, git patch-id) and the description moved to the current template.
tests/test_ub_gap.py and the 88 checks that read vk_replay.cpp were run again on the new base (no failure main lacks); our Field numbers still hold because main's changes to vk_replay.cpp since 9c9d69ed (#588's render thread, off on a Deck; #761's Arc occlusion copy) do not touch the lookup, and p0lux's run on 9e6f231d, after #588, agrees. T-NTAP (Windows) still waits for the testing wanted label.

Thank you, p0lux, for the CPU-bound Deck run: it is in the description as you reported it (0.2-0.8% fewer instructions a frame, inside the start-to-start spread, fps unchanged, built together with #551), beside our GPU-bound Field numbers. Rebased onto `main` aa95d35f with no conflict (the same three changes, `git patch-id`) and the description moved to the current template. `tests/test_ub_gap.py` and the 88 checks that read `vk_replay.cpp` were run again on the new base (no failure `main` lacks); our Field numbers still hold because `main`'s changes to `vk_replay.cpp` since 9c9d69ed (#588's render thread, off on a Deck; #761's Arc occlusion copy) do not touch the lookup, and p0lux's run on 9e6f231d, after #588, agrees. T-NTAP (Windows) still waits for the `testing wanted` label.
zeldaootr force-pushed scan-uniform-updates from a2b24e4baf
All checks were successful
beanstalk/approval Test farm not requested
PR description / Description follows the template (pull_request_target) Successful in 4s
to 57723ffb41
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Guide screenshots / Guide images and local UI source review (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 4s
2026-11-15 16:43:24 +00:00
Compare
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Guide screenshots / Guide images and local UI source review (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 4s
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'scan-uniform-updates:zeldaootr-scan-uniform-updates'
git switch 'zeldaootr-scan-uniform-updates'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!472
No description provided.