Skip to content

feat(shader): support 32-bit image atomics through keyed image tables - #8792

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
SuarezPM:ai/image-atomics-keyed-tables
Oct 23, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
SuarezPM:ai/image-atomics-keyed-tables

Conversation

@SuarezPM

Copy link
Copy Markdown
Contributor

What

32-bit image atomics through keyed image tables no longer fault. A T# keyed at runtime feeding image_atomic_*32 now lowers to OpAtomic* on the table's AtomicView instead of unsupported operation, so compute using bindless UAVs stops dying on the first atomic dispatch. Follows up the stores support from #6402, which left atomics faulting ("Atomic, packed, BY2/BY4 and dynamic-mip table stores still fault").

  • ResourceTracker::AssignTables keeps tableOperation supported for 32-bit atomics; 64-bit atomics, packed and BY formats still mark Unsupported.
  • Table elements carry the atomic flag through dedup into the descriptor binding (DescriptorBindingBuilder, ShaderResources), and the table write path binds AtomicView exactly like the direct path.
  • docs/dev/debt/shader.md rewrites the image-table entry: 32-bit atomics supported (Device scope, relaxed, AcquireRelease/UniformMemory barrier as the direct path); faulting remainder is image_store_mip/image_store_pck, PCK2/PCK4, MIMG BY2/BY4 and 64-bit atomics.

Tested

$ cmake -S . -B build-full -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON
$ cmake --build build-full
[4052/4052] built, exit 0

$ ctest --test-dir build-full --output-on-failure -E "trace_|progress"
100% tests passed out of 1732

$ ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ./build-full/tests/agc_driver_image_table_tests
image table tests passed (incl. new RunAtomicTests: add/swap/cmpswap on R32_UINT by runtime key, float-record add, null-entry no-reach)

$ ./build-full/tests/agc_shader_memory_tests
Shader memory capture, strict validation and deterministic replay passed

$ python3 tools/check_conventions.py --base origin/main
All conventions checks passed.

$ python3 tools/pr_overlap.py --branch ai/image-atomics-keyed-tables
Line-level contact only with #7803 (MSAA sample-0) and #8429 (FMASK width): neighbouring debt entries, no same-function overlap (reviewed manually).

Host: Linux x86-64 (CachyOS), Ryzen 5 3600, RTX 2060 SUPER (NVIDIA 615.78.08). No validation layer installed on this host, so no VUIDs could be produced here; full builds run on merge.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: NVIDIA RTX 2060 SUPER, driver 615.78.08, CachyOS Linux (execution tests with validation layers requested; no VUIDs observed — validation layer not installed on host)
  • AI-assisted: yes

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I found a remaining StorageView in the recorded image-table descriptor update path. In ShaderResources::buildComplete(), the binding.flags != 0 branch around line 2349 constructs VkDescriptorImageInfo with storageTextures[index]->StorageView(...) unconditionally. The compact-binding path below it (around line 2394) now selects AtomicView when storageAtomic[index] is set, but table descriptors use the recorded branch. Since this PR marks table elements atomic in resolveImageTables(), the table can still be written with the non-atomic view and the new execution tests may fail or trigger Vulkan validation there. Please make the recorded table write use the same storageAtomic64 / storageAtomic view selection as the compact path, then run the image-table tests with validation when available. Hosted GPU/build jobs are skipped on the current PR head, so they do not verify this branch.

@github-actions

github-actions Bot commented Oct 21, 2026 •

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 907 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 5021 core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 5371 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 5609 core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
PR 6192 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6194 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6202 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6223 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6235 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/debt/shader.md
PR 6280 core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 6412 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
docs/dev/debt/shader.md
PR 6481 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/execution/ImageTable.cpp
PR 6488 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 6539 core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
PR 6924 docs/dev/debt/shader.md
PR 7085 core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 7410 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/debt/shader.md
PR 7484 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/shader.md
PR 7550 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
PR 7618 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 7684 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/libs/prx/libSceAgcDriver/tests/execution/ImageTable.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
docs/dev/debt/shader.md
PR 7689 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 7922 docs/dev/debt/shader.md
PR 8004 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/debt/shader.md
PR 8077 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
docs/dev/debt/shader.md
PR 8998 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 9020 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
docs/dev/debt/shader.md
PR 9049 core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
PR 9183 core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 9193 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/debt/shader.md
PR 9335 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
docs/dev/debt/shader.md
PR 9356 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 9387 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 9400 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 9508 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 9592 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 9633 core/libs/prx/libSceAgcDriver/tests/execution/ImageTable.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 9674 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 9748 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
docs/dev/debt/shader.md
PR 9749 core/libs/prx/libSceAgcDriver/tests/execution/ImageTable.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 9852 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
PR 9889 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 9897 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
PR 9957 core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
PR 9969 core/libs/prx/libSceAgcDriver/tests/execution/ImageTable.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 9989 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 10000 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
docs/dev/debt/shader.md
PR 10049 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
PR 10064 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 10193 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 10194 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 10348 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 10359 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Correction to my earlier review: I misread the recorded table descriptor branch. It already selects AtomicView for atomic entries (and Atomic64View for 64-bit entries in the compact branch); the table image-info construction shown here is correct. I withdraw my request for changes. The PR checks currently pass for eligibility, conventions, intake, labels, and report; build/runtime jobs are skipped, so I have not independently verified the GPU execution claim.

@github-actions github-actions Bot added shader core/shader docs agc libSceAgc* labels Oct 21, 2026
@SuarezPM

Copy link
Copy Markdown
Contributor Author

Thanks for the careful review and the correction, MarcZX10 — glad the recorded table branch reads correctly on re-read. GPU execution evidence is in the PR body (RTX 2060 SUPER runs); happy to add any extra run you want. Leaving it in the maintainers' queue.

@xXPBSXx

xXPBSXx commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Windows data point for a55b76e09 merged onto main a683688fe (clean, git merge-tree rc 0): Windows 10.0.19045, MinGW-w64 GCC 15.2.0, Release. ctest -N -R '^agc_shader_memory$' lists exactly that one test; ctest --no-tests=error -R '^agc_shader_memory$' was run three times per tree.

  • With the PR: passes 3 of 3 (0.9-1.1 s).
  • Same tree with the non-test files of the PR (ShaderResources.cpp, ResourceMaterializer.hpp/.cpp, DescriptorBindingBuilder.cpp, ResourceTracker.cpp) restored to main (the new test cases kept): fails 3 of 3 (ordinary test failure, not a crash).
    Not tested: the new execution/ImageTable.cpp cases (a GPU test, not run), other platforms, a title run.
    AI-assisted (Claude Code).

@thuanquang

Copy link
Copy Markdown
Contributor

An AMD proprietary-driver run: RX 6700 XT (gfx1031), AMD Windows driver 32.0.21045.5002, Windows 11 26200, MinGW GCC 15.2.0, Release, ANYPS5_REQUIRE_VULKAN=1.

  • main 338f22e49 with only this PR's ImageTable.cpp and ShaderMemory.cpp: agc_driver_image_table fails with image table registration: a table T# feeding image_atomic_add was not a supported table operation, and agc_shader_memory fails too. agc_driver_image_atomics and agc_driver_image_atomics_float pass.
  • main 338f22e49 merged with this head a55b76e09 (clean merge): all four pass 3 of 3.
  • Khronos validation layer, ctest -V on the same four: main a07e985c8 and a07e985c8 + this head both pass 4 of 4. There are 0 VUID- lines on either side, and the layer was inserted in each GPU test process.

On the float-record case: integer image_atomic_add on a 32_FLOAT texel adds the raw bits on RDNA 2, as the test expects. I measured this earlier on the same card through the HIP oracle for #7939: the stored value matched the raw-integer model on all 121 rows, in wave32 and wave64.

@SuarezPM

Copy link
Copy Markdown
Contributor Author

Thanks xXPBSXx and thuanquang — the reverted-to-main control failing 3/3 and the AMD + validation 0-VUID runs are exactly the evidence this PR needed, including the float-record confirmation against your HIP oracle rows. Much appreciated.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@SuarezPM thanks for the update. The full CI run on a55b76e (merged into current main) fails three tests on Linux, and the cause is in this PR:

  • agc_shader_bindless, agc_shader_memory and agc_driver_image_table all stop with SPIR-V validation before optimization failed: OpPhi's result type <id> '1[%1]' does not match incoming value ..., e.g. %246 = OpPhi %uint %263 %254 %272 %267 %273 %248.
  • Main passes all three in the same lane at the same base, and the commits between bases don't touch the shader recompiler.

Why it shows up only there: only the Linux CI lane configures with -DANYPS5_ENABLE_SPIRV_TOOLS=ON (build.yml:511). The option defaults to OFF, so Windows CI and a default local build skip validation, and GPU drivers accept the module. That is why the execution runs on real hardware passed.

Cause: before this PR, 32-bit image atomics never reached the keyed-table path, so the table merge in SpirvImageEmitter.cpp could assume a 4 x u32 result. With atomics now in the table path, the result is a scalar %uint, but the poisoned-element branch still feeds the merge Phi a vector zero:

// SpirvImageEmitter.cpp:1971
if (returnsValue) incoming.insert(incoming.end(), {ConstantU32CompositeZero(state, 4u), invalid});

The sampler-guard Phi at :1988 has the same constant. ZeroResult(ctx, inst) in the same file already returns the right zero for U32, U64 and U32x4, so using it at both places should fix it. To reproduce locally, configure with -DANYPS5_ENABLE_SPIRV_TOOLS=ON and run agc_shader_bindless.

@SuarezPM

Copy link
Copy Markdown
Contributor Author

Fixed as suggested: both merge sites now use ZeroResult(ctx, inst) (commit on top of current main, rebased cleanly). Reproduced your lane locally with ANYPS5_ENABLE_SPIRV_TOOLS=ON: agc_shader_bindless, agc_shader_memory and agc_driver_image_table all pass, plus the Turing execution runs. The missing submodule for the new main (3rdparty/vgmstream) was the only rebase friction; no code conflicts.

@SuarezPM
SuarezPM force-pushed the ai/image-atomics-keyed-tables branch from a55b76e to 40f112e Compare October 22, 2026 10:38
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 23, 2026
@adriadam10 adriadam10 self-assigned this Oct 23, 2026
Table T#s keyed at runtime no longer fault on image_atomic_*32: the
table operation stays supported, elements carry the atomic flag through
dedup into the descriptor binding, and the table path binds AtomicView
like the direct path. 64-bit atomics stay unsupported, packed and BY
formats stay unsupported, and the debt entry records the relaxed
Device-scoped emission and the remaining gaps.
Table accessors returning scalar uint fed the merge Phi a 4-wide
zero, failing SPIR-V validation with SPIRV_TOOLS on. Use ZeroResult
like the unsupported path.
@adriadam10
adriadam10 force-pushed the ai/image-atomics-keyed-tables branch from 40f112e to 0ef31ba Compare October 23, 2026 19:46
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 23, 2026
@adriadam10
adriadam10 merged commit 495b7c1 into boykopovar:main Oct 23, 2026
10 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Re-checked after main moved: the SRT branch-condition change doesn't interact with the table atomic path, and the code merges cleanly. Only the docs/dev/debt/shader.md adjacency conflicted; rebased keeping main's FMASK and MSAA entries and this PR's rewritten image-table entry. 32-bit atomics through tables use the AtomicView descriptor path with the flag carried through dedup, 64-bit/packed/BY stay unsupported and documented, and the merge phis use ZeroResult; merged.

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

Labels

agc libSceAgc* docs shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants