Skip to content

feat(shader): read samplers through nested and pointer-keyed descriptor tables - #7395

Open
EagleBearerYS wants to merge 9 commits into
boykopovar:mainfrom
EagleBearerYS:feat/shader-nested-sampler-tables
Open

EagleBearerYS wants to merge 9 commits into
boykopovar:mainfrom
EagleBearerYS:feat/shader-nested-sampler-tables

Conversation

@EagleBearerYS

@EagleBearerYS EagleBearerYS commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

What

Depends on #7223. With #7223, Marvel's Wolverine (PPSA03671) and Ratchet & Clank: Rift Apart (PPSA01474) stop on the sampler half of the descriptor chain that #7223 resolves for images (#6191):

#7223 resolves record → pointer → V# → T# and leaves samplers refused. Its nested table representation, pointer dictionary and limits are what this PR builds on. The PR extends them to samplers and to a one-hop pointer whose low word is merged through a Phi. Three commits:

  1. fix(shader): refuse nested table entries whose words have other consumers. On feat(shader): resolve nested image tables through runtime pointers #7223's head, a nested child whose descriptor words another instruction also reads crashes lavapipe inside the shader. The child's V# words are planning-only loads and are never emitted, while the other consumer keeps its runtime read through a V# that exists only in the snapshot. The nested branch now refuses such a child, as feat(shader): resolve nested image tables through runtime pointers #7223 already refuses a V# with another consumer.
  2. feat(shader): resolve S# reads through nested descriptor tables. This is Wolverine's shape: record → pointer → V# → S#.
  3. feat(shader): look up a Phi-merged record pointer to an S# or T#. This is Ratchet & Clank's shape: record → pointer → S# through an address, with the pointer's low word merged through a Phi.
  4. fix(shader): accept a lane-mask word in a pointer-keyed record pointer. Ratchet & Clank: Rift Apart's next stop (ps54all, #6191): GetImageResource dword 0 is not a valid runtime value. A T# is read through a record pointer whose low word passes six levels of Phi, and one arm is a word of the waterfall loop's Ballot lane mask.
    • A pointer-keyed column now accepts such an arm as one that can't be a record pointer, as it does for constants. It also accepts copies of the record word read under other keys (same table, offset, stride and addend), since the lookup is by the pointer's value.
    • At run time, a pointer built from a lane mask faults as outside the snapshot.
    • This commit leaves the disk-cache format as it is: shaders that planned before plan the same way, and only ones refused before now plan.

Ordering. This PR is stacked on #7223, so until that merges its diff shows #7223's two commits too.

Tested

  • Merged with main 9caecfd (aed137c, the head), on GitHub's runners, at 3730bb8, the same merge with the same tree before the co-author line was added (run): the ^agc tests pass 3 runs in a row on Linux (715, 0 failed), and the libc and libkernel suites pass. On Windows the only failures are agc_driver_pm4_replay_tool and agc_shader_replay_tool, which fail the same way on that runner for an unrelated branch (run).
  • GitHub's Linux (lavapipe, Mesa 25.2.8) and Windows runners (verify run):
    • agc_driver_nested_image_table, agc_driver_nested_sampler_table, agc_driver_pointer_phi_sampler_table, agc_shader_disk_cache and agc_shader_disk_cache_large_file pass 10 of 10 runs on both runners. Three of them skip on the Windows runner.
    • The libc and libkernel suites pass.
    • With feat(shader): resolve nested image tables through runtime pointers #7223's code the new tests don't build, so the red below is from the Linux run.
  • Linux, a run that mirrors CI's Linux job (Ubuntu 24.04, GCC 13.3, lavapipe Mesa 25.2.8 / LLVM 20.1.2): 1376 tests on this branch, with the same 5 IPv6 failures as on feat(shader): resolve nested image tables through runtime pointers #7223's head on that machine. No new failures or skips.
  • New tests, red on feat(shader): resolve nested image tables through runtime pointers #7223's head and green here:
    • agc_driver_nested_sampler_table: S# through record → pointer → V#. It covers independent keys, wave32 against wave64, an edited S#, unmapped and non-buffer parents, shared children, the consumer refusals, captured and uncaptured Phi pointers, the S# budget and the nested key budget. Red: GetSamplerResource dword 0 is not a valid runtime value.
    • agc_driver_pointer_phi_sampler_table: S# through a Phi-merged record pointer. It covers the direct control, captured and uncaptured Phi constants, a constant equal to another record's pointer, a null pointer, a non-buffer record table, an S# with another consumer, and a T# through the same path.
    • agc_driver_nested_image_table gains "a T# with another consumer" (refused). Red is a SIGSEGV on feat(shader): resolve nested image tables through runtime pointers #7223's head.
    • agc_shader_disk_cache round-trips both new plan shapes and rejects a malformed pointer-keyed column.
  • The fourth commit's tests (b91f050), agc_driver_pointer_phi_ballot_table and agc_driver_pointer_phi_copies_table: synthetic wave64 waterfall loops shaped like Ratchet & Clank: Rift Apart's chain, where the lane-mask pointer faults as outside the snapshot. On GitHub's runners (run) both fail in all 3 runs on Linux with the third commit's code (4a4db4b) and pass 10 of 10 with the fourth, with the other nested and pointer tests and agc_shader_disk_cache; the libc and libkernel suites pass. On Windows they skip without a Vulkan device and agc_shader_disk_cache passes. A run that mirrors CI's Linux job gives 1378 tests against 1376 on 4a4db4b, with only the two new tests changing.
  • Repeats: 10 of 10 for each.
  • Ratchet & Clank: Rift Apart (PPSA01474, 01.005.003), run by ps54all on Windows 11 with an NVIDIA GeForce RTX 3080 10 GB (driver 610.74), an i9-10850K, WinLibs GCC 15.2 and a Release build (comment). Their test branch (main 82f0c9a with perf(agc): draw finisher pipeline (APS5_DRAW_PIPELINE) #6946, fix(agc): skip a periodic pipeline cache save while a pipeline creation holds the cache #6958, fix(recompiler): give a dynamic-mip storage image a slot for every level #7238, feat(shader): resolve nested image tables through runtime pointers #7223, fix(recompiler): read lane spills back through relative-addressing window registers #6277, fix(shader): plan image tables through invariant descriptor phis #6417, feat(agc): link calls through the AGC FS table #6160 and fix(ampr): return EBUSY when a command does not fit and EPERM before a buffer is bound #7300) plus this PR's three commits, cold shader cache, one run:
    • the GetSamplerResource stop no longer occurs (0 hits);
    • the title then stops 236 s in, on another compute shader, at an image read whose record pointer goes through six levels of Phi before LoadAddressU32 (GetImageResource dword 0 is not a valid runtime value). That is the title's next stop. The fourth commit (b91f050) addresses that chain; Tamalero has since run the title with it (below).
  • Ratchet & Clank: Rift Apart (PPSA01474, 01.005.003), run by Tamalero on CachyOS (Linux 7.2) with an NVIDIA GeForce RTX 4090 (driver 615.78.08), GCC and a Release build (comment). This PR's head b91f050 against feat(shader): resolve nested image tables through runtime pointers #7223's head f454944 as the control, both on main 66702f1 (feat(shader): resolve nested image tables through runtime pointers #7223's base) with no local patches, fresh shader cache, one run each:
    • the control stops about 480 s in, at GetSamplerResource dword 0 (pc=0x6b74, 3 refusals);
    • with this PR there are no sampler refusals, and neither stop from Tamalero's earlier runs of feat(shader): resolve nested image tables through runtime pointers #7223 recurs: the S# at pc=0x6b74 and the T# at pc=0x7cbc;
    • the title then stops about 470 s in, on another compute shader, at GetImageResource dword 0 (pc=0x99ec, 3 refusals). Its record pointer goes through a deeper Phi nest, with a word of the Ballot lane mask ANDed into it (BitwiseAnd32), a shape the fourth commit doesn't cover. That is the title's next stop.
  • Ratchet & Clank: Rift Apart (PPSA01474), run by ps54all on Windows 11 with an NVIDIA GeForce RTX 3080 (driver 610.74), MinGW GCC 15.2.0 and a Release build (comment). Their test branch (main 82f0c9a with open PRs, already carrying this PR's earlier commits) plus the fourth commit (b91f050), one run each before and after:
    • the stop on compute shader 0x116bb5f500 (LoadAddressU32@pc=0x8fd4, six levels of Phi) no longer occurs;
    • the title then stops about 214 s in, on a black window, on compute shader 0x116bb67000 (GetImageResource dword 0 is not a valid runtime value, LoadAddressU32@pc=0x9a30): a seven-level Phi nest with a Ballot lane-mask word ANDed in and ReadFirstLane(SelectU32 …) arms. That is Tamalero's next stop (above) in a sibling shader: both chains read the record table at pc=0x1aa8, and the stop and the second buffer's reads sit 0x44 later than in Tamalero's (0x9a30, 0x908c and 0x900c against 0x99ec, 0x9048 and 0x8fc8).
  • Hardware test runs by Capmare at aed137c, the head before the merge of main 61d3d30: Rocky Linux 10.2, Release, BUILD_TESTING=ON, 735 graphics tests per GPU, no title (comment):
    • NVIDIA GeForce RTX 4070 SUPER (driver 615.71.09): all 735 pass, agc_driver_nested_sampler_table and agc_driver_pointer_phi_sampler_table among them;
    • AMD Radeon RX 7600 (RADV, Mesa 25.2.7) and llvmpipe (Mesa 25.2.7): both new sampler tests pass 3 of 3 runs. On the RX 7600, agc_pipeline_cache_file fails, which Capmare reports as unrelated;
    • Intel UHD 770 (ANV, Mesa 25.2.7): both new sampler tests fail 3 of 3 runs, each reading the other test's expected sampler result ("lane 0 is 0x3e800000, expected 0x427d0000" and the reverse). On ANV, workgroup 1 gets workgroup 0's sampler through the non-uniform sampler-array pick. Capmare reproduced it in a standalone Vulkan program with no AnyPS5 code (a compute stage with requiredSubgroupSize = 32, local_size_x = 32 and a sampler array of more than 2 elements indexed per workgroup), so it's an ANV issue, filed as Mesa issue 16622 (comment, comment). The tests stay as they are. The existing nested image-table tests pass on all three GPUs.
  • Merged with main 61d3d30 (ed4f148), on GitHub's runners (run): the ^agc tests pass on Linux (734, 0 failed, 49 skipped), and the suites' only failure is libc's precise_wait timing test. On Windows, where 551 of the 734 skip without Vulkan, the only failures are agc_driver_pm4_replay_tool and agc_shader_replay_tool (exit code 0xc0000139), which fail the same way on that runner for an unrelated branch (run).
  • Merged with main ff2fc57 (59b319a, the head), on GitHub's runners (run): the ^agc and ^shader tests pass on Linux (739, 0 failed, 49 skipped), and the suites' only failure is again libc's precise_wait timing test. On Windows, where 551 of the 738 skip without Vulkan, the only failures are again agc_driver_pm4_replay_tool and agc_shader_replay_tool (exit code 0xc0000139).
  • Not tested: Marvel's Wolverine (PPSA03671) (we asked Capmare to run the branch on #6191); AMD and Intel hardware; on NVIDIA, only the title runs above.

Checklist

yabanana and others added 5 commits October 17, 2026 18:48
…mers

A T# read from a nested child table stayed a runtime read when one of
its words also fed another instruction. That read goes through a V#
that exists only in the snapshot, so the dispatch read a garbage
address; lavapipe crashed in the shader. The nested branch now refuses
a child whose descriptor words are used by anything but equal handles,
as it already does for a V# with another consumer.
An S# read from a V# that a scalar record selects through a pointer
was refused, so recompiling failed with "GetSamplerResource dword 0 is
not a valid runtime value". The nested branch now takes sampler
columns: they get child tables per distinct V# like nested T#s, share
equal children, count against the nested key budget and the S# entry
budget, and fault on an unmapped, non-buffer or uncaptured parent. The
plan decoder accepts a nested sampler column; the plan encoding is
unchanged.
An S# or T# loaded through a pointer that a scalar record holds was
refused when a Phi merged the pointer's low word with constants, so
recompiling failed with "GetSamplerResource dword 0 is not a valid
runtime value". Such a column is now pointer-keyed: the snapshot
captures every record's pointer and the descriptor behind it, the map
holds boykopovar#7223's sorted pointer dictionary with descriptor records as its
codes, and the GPU looks up the actual 64-bit pointer. A pointer that
was not captured faults as outside the snapshot, and a null one as
unmapped.

The plan gains TableColumn::pointerKeyed and the snapshot gains
ImageTableColumnSnapshot::pointerKeyed, so the disk-cache format moves
to 38 and the image-table ABI to 4.
@github-actions github-actions Bot added build shader core/shader docs agc libSceAgc* needs rebase Conflicts with main, rebase needed labels Oct 18, 2026
@github-actions

github-actions Bot commented Oct 18, 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 1341 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/ResourcePlan.hpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 3247 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 4072 core/shader/recompiler/Recompiler.cpp
PR 4891 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 5315 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 5426 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/shader.md
PR 5431 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5480 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5499 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5500 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
docs/dev/debt/shader.md
PR 5609 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 5656 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 5782 docs/dev/debt/shader.md
PR 5799 docs/dev/debt/shader.md
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 5941 docs/dev/debt/shader.md
PR 6009 docs/dev/debt/shader.md
PR 6010 docs/dev/debt/shader.md
PR 6011 docs/dev/debt/shader.md
PR 6044 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6169 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6177 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
PR 6182 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6185 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6186 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6218 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 6235 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
docs/dev/debt/shader.md
PR 6249 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6280 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6326 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6373 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/ResourcePlan.hpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
docs/dev/debt/shader.md
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/execution/ImageTable.cpp
PR 6488 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/ImageTableAbi.hpp
core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/ControlFlowInfo.hpp
core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/ResourcePlan.hpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 6501 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6539 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6588 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6604 docs/dev/debt/shader.md
PR 6611 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
PR 6612 core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 6642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6679 core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 6701 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6718 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6741 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6743 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6747 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6759 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6762 core/shader/recompiler/Recompiler.cpp
PR 6763 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6788 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6799 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
docs/dev/debt/shader.md
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6815 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
docs/dev/debt/shader.md
PR 6820 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6850 docs/dev/debt/shader.md
PR 6856 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6858 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6868 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6879 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6887 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6905 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6914 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6924 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
docs/dev/debt/shader.md
PR 6937 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6941 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6946 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6947 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/ResourcePlan.hpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6948 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6959 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
docs/dev/debt/shader.md
PR 6963 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6965 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6970 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6973 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6977 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
docs/dev/debt/shader.md
PR 6982 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6987 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6989 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6995 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7002 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7036 docs/dev/debt/shader.md
PR 7042 docs/dev/debt/shader.md
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7074 docs/dev/debt/shader.md
PR 7082 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7085 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
PR 7275 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7307 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7310 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7332 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 7381 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
docs/dev/debt/shader.md
PR 7388 core/shader/recompiler/Optimization/src/ResourceTracker.cpp
docs/dev/debt/shader.md
PR 7390 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 7392 docs/dev/debt/shader.md
PR 7410 core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 7426 docs/dev/debt/shader.md
PR 7427 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7429 docs/dev/debt/shader.md
PR 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7464 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7484 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 7505 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7508 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7514 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7520 core/shader/recompiler/ShaderDiskCache.cpp
PR 7522 core/shader/recompiler/Recompiler.cpp
PR 7550 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 7560 core/shader/recompiler/Recompiler.cpp
PR 7570 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7610 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7615 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7622 core/shader/recompiler/ShaderDiskCache.cpp
PR 7623 docs/dev/debt/shader.md
PR 7627 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7647 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7666 core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 7677 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7684 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/libs/prx/libSceAgcDriver/tests/execution/ImageTable.cpp
core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/ControlFlowInfo.hpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
docs/dev/debt/shader.md
PR 7689 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 7694 core/shader/recompiler/Optimization/src/ResourceTracker.cpp
PR 7705 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7707 core/libs/prx/libSceAgcDriver/tests/execution/ImageTable.cpp
PR 7731 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 7800 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7803 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
docs/dev/debt/shader.md
PR 7816 core/shader/recompiler/Recompiler.cpp
PR 7817 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
PR 7829 docs/dev/debt/shader.md
PR 7831 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 7849 core/libs/prx/libSceAgcDriver/CMakeLists.txt

A T# loaded through a record pointer whose low word passes nested Phis
was refused when one Phi arm held a word of a Ballot lane mask, as in a
waterfall loop that reuses the pointer's SGPR for an s_and_saveexec or
v_cmp mask, so recompiling failed with "GetImageResource dword 0 is not
a valid runtime value". A pointer-keyed column now accepts such a word
as an arm that is not a record pointer, as it does for constants, and
still refuses any other foreign arm. At run time a pointer built from a
lane mask is not a captured pointer and faults as outside the snapshot.

A pointer-keyed column also accepts low-word reads, and a high-word read,
of the same record word under different keys: same table, record offset,
stride and addend. The GPU looks the pointer up by its value, so the key
does not matter there. The column takes its table and record offset from
the high-word read, which dominates the descriptor load.

A shader that planned before plans the same way, so the disk-cache
format stays at 38.
@blawesom

Copy link
Copy Markdown
Contributor

Rebase handoff — rebased onto current main (all six commits, authorship unchanged). The conflicts reconcile main's evolved table-record architecture (DenseScratchMap/tagged scratch records, the selected/selection columns) with this PR's nested/pointer-chained image-table support:

  • ResourceMaterializer.cpp: main's DenseScratchMap records kept, plus a second tagged map for the nested-table codes (the auto-merge had produced a non-compiling try_emplace — the nested/leaf namespaces now index separate maps, with the PR's per-invocation clearing semantics preserved across recursion); your recursive fill(self, …) shape and the growMap budget guards layered on top
  • ControlFlowInfo.hpp/ShaderDiskCache.cpp: additive fields throughout — selected/selection (main) + buffer (yours) + pointerKeyed (commit 5) — the TableColumn field count is now 19, encoder/decoder and the static assert agree
  • ResourceTracker.cpp: main's constant/addend offset logic with your parent-pointer word patch unioned (args 1–2 of a parent-pointer patch take the pointer's words)
  • test coverage: ImageTable's binding-role counts, the --nested*/--pointer-phi* flags, and the disk-cache reject cases all carried

One combination to review when you pick it up: when a selected column and a parent-pointer patch apply together, the args take the pointer's words while remaining args keep main's constant offsets — no test covers that intersection.

Verified on Linux (CachyOS, kernel 7.2.9, GCC 16.2.1, Ninja, Release, BUILD_TESTING=ON, AMD RX 9070 XT / RADV): builds; conventions pass; the image-table and disk-cache tests pass; full suite green apart from the known main regression (agc_driver_tessellation segfault — Mesa, #5444), not this PR.

To pick it up:

git fetch https://github.com/blawesom/AnyPS5 rebase/7395
git checkout <your branch>
git merge --ff-only FETCH_HEAD

Head is fdcf0d57.

The conflicts in the image table, disk cache and resource tracking files are resolved as in
blawesom's rebase of this branch onto main (fdcf0d5).

Co-authored-by: blawesom <9946064+blawesom@users.noreply.github.com>
@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@blawesom thank you for the rebase and for reconciling all eight files, including the auto-merge's non-compiling try_emplace. We took your resolution as a merge rather than a rebase, to keep the branch's history without a force-push: aed137c merges main 9caecfd into this branch, its tree is exactly your fdcf0d5's, and you're credited as co-author on it. On GitHub's runners (run) the ^agc tests pass 3 runs in a row on Linux (715, 0 failed); on Windows the only failures are the two replay tools that fail the same way on that runner for unrelated branches. Thanks also for flagging the case where a selected column and a parent-pointer patch apply together; we'll add a test for it.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@blawesom a follow-up on the case you flagged, where a selected column and a parent-pointer patch apply together. We said we'd add a test for it, but no shader can reach that case on this branch, so there's nothing for a test to cover:

  • A leaf column gets a parent pointer only when its heap V# doesn't validate as a runtime source (ResourceTracker.cpp:650-670 at aed137c). The other place that sets one, TryMakeIndirectColumn (825), returns before selected is worked out.
  • A column is selected only when its T#'s dwords validate (692). Those dwords are ReadConstBuffer reads through the heap V#, and validating such a read validates its V# handle and the handle's four dwords (SrtRuntimeValidator.cpp:111-142), the same check line 650 makes.
  • So a selected leaf never takes the nested branch, and a nested leaf is never selected. The materialization loop never takes its selected arguments and its parent-pointer arguments for the same plan.

A Linux run of the ^agc tests (716) with a temporary print logged 211 table-column plans, none with both. A nested-table shader whose keys the CPU evaluates gave a single selected column and no nested table at all. We've left the code as it is.

@ps54all

ps54all commented Oct 18, 2026

Copy link
Copy Markdown

@EagleBearerYS thanks for b91f050. Ratchet & Clank: Rift Apart (PPSA01474) data with it:

  • Base: a local test branch (main 82f0c9a plus open PRs, not clean main) that already carried this PR's earlier commits; b91f050 cherry-picked on top. Windows 11, RTX 3080 (NVIDIA 610.74), MinGW GCC 15.2.0, Release.
  • The stop on compute shader 0x116bb5f500 (LoadAddressU32@pc=0x00008fd4, 6-deep Phi) is gone.
  • The run now stops about 214 s in, still on a black window, on the next compute shader with the same error and a 7-deep Phi chain:
compute shader 0x116bb67000: shader 0x116bb67000 stage 0: ShaderRecompiler::Recompile: GetImageResource dword 0 is not a valid runtime value; chain: LoadAddressU32@pc=0x00009a30(GetAddressResource(Phi(Phi(Phi(Phi(Phi(Phi(Phi, Phi), Phi(Phi, ReadConstBuffer@pc=0x00001aa8)), Phi(Phi(Phi, Phi), CompositeExtractU32x4(Ballot, Immediate))), Phi(Phi(Phi(Phi, ReadConstBuffer@pc=0x00001aa8), Phi(Phi, Phi)), Phi(Phi(ReadConstBuffer@pc=0x00001aa8, Phi), ReadConstBuffer@pc=0x00001aa8(GetBufferResource, ShiftLeftLogical32)))), BitwiseAnd32(Phi(Phi(Phi(Phi, Phi), BitwiseAnd32(Phi, CompositeExtractU32x4)), Phi(Phi(Phi, BitwiseAnd32), Phi(Phi, Phi))), CompositeExtractU32x4(Ballot(Immediate), Immediate))), Phi(Phi(Phi(Phi(Phi(Phi, ReadConstBuffer@pc=0x00001aa8), Phi(Phi, Phi)), Phi(Phi(ReadConstBuffer@pc=0x00001aa8, Phi), ReadConstBuffer@pc=0x00001aa8(GetBufferResource, ShiftLeftLogical32))), Phi(Phi(Phi(Phi, Phi), Phi(Phi, ReadConstBuffer@pc=0x00001aa8)), CompositeExtractU32x4(Ballot(LogicalAnd), Immediate))), Phi(Phi(Phi(Phi(ReadConstBuffer@pc=0x00001aa8, Phi), ReadConstBuffer@pc=0x00001aa8(GetBufferResource, ShiftLeftLogical32)), Phi(Phi(Phi, ReadConstBuffer@pc=0x00001aa8), Phi(Phi, Immediate))), Phi(Phi(ReadConstBuffer@pc=0x00001aa8(GetBufferResource, ShiftLeftLogical32), Phi(ReadConstBuffer@pc=0x00001aa8, Immediate)), ReadConstBuffer@pc=0x00001aa8(GetBufferResource(ReadConst, ReadConst, ReadConst, ReadConst), ShiftLeftLogical32(ReadFirstLane, Immediate)))))), Phi(Phi(ReadConstBuffer@pc=0x00001aa8(GetBufferResource(ReadConst(GetSrtResource, Immediate), ReadConst(GetSrtResource, Immediate), ReadConst(GetSrtResource, Immediate), ReadConst(GetSrtResource, Immediate)), ShiftLeftLogical32(ReadFirstLane(SelectU32(LogicalAnd, IAdd32, Phi), Phi(LogicalAnd, LogicalAnd)), Immediate)), Phi(ReadConstBuffer@pc=0x00001aa8(GetBufferResource(ReadConst(GetSrtResource, Immediate), ReadConst(GetSrtResource, Immediate), ReadConst(GetSrtResource, Immediate), ReadConst(GetSrtResource, Immediate)), ShiftLeftLogical32(ReadFirstLane(SelectU32, Phi), Immediate)), ReadConstBuffer@pc=0x0000908c(GetBufferResource(LoadAddressU32@pc=0x0000900c(GetAddressResource, Immediate, Immediate, Immediate), LoadAddressU32@pc=0x0000900c(GetAddressResource, Immediate, Immediate, Immediate), LoadAddressU32@pc=0x0000900c(GetAddressResource, Immediate, Immediate, Immediate), LoadAddressU32@pc=0x0000900c(GetAddressResource, Immediate, Immediate, Immediate)), Immediate))), ReadConstBuffer@pc=0x00001aa8(GetBufferResource(ReadConst(GetSrtResource, Immediate), ReadConst(GetSrtResource, Immediate), ReadConst(GetSrtResource, Immediate), ReadConst(GetSrtResource, Immediate)), ShiftLeftLogical32(ReadFirstLane(SelectU32(LogicalAnd(INotEqual32, INotEqual32), IAdd32(Phi, Phi), Phi(Phi, Phi)), Phi(LogicalAnd(INotEqual32, INotEqual32), LogicalAnd(Phi, INotEqual32))), Immediate)))), Immediate, Immediate, Immediate)
  • The chain has Ballot / CompositeExtractU32x4(Ballot, Immediate) and ReadFirstLane(SelectU32 ...) arms feeding the Phis, plus a ReadConstBuffer@pc=0x908c whose buffer comes from LoadAddressU32@pc=0x900c(GetAddressResource).
  • One run each, before and after.

I can capture the shader (.req) if that helps.

AI assistance: the run and this summary were made with Claude Code.

@EagleBearerYS

EagleBearerYS commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor Author

@ps54all thanks for running it. On your branch, b91f050 clears the stop on compute shader 0x116bb5f500 (LoadAddressU32@pc=0x8fd4). Your next stop is the one Tamalero reached on Linux (#6191), in a sibling shader: both chains read the same record table at pc=0x1aa8, and every pc after it is 0x44 past theirs (the stop at 0x9a30 against 0x99ec, the second buffer's read at 0x908c against 0x9048, and its V# load at 0x900c against 0x8fc8). So one fix covers both.

No need for the .req, thanks. What's still open is which values those reads return at run time, and the shader's code can't show that. If you, or Tamalero on #6191, can run one diagnostic on that dispatch, it's the one we asked Tamalero for (comment): the dword the read at pc=0x908c returns, next to the high dwords of the records the pc=0x1aa8 reads return, with addresses masked. Whether those are always equal decides how #7395 takes the pointer's high word. The chain's other two parts, the AND with a Ballot word and the high word's Phi of record reads, don't need that data.

main's boykopovar#7296 took disk-cache format 38, so this branch's format change moves to 41.
@Capmare

Capmare commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Tested on real hardware, with no title run. Head aed137cae, full build with BUILD_TESTING=ON, Release, Rocky Linux 10.2, ANYPS5_GPU=<name> ANYPS5_REQUIRE_VULKAN=1, 735 graphics tests per GPU.

GPU (driver) Result
RTX 4070 SUPER (NVIDIA 615.71.09) all 735 pass, including agc_driver_nested_sampler_table and agc_driver_pointer_phi_sampler_table
RX 7600 (RADV, Mesa 25.2.7) both new sampler tests pass, 3 of 3 runs each; 1 unrelated failure, agc_pipeline_cache_file
UHD 770 (ANV, Mesa 25.2.7) both new sampler tests fail, 3 of 3 runs each
llvmpipe (Mesa 25.2.7) both new sampler tests pass, 3 of 3 runs each

The Intel failures, run as agc_driver_image_table_tests --nested-sampler and --pointer-phi-sampler:

nested sampler table: the sampler selected through record, pointer and V#: lane 0 is 0x3e800000, expected 0x427d0000
pointer sampler table: the S# read through the record's pointer: lane 0 is 0x427d0000, expected 0x3e800000

The two values are swapped between the two tests. The tests choose between a wrap and a clamp point sampler, so ANV may return the result of the other sampler. I have not found the cause. The existing agc_driver_nested_image_table* tests pass on all three GPUs.

Tested and written by Claude (AI assistant) on a contributor's machine.

@github-actions github-actions Bot added unblocks title Names a title ID on the Unblocks line and removed needs rebase Conflicts with main, rebase needed labels Oct 18, 2026
@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@Capmare thank you for running all four devices, and for repeating the ANV failures three times.

The tests can't confuse the two samplers. Every lane samples past the right edge, so a wrap sampler reads texel x (0x3e800000, 0.25, at lane 0) and a clamp sampler reads texel 63 (0x427d0000, 63.25).

Both failures come from the first check of the first dispatch. That check walks the workgroups in order and stops at the first one that differs, so your two lines say this:

  • --nested-sampler: workgroup 0 selects the wrap S# and matches. Workgroup 1 selects the clamp S# and reads the wrap result.
  • --pointer-phi-sampler: workgroup 0 selects the clamp S# and matches. Workgroup 1 selects the wrap S# and reads the clamp result.

So on ANV the second workgroup gets the first workgroup's sampler. Workgroups 2 and 3 never ran through the check.

The agc_driver_nested_image_table* tests go through the same records and keys, and they pass there. So the difference is in how the sampler is picked. Each workgroup indexes a VK_DESCRIPTOR_TYPE_SAMPLER array with its own index, decorated NonUniform, with shaderSampledImageArrayNonUniformIndexing enabled. We haven't found yet whether the fault is in our SPIR-V or in ANV, so the tests stay as they are and we're keeping this open.

If you have time for one more run: the same two tests with the Khronos validation layer (VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, if it's installed) would show whether it reports anything for those dispatches.

@Capmare

Capmare commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

@EagleBearerYS I ran the two tests with the Khronos validation layer, and I found a trigger on ANV that does not involve our SPIR-V. Details below, including what I could not prove.

Summary

  • The validation layer is silent, and spirv-val accepts the module.
  • The failing shader is valid and selects the sampler correctly. ANV returns the sampler of workgroup 0 for workgroup 1.
  • I reproduced the same behaviour in a standalone Vulkan program with no AnyPS5 code. The trigger is a compute stage with requiredSubgroupSize = 32, local_size_x = 32 and a sampler array with more than 2 elements, indexed per workgroup.
  • APS5_SPANNED_WAVE=1 makes both tests pass on ANV. It changes no other registered test result on the Intel GPU.
  • I do not know why ANV does this. I did not look at the Mesa source.

Setup

  • Intel RPL-S (the iGPU of an i7-14700K), Mesa 25.2.7 (ANV), Vulkan 1.4.318. The same PC also has an RTX 4070 SUPER (driver 615.71.09), an RX 7600 (RADV) and llvmpipe (LLVM 21.1.8).
  • Local build of this PR head (aed137cae) merged with origin/main at c4c2b3aa7, plus some local patches (see "Caveats").
  • Command: ANYPS5_GPU=Intel ANYPS5_NO_SHADER_CACHE=1 ./agc_driver_image_table_tests --nested-sampler (and --pointer-phi-sampler), from the source directory. I ran the binary directly and did not use ctest.

Results of the two tests

Device --nested-sampler --pointer-phi-sampler
Intel RPL-S (ANV) fail fail
AMD RX 7600 (RADV) pass pass
NVIDIA RTX 4070 SUPER pass pass
llvmpipe pass pass

The two Intel failures are the lines from your comment:

  • --nested-sampler: group 1 lane 0 is 0x3e800000, expected 0x427d0000 (the wrap result instead of clamp).
  • --pointer-phi-sampler: lane 0 is 0x427d0000, expected 0x3e800000 (the clamp result instead of wrap).

I added the group number to the failure message locally. It confirms your reading: the first mismatch is workgroup 1, and workgroup 1 gets the sampler of workgroup 0. The failure does not depend on the validation layer, on APS5_NO_GPL, on APS5_NO_PROC_TABLE, on APS5_NO_TIMELINE, on APS5_NO_HOST_IMPORT, or on APS5_NO_SAMPLER_CACHE. I ran each of these on Intel, and the result did not change. All my runs used ANYPS5_NO_SHADER_CACHE=1; I did not test with the shader cache on.

Validation layer

VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation loads (VK_LOADER_DEBUG=layer shows Insert instance layer "VK_LAYER_KHRONOS_validation"). It prints no message for either test on any device. spirv-val --target-env vulkan1.3 accepts the captured compute module (5,656 words). I built spirv-val and spirv-dis from the repo's SPIRV-Tools source for this.

The SPIR-V of the failing dispatch

I captured the pipeline with APS5_DUMP_COMPUTE_PIPELINES. Facts about the module:

  • LocalSize 32 1 1. The guest wave is 64, and the module has two OpImageSampleExplicitLod. I read this as two guest lanes per host invocation, but I did not verify that in the emitter.
  • The sampler is loaded with OpLoad from table_samplers[element] (a runtime array of OpTypeSampler), then OpSampledImage combines it with the image. The element comes from table_map. There is no NonUniform decoration, because the emitter treats the index as uniform per workgroup when the workgroup is one subgroup.

What I tried on the real test, with the real buffers

I added a local diagnostic hook that replaces the compute module with an edited file, so the driver, the buffers and the descriptors stay real. Results on the Intel GPU:

Change Result
unmodified module same failure
sampler element forced to 0 group 1 gets wrap (as in the failure)
sampler element forced to 4 group 0 gets clamp (so the index is honoured)
sampler element computed from gl_WorkGroupID only, with the expected pattern wrap/clamp/clamp/wrap group 1 still gets wrap
NonUniform forced on the index (emitter switch) no change
OpGroupNonUniformBroadcastFirst on the index (emitter switch) no change

The row with the workgroup id is the important one. It means the key, the record and the pointer chain are not the cause.

Standalone reproduction (no AnyPS5 code)

A C program (about 300 lines) and one GLSL compute shader. It makes two samplers (REPEAT and CLAMP_TO_EDGE) and a 64x1 R32_SFLOAT texture with texel i = i. It dispatches 4 workgroups of 32 threads. Workgroup g samples at u = 1.2578125 through sampler g & 1. Wrap gives texel 16, clamp gives texel 63. Expected output per workgroup: 16 63 16 63.

On the Intel GPU:

Pipeline stage setup Sampler array Result
default subgroup size [2], [8], [1024], runtime, with and without nonuniformEXT, with a branch, with 2 samples per invocation, with a two-level index all pass
requiredSubgroupSize = 32 [2] fixed pass
requiredSubgroupSize = 32 [8], [1024] or runtime (8 to 1024 descriptors, partially bound), index from a buffer, from a subgroup broadcast, with a branch, with 2 samples all fail: wg0=16 wg1=16 wg2=16 wg3=16
requiredSubgroupSize = 32 + REQUIRE_FULL_SUBGROUPS same same result as above
requiredSubgroupSize = 8 or 16 runtime, index from a buffer pass

NVIDIA, RADV and llvmpipe pass every variant. The validation layer reports nothing for the program.

The trigger is therefore: required subgroup size 32, a workgroup of exactly 32 threads (one SIMD32 thread), and a sampler array with more than 2 elements indexed per workgroup. The driver creates our compute stages with requiredSubgroupSize = 32 for wave32 programs, which is why the tests hit it.

The compute shader of the reproduction
#version 450
#extension GL_EXT_nonuniform_qualifier : require
#extension GL_KHR_shader_subgroup_ballot : require
layout(local_size_x = 32) in;
layout(set = 0, binding = 0) uniform texture2D tex;
layout(set = 0, binding = 1) uniform sampler samplers[];   // compiled with -DRUNTIME_ARRAY
layout(set = 0, binding = 2) readonly buffer Indices { uint indices[]; };
layout(set = 0, binding = 3) writeonly buffer Results { float results[]; };
void main() {
    uint index = indices[gl_WorkGroupID.x];
    float value = textureLod(sampler2D(tex, samplers[index]), vec2(1.2578125, 0.5), 0.0).x;
    results[2u * gl_GlobalInvocationID.x] = value;
}

Workarounds I tested on the Intel GPU

  1. APS5_SPANNED_WAVE=1: works. Both tests pass, 3 runs each. The program then runs as LocalSize 64 with two 32-lane subgroups, so a workgroup is no longer one SIMD32 thread.
  2. Not requiring the subgroup size (diagnostic switch in computeSubgroupSize): not enough. The sampler check then passes, but both tests fail at a later check: a captured pointer selected through Phi faulted (--nested-sampler) and a captured pointer selected through Phi changed the result (--pointer-phi-sampler). The wave32 logic needs a subgroup of 32 lanes.
  3. A fixed-size array instead of a runtime array: not a fix. Arrays of 8 and 1024 elements fail the same way.

Effect of APS5_SPANNED_WAVE=1 on the other registered tests

I ran all 235 registered libSceAgcDriver tests directly (121 test executables, with the environment, timeout and working directory from CMakeLists.txt), on each GPU:

Run pass skip fail
Intel, default 217 3 15
Intel, APS5_SPANNED_WAVE=1 219 3 13
RTX 4070 SUPER, default 220 2 13
RX 7600, default 218 3 14

Between the two Intel runs only agc_driver_nested_sampler_table and agc_driver_pointer_phi_sampler_table change, from fail to pass. No test goes from pass to fail.

Tests that fail on every device

These 13 tests fail on Intel, NVIDIA and RADV alike, with and without the spanned-wave switch: agc_buffer_pool, agc_driver_stencil_replay_no_gpl, agc_host_import_second_mapping, agc_import_settle_deferred, agc_import_settle_deferred_other_family, agc_import_settle_earlier_import, agc_import_settle_first_use, agc_import_settle_retire, agc_import_settle_storage_image, agc_shader_bindless, agc_shader_frontend_pair, agc_storage_cpu_snapshot, agc_vertex_barrier. RADV also fails agc_pipeline_cache_file. I did not investigate them. They may come from my merge of origin/main, from my local patches, or they may exist upstream. I did not run them on a clean checkout of the PR head.

Caveats

  • This is not a clean PR head. My tree has local patches for a different goal (running a game). They do not touch the sampler-table code, but the local build has a diagnostic fallback in the resource tracker that replaces a sampler reached through a runtime pointer chain. With it, --nested-sampler fails on every device with a V# with another buffer consumer was not refused by resource planning. I turned it off with APS5_NO_DESCRIPTOR_SUBSTITUTE=1 for all runs above. A clean PR head does not have this fallback.
  • I tested only the Intel iGPU of this PC. I did not test other Intel GPUs or other Mesa versions.
  • I did not read the Mesa source. I cannot say which ANV code path causes the behaviour.
  • The test comparison covers the registered unit tests. I did not test game workloads with the spanned-wave mode.

Suggestion

I did not change the tests or the driver. If you want a driver change, enabling the spanned-wave mode on ANV is the smallest one I found. It changes how every single-wave wave64 compute shader runs on that driver, so I leave the decision to you. Separately, the standalone program is a clean bug report for Mesa. I can attach the source and a short write-up, or file the Mesa report myself if you prefer.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@Capmare thank you, this settles it, and the standalone program is the strongest part. With no AnyPS5 code, ANV gives workgroup 1 workgroup 0's sampler whenever the stage has requiredSubgroupSize = 32 and the sampler array has more than 2 elements. A fixed [2] array passes, and so does the default subgroup size. The validation layer and spirv-val report nothing. So it's an ANV issue, not our SPIR-V.

A correction to my last comment: the module carries no NonUniform on that index, as you found. The emitter treats the index as uniform when the workgroup is one subgroup, and your forced-NonUniform run shows the decoration makes no difference.

The two tests stay as they are, since they're what caught it. APS5_SPANNED_WAVE=1 passing on ANV is a useful lead for a workaround on our side. We aren't changing anything for it yet.

If you want to report it, your program looks ready to post as a Mesa issue as it stands (https://gitlab.freedesktop.org/mesa/mesa/-/issues). That's entirely up to you.

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

Capmare commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

@EagleBearerYS thank you for the careful review and for the clear questions. They pointed me to the validation layer and to the exact failing dispatch, which is how I found the cause.

I posted the standalone program as a Mesa issue: https://gitlab.freedesktop.org/mesa/mesa/-/work_items/16622

I agree with leaving the two tests as they are and with not changing the driver yet. If Mesa answers, I will post the result here.

Resolves the image-table record columns against boykopovar#6232's pointer chains: TableColumn keeps buffer, pointerKeyed and pointerHops (20 fields, all compared and encoded), the materializer records a buffer or pointer-keyed column's record pointer before following hops, and a chain whose record read is merged across lanes or feeds a buffer column is refused, as main refuses it, since neither side plans that combination. The image-table tests keep this branch's nested and pointer-keyed cases and boykopovar#6232's pointer-chain case.
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 18, 2026
@fdfawef324

fdfawef324 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

#7684 adds conservative table-key ranges and sentinel handling, with 3 CPU suites and a 7-case regression batch (5 real Vulkan, 2 CPU/mock) passing on its exact head. The overlap check found a textual conflict with this PR's capture refactor.

I propose #7684 first, then this PR can retain TableColumn::allOnesKey, its plan-cache encoder/decoder field, and the heap/address sentinel fault predicate when moving capture into ReadColumn. The deliberately unbounded merged-pointer branch must also stay unbounded. The topics have no runtime dependency on one another.

I left #7684 as a draft pending agreement on ordering. If the #7223 → #7395 sequence should go first, I can rebase the bounded change afterward and rerun the combined fixtures.

AI-assisted: yes

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@fdfawef324 thank you for checking the overlap and listing what to keep. #7684 first works for us. On a trial merge of this PR's head (59b319a) over #7684's df6aa65, the conflicts are small: six hunks in ResourceMaterializer.cpp, ResourceTracker.cpp, ShaderDiskCache.cpp and the shader debt file. They compose as follows:

  • TableColumn keeps both new fields, allOnesKey and this PR's pointerKeyed, and the plan encoder and decoder write and read both.
  • In the tracker, a column whose record pointer is merged through a Phi stays unbounded (maxKey = 0xffffffff), and every other column goes through boundTableKey.
  • In ReadColumn, the key count keeps this PR's limit (the nested tables' parent budget), and outside takes your heap/address sentinel fault predicate.

If #7684 lands first, we'll merge main here with those resolutions and rerun this PR's table tests on both runners.

@fdfawef324

Copy link
Copy Markdown
Contributor

Agreed: #7684 first. I have marked it ready for review and linked your composition plan in its body. The published tests cover #7684 alone; the combined nested-table tests you describe remain the follow-up after merging main here.

AI-assisted: yes

@j2m304

j2m304 commented Oct 18, 2026

Copy link
Copy Markdown

Tested #7395 head 59b319a (including #7223) offline against a captured Wolverine compute request, with SPIRV-Tools enabled. Needed a local iterative Tarjan walk to avoid the previously observed stack overflow; used APS5_FORCE_STRUCTURIZER_FALLBACK=1 and disabled shader caching. This is a combined experiment, not a stock-PR title test.

The request loads, but still stops in resource analysis at GetSamplerResource dword 0 is not a valid runtime value, with nested Phi / ReadConst inputs. Adding #6417's production invariant-word resolution and a local repeated-Phi folding loop following #6332's description did not resolve it. Seven CPU and seven nested-image/sampler/pointer GPU checks passed before that follow-up, and four CPU plus seven GPU checks passed afterward.

A diagnostic traversal of the failing word's Phi edges finds 129 Phi nodes and 43 ReadConst nodes referring to 30 distinct runtime-read slots, plus arithmetic and lane-operation leaves. This is only the Phi surface, not a full dependency graph; it does not prove that the live sampler path combines different values.

Attached synthetic DescriptorMerge.cpp isolates the validator boundary: equivalent-slot Phi inputs are accepted, while distinct-slot Phi inputs are individually valid and rejected after merging, at depths 1/2/9. All six controls pass. This is a unit-level capability reproducer, not a minimized full resource-tracker/title reproduction and not evidence that the conservative rejection should be removed. No title code, capture or dump is included.

Is a synthetic full-tracker fixture retaining the incoming control-flow correlation the most useful next step for this shape? I have kept the title capture and memory dump local.

AI-assisted: yes. No compatibility or performance claim.
sampler-merge-reproducer.zip

@klosixgaming-arch

Copy link
Copy Markdown
Contributor

Little Nightmares Enhanced Edition (PPSA10737, version 01.004.000, on Windows 11 / RTX 5090 with #7413 cc6901685 to get past an earlier hoisted table call) stops 2 min 46 s in on compute shader 0x2010fa0000 with the same BitwiseAnd32 Ballot lane-mask shape noted in this PR's description:

[dispatch] failed: compute shader 0x2010fa0000, stage 0: ShaderRecompiler::Recompile: GetImageResource dword 0 is not a valid runtime value (value=LoadAddressU32[id=13325, type=U32, flags=0x4, arg0=LoadAddressU32@pc=0x3384(arg0=GetAddressResource@pc=0x3384(arg0=Phi(arg0=Phi(arg0=Phi(arg0=BitwiseAnd32@pc=0x1600(arg0=CompositeExtractU32x4@pc=0x15d4(arg0=Ballot@pc=0x15d4(arg0=IntegerNotEqual32@pc=0x15cc(...)), arg1=imm=0x0), arg1=BitwiseNot32@pc=0x15fc(...)), arg1=Phi(arg0=imm=0x0, arg1=ReadConstBuffer@pc=0x15ec(arg0=GetBufferResource@pc=0x15ec(arg0=UserData7), arg1=ShiftLeftLogical32@pc=0x15ec(arg0=ReadFirstLane@pc=0x15dc(...), arg1=imm=0x4)), arg2=Phi(...)), arg2=Phi(...)), arg1=...), arg1=...), arg1=ReadConstBuffer@pc=0x15ec(arg0=GetBufferResource@pc=0x15ec(arg0=UserData7), arg1=ShiftLeftLogical32@pc=0x15ec(arg0=ReadFirstLane@pc=0x15dc(...), arg1=imm=0x4))), arg1=imm=0x0), arg1=imm=0x0] (13 total refusals in this shader))

In 0x2010fa0000, GetAddressResource@pc=0x3384 has an unmerged high dword (arg1 = ReadConstBuffer@pc=0x15ec at +4) while the low dword (arg0) goes through nested Phi nodes that merge the matching ReadConstBuffer@pc=0x15ec at +0 with imm=0x0 and BitwiseAnd32@pc=0x1600(CompositeExtractU32x4@pc=0x15d4(Ballot@pc=0x15d4(...), imm=0x0), BitwiseNot32@pc=0x15fc(...)) because the low-dword SGPR is reused for the loop's lane mask.

With a local change in TryMakeIndirectColumn that unwraps Phi arms to the single equivalent ReadConstBuffer origin while ignoring imm=0x0 and Ballot-derived lane-mask expressions, 0x2010fa0000 compiles and creates its pipeline (414421 SPIR-V words in 26173 ms), and the title advances to its next stop on compute shader 0x200c340000 (computed/data-dependent s_swappc_b64 call target at program counter 0x000008b4 is not statically resolvable).

AI-assisted: yes. Written with AI assistance and reviewed by me.

@fdfawef324

Copy link
Copy Markdown
Contributor

Following the agreed #7684-first order, #6488 is now rebased and verified at a48c5a7c7321 on main 676d423968a1. Its lane-loaded image handle keeps the numeric key in argument 0 and an independent U32 load predicate in argument 1; an inactive descriptor load selects NullCode before a table fault, while active real UINT_MAX keys keep the unsupported-OOB fault.

At your reviewed 59b319a77919 head, pointer/nested columns use handle arguments 1/2 for parent-pointer words and move ordinary access into LookupTableWord/LookupTablePointer. Composition must retain the laneKey branch before interpreting those pointer operands, plus current SharedValues/Seal storage, sharedMode memo/hash identity and both complete metadata layouts. The features are separate; the operand convention is a concrete shared implementation point.

I propose #7684, then #6488, then your #7395 rebase with both conventions and the masked-invalid-table controls retained. Only the #7684-first part is already agreed; this extension is a proposal. I am keeping #6488 in draft while that order is coordinated. Current #6488 CPU3 and Vulkan3 pass, including the new controls, but no combined #7395 runtime result is claimed. Let me know if the opposite order for these two is preferable.

AI-assisted: yes (Codex)

@Capmare

Capmare commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Marvel's Wolverine (PPSA03671) on this PR's head 59b319a, as asked on #6191. Rocky Linux 10.2, Release build of the head as it is (no local patches), NVIDIA GeForce RTX 4070 SUPER (driver 615.71.09), cold shader cache, one run of about 9 minutes, then I stopped it.

  • The sampler stop is gone. The log has no refusal of any kind (GetSamplerResource or GetImageResource), no [dispatch] failed and no presentation failed.
  • The title then makes no progress to a frame. No frame was presented in the 9 minutes: the harness dumps the first presented frame, and none was dumped. On my patched tree the first frame is dumped within about 20 seconds.
  • What it does instead: 284,392 StorageTexture creations in that time, on 371 distinct addresses. The same surfaces are created thousands of times (0x115df30000 7,681 times, 0x1163060000 5,790, 0x112c7b0000 5,787). At about 4 minutes the device memory was at 11.9 of 12 GB, two threads were at 100% CPU, and the process used 18 GB of RAM.

So the next stop of the title on this head is not a refusal but this build storm. I think the cause is the one-image-per-table-element build of surfaces that appear at several extents (dynamic-resolution variants of one surface). On my local tree, substituting one representative per family of variants and building table entries lazily avoids it, and the same title then reaches the 3D scene and the Initial Setup menu. That is a separate topic from this PR. I can open it as its own PR if you want it.

@Capmare

Capmare commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Update on the ANV failure of the two sampler tests: the Mesa developers answered on Mesa issue 16622. It is fixed by Mesa merge request !37394 ("nir/brw: Fix texture/sampler accesses with EU fusion on Gfx12.X HW"). On Gfx12 two fused threads run in lock step and cannot have different sampler handles, which matches workgroup 1 getting workgroup 0's sampler. The fix is in Mesa 25.3.0 and later, and not in the 25.2 branch.

I checked it on the same Intel RPL-S with the freedesktop Flatpak runtimes' Mesa, using the standalone program from the issue. Mesa 25.2.7 (the distribution package) gives 18 failing runs. Mesa 26.2.2 and 26.2.4 give 0.

I did not run agc_driver_nested_sampler_table and agc_driver_pointer_phi_sampler_table themselves on the newer Mesa. So the description can say that the two tests fail on ANV with Mesa 25.2.x and that Mesa 25.3 and later has the fix. The tests stay as they are.

This branch has not been deployed

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

Labels

agc libSceAgc* build docs needs rebase Conflicts with main, rebase needed shader core/shader unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants