Skip to content

perf(shader): emit runtime V# byte writes through the looping helper - #6706

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
milescoviello:perf/runtime-bda-byte-writes
Oct 19, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
milescoviello:perf/runtime-bda-byte-writes

Conversation

@milescoviello

@milescoviello milescoviello commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

What

#6372 (70a8844) made BDA stores through a bound V# (EmitBdaStore) send their byte path through one looping helper, write_bda_bytes. Stores through a V# the shader computes (EmitBdaWrite, from SpirvRuntimeBuffer.cpp) still emitted that path inline. Each 32-bit store had four get_bda_write_pointer lookups, mapped checks, byte stores and note_bda_write calls for its unaligned case, plus one lookup for the aligned case. Each 8- or 16-bit store had one lookup and one note per byte.

EmitBdaWrite now does the same overflow check as before and then calls write_bda_bytes. The helper does the same lookup, store and note per byte, in order, so faults and written pages are recorded as before.

Crimson Desert (PPSA25286) runs large compute programs whose stores go through computed V#s. On NVIDIA, how long a pipeline takes to create tracks how many calls its module makes. These modules make 500 to 2,000 calls, mostly fault, lookup and note calls, and on main they take up to two minutes each to create. The title reaches them only with #6612 and #6613.

In Crimson Desert, 900 s limit with scripted pad input, APS5_PROFILE_DRAW=1 (which logs every compute pipeline over 100,000 SPIR-V words with its creation time), #6612, #6613 and #6178 (for memory) on both sides:

Build Large compute pipelines Result
without this PR (#6612 at 0eeb815) 11 by 885 s, 501 s spent creating them, the longest 133 s no draw; my 16 GiB cap ends it at 885 s
with this PR (#6612 at de9f0f7) 21 by 597 s, 253 s spent creating them, the longest 41 s reaches its first draw at 597 s, which stops on AGC graphics: fragment shader must export float4 colors to its attachments

The #6612 heads differ only in their tessellation hull code, which these compute programs don't use. That new stop is a separate change.

Tested

Linux (Ubuntu 24.04, GCC 13.3, Release), RTX 4070 Ti, NVIDIA 595.99.

  • agc_driver_bda_device: its runtime write cases (byte, split short, split unaligned dword, read-only byte and suffix, unmapped middle byte, split aligned dword, each plain and coherent) already checked stored bytes, faults and written pages, and they pass with this change. A throwaway build that fails on this byte path shows that only this test reaches it in the 468 area tests. A new check in RunBdaContractTests requires a runtime V# write's byte path to go through write_bda_bytes. A 32-bit write may then make two get_bda_write_pointer calls (the helper's and the aligned store's), and an 8- or 16-bit write one. With main's SpirvBdaRead.cpp it fails with a runtime V# write of 8 bits has 2 write lookups and 0 byte-loop calls. It passes 3 of 3 with this change.
  • ctest (^agc_driver_|^agc_vertex_barrier|recompiler|shader_cache|request_serializer|spirv, 468 tests): 467 pass. agc_driver_store_over_untracked fails the same way on main 5d9dcc4 here; test(agc): keep waiting for the neighbour copy case's result #6385 addresses it.
  • Crimson Desert's seven slowest compute programs, from APS5_DUMP_SHADERS request files. Each was recompiled with agc_shader_replay built from main and from this PR. The SPIR-V was put into that program's own pipeline capture (APS5_DUMP_COMPUTE_PIPELINES, same layout and specialization), and creation was timed with agc_compute_pipeline_replay, with the NVIDIA shader disk cache off (__GL_SHADER_DISK_CACHE=0). The replay tool compiles for its own target, so the word counts differ a little from the title's. One run each:
Program SPIR-V words Calls Creation
A 101,515 → 90,081 874 → 482 33.6 → 6.2 s
B 146,697 → 141,791 694 → 526 22.0 → 9.6 s
C 171,597 → 160,775 1,093 → 722 50.4 → 14.3 s
D 336,216 → 318,702 2,028 → 1,426 123.9 → 32.5 s
E 568,775 → 552,250 1,483 → 916 88.7 → 21.5 s
F 815,765 → 802,296 1,286 → 824 84.4 → 28.4 s
G 786,605 → 783,437 528 → 420 57.9 → 37.1 s
H 172,146 → 140,520 2,212 → 1,127 218.9 → 27.3 s

That's 680 s for the eight on main and 177 s with this change. Program H, the slowest per word, has 776 write lookups and 776 write notes on main. The driver needs 8.6 GiB of memory to create its pipeline on main, and 1.2 GiB with this change (peak RSS of the replay process). Program A's own captured module, as the title compiled it, took 34.5 s in the same replay.

Validated on Windows 11 with an AMD Radeon 860M (RDNA 3.5, driver 32.0.13058.8001) by @LeandroLP23 (comment), in one build of main 6efb726 with this head and 13 other pull requests: agc_driver_bda_device and agc_driver_store_over_untracked pass 3 runs of 3. 713 of 716 agc_* tests pass; the 3 that fail fail the same way on main there.

Not checked on Intel or on a console.

Checklist

🤖 Generated with Claude Code

A store through a V# the shader computes (EmitBdaWrite) still emitted its
byte path inline: one get_bda_write_pointer lookup, a mapped check, a byte
store and a note_bda_write per byte, five write lookups per 32-bit store.
It now calls write_bda_bytes after the same overflow check, like stores
through bound V#s since 70a8844; the helper does the same lookup, store
and note per byte in order, so faults are recorded as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 18, 2026
@milescoviello
milescoviello force-pushed the perf/runtime-bda-byte-writes branch from 7eeb12f to 2f3e71b Compare October 18, 2026 14:24
@milescoviello

Copy link
Copy Markdown
Contributor Author

Rebased onto main a62c724 (head 2f3e71b). The only conflict was in tests/BdaContracts.cpp: #5859 added includes and its ordered-ranges checks at the end of RunBdaContractTests, next to this PR's helper-call checks. Both are kept. EmitBdaWrite is the same as before the rebase; #5859 didn't change it.

Tests on the RTX 4070 Ti (NVIDIA 595.99.02): 7 of the 8 tests matching bda|^shader_ir|request_serializer|agc_shader_replay pass. agc_driver_bda_device stops in #5859's new RunBdaAnchorTests with vkWaitForFences graphics: Vulkan result 2 (a 5 s fence timeout). It does the same on main a62c724 and on #5859's merge 644e384, and passes on 385ed71 just before it, so it isn't from this PR. With that one call skipped locally, the rest of agc_driver_bda_device, including RunBdaStoreExecutionTests and the contract checks for this PR's byte path, passes 3 of 3 on this head. I'll report the timeout separately.

AI-assisted: yes (Claude Code)

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 18, 2026
@LeandroLP23

Copy link
Copy Markdown
Contributor

AMD on Windows: the tests this PR names pass on an RDNA 3.5 GPU.

Radeon 860M (RDNA 3.5, driver 32.0.13058.8001), Windows 11 26300, WinLibs GCC 15.2.0 r7, Release. main 6efb7261b with this PR (2f3e71bae) merged, together with #7296, #7446, #7377, #7445, #6708, #6748, #6785, #6613, #7449, #7391, #7489, #7484, #7514. They were built once, so the result is for the combination; each merge was clean.

  • agc_driver_bda_device, agc_driver_store_over_untracked: pass, 3 runs of 3.
  • Every agc_* test on the combined build: 713 of 716 pass. The 3 that fail, agc_driver_host_import_lifetime, agc_driver_quad_pixel_mask and agc_driver_layered_volume_vertex_no_gpl, fail the same way on unchanged main here, so none comes from these PRs.
  • agc_compute_pipeline_replay and agc_shader_replay are not ctest names here; I did not replay captures.

AI-assisted

@milescoviello

Copy link
Copy Markdown
Contributor Author

Thanks @LeandroLP23 for the AMD run on Windows. I added it to the description.

AI-assisted: yes (Claude Code)

@github-actions github-actions Bot added perf Performance change perf numbers missing perf pull request without a before and after summary line labels Oct 18, 2026
@github-actions

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 7392 core/shader/recompiler/SpirvBackend/src/SpirvBdaRead.cpp

@adriadam10 adriadam10 self-assigned this Oct 19, 2026
@adriadam10
adriadam10 merged commit d65a58e into boykopovar:main Oct 19, 2026
10 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked that EmitBdaWrite now matches EmitBdaStore: the helper does the same per-byte lookup, store and note in order, the overflow check stays before the call, and write_bda_bytes/_coherent are defined together with get_bda_write_pointer. The new CallSites check fails on main (no helper calls) and passes with the change; the runtime write cases of agc_driver_bda_device still cover bytes, faults and written pages. Merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* perf numbers missing perf pull request without a before and after summary line perf Performance change shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants