Skip to content

fix(shader): follow a conditional pointer chase through a V# behind a pointer-keyed record pointer - #9633

Open
EagleBearerYS wants to merge 26 commits into
boykopovar:mainfrom
EagleBearerYS:fix/shader-pointer-chase-closure
Open

EagleBearerYS wants to merge 26 commits into
boykopovar:mainfrom
EagleBearerYS:fix/shader-pointer-chase-closure

Conversation

@EagleBearerYS

@EagleBearerYS EagleBearerYS commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

What

On Ratchet & Clank: Rift Apart (PPSA01474), with #8939, a compute shader stops with GetImageResource dword 0 is not a valid runtime value at LoadAddressU32@pc=0x10b84. #8939 itself got it past its earlier stop at 0x9a30. The sources are a retail run on #8939 (comment), and two IR readings of the shader (1, 2).

The descriptor pointer is a Phi of the record's pointer R and up to two chased pointers. The shader conditionally replaces P by the 64-bit pointer read at offset 0 of a buffer whose V# sits at P+0x60, up to twice. It then loads the T# at the final P+0. So the pointer is R, H(R) or H(H(R)).

The change:

  • Tracker: a chase arm is a buffer read at dwords 0/1 of one handle whose V# is four words loaded through GetAddressResource(P) at an immediate offset, where P resolves to R or to another chase arm.
  • Materializer: for each record's pointer, the column also publishes H(R), H(H(R)) and so on up to its depth, breadth-first, as pointer-keyed entries, each with its own T#.
  • The disk cache format goes 46 → 47.
  • A debt entry for the depth cap and refused loops.

Overlap: checked with git merge-tree against every open PR touching these files, on main da54c65, and rechecked at 7928543 (with main 3ca07d0) against the PRs below: the conflicts are the same. #9508's other conflict there, in GuestMemory.cpp, is with main itself.

Tested

  • GitHub's Linux runner (run): agc_driver_pointer_chase_table fails ×2 with fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457's code, and passes ×5 with this change. agc_driver_pointer_hop_table and agc_shader_disk_cache pass. GitHub's Windows runner builds it, and its Vulkan tests skip there.
  • 2c1a650 on GitHub's runners (run, against 6a72841): agc_driver_pointer_chase_words_table fails 3 of 3 without the change ("pointer table with two chase links in one V#: the T# behind one of the two links faulted") and passes 10 of 10 with it. With agc_driver_pointer_chase_table, _hop_table, _phi_high_table, _vsharp_fixed_table and agc_driver_nested_image_table, it passes 10 of 10 on Linux; on Windows they build and pass, with the Vulkan tests skipping. libc's precise_wait failed on both arms of that run.
    • Validation, 6a72841 against 2c1a650 on one GitHub Linux runner (run), every test named: the layer loads in both runs, both log only VUID-VkDeviceCreateInfo-pNext-pNext, and the commit adds no VUID- message.
  • 7928543, fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457's head 2a5db7a and then main 3ca07d0 merged in (both clean; this PR's own diff is unchanged), on GitHub's runners (run): the 18 nested_/pointer_ table tests, agc_driver_image_table_vouched_lookups, agc_driver_image_table_vouch_queued_draws and the disk-cache tests pass 10 of 10 on Linux, none skipped. On Windows they build and pass, with the Vulkan tests skipping.
    • The suites: libkernel passes on Linux and libc on Windows. On Linux libc's one failure, precise_wait, failed with main's implementation too. On Windows libkernel's one failure is guest_raise_in_system_call, a 30 s timeout in a libkernel test this PR doesn't touch.
    • Validation layer, main against this head, in the same run: one GitHub Linux runner, Ubuntu 24.04.5, llvmpipe (Mesa 25.2.8, LLVM 20.1.2), with ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_LAYER_KHRONOS_validation and ctest -V. main's arm runs the 2 image-table tests and the disk-cache tests it has, and this head's arm runs all of them. The layer loads in both runs, with no "was not found" line. Both log only VUID-VkDeviceCreateInfo-pNext-pNext, and this head adds no VUID- message.
  • Linux, Mesa llvmpipe:
    • The test shader: an RDNA2 compute shader with the title's shape, assembled with llvm-mc -arch=amdgcn -mcpu=gfx1030. A record pointer is keyed by a first-lane read, then two conditional chase steps each load a V# at P+0x60 and a pointer at offset 0, then a T# at the final P+0.
    • Cases:
      • all 16 lane classes read the T# of the node they reach;
      • a moved link is followed without a recompile;
      • a link outside the snapshot faults "outside the snapshot" with the output untouched;
      • a chase in a loop and a five-step chase are refused. Disabling each rule turns its case red.
    • Red: with the tracker change reverted, the shader refuses the way the title does: GetImageResource dword 0 is not a valid runtime value; chain: LoadAddressU32@pc=0x000000c0(GetAddressResource(Phi(Phi(ReadConstBuffer….
    • Wider set: the 41 image-table, pointer, nested-table and disk-cache tests pass.
    • CI's Linux job mirrored: only the build machine's IPv6 tests and one timing test that passes alone fail. ^agc and ^shader show the same failure set on fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457's head and on this change.
  • Validation layer, fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457 against this PR (CONTRIBUTING's AGC rule), on one GitHub Linux runner (run). Both runs used VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, ANYPS5_NO_SHADER_CACHE=1, ANYPS5_REQUIRE_VULKAN=1 and ctest -V, with VK_LOADER_DEBUG=layer to show the layer loading. The device was llvmpipe (LLVM 20.1.2) on Mesa 25.2.8, Ubuntu 24.04.5, Khronos validation layers 1.3.275.
  • jotitadev's run on an RTX 3070 and lavapipe (review): Ubuntu 26.04, GCC 15.2, Release, 6a72841 with its stack merged into main da54c65.
    • agc_driver_pointer_chase_table passes 3 of 3 on both devices, including the moved link, the link outside the snapshot, and the refused loop and five-step chase.
    • With this PR's ResourceTracker.cpp change reverse-applied, it fails on both the way the title does: GetImageResource dword 0 is not a valid runtime value; chain: LoadAddressU32@pc=0x000000c0(GetAddressResource(Phi(Phi(ReadConstBuffer….
    • ^agc, without the flip, display DCC and lds_atomics64 tests: 875 of 878 on the 3070 and 877 of 878 on lavapipe. The failures are the same as on main on that machine: the three sample_order tests on the 3070, and multisample_retirement on lavapipe.
    • As jotitadev notes, that covers the chase at a subgroup size of 32 on a real GPU as well as llvmpipe's 8.
  • check_conventions.py passes, with no comment lines added.
  • Ratchet & Clank: Rift Apart (PPSA01474), by @Tamalero (comment): Linux (CachyOS), GCC 16.2.1, Release, RTX 4090 (NVIDIA 615.78.08), content 01.005.003. Both builds used the same relinked executable and a cold shader cache, with no input.
    • With this PR's parent c00cb0f (the same stack without the chase), it stops after 321 s at LoadAddressU32@pc=0x10b84, the stop from fix(shader): take lane-mask arms and a merged high word in a pointer-keyed record pointer #8939.
    • With this PR (6a72841) there's no refusal at 0x10b84. It stops later, after 351 s, in another compute shader at LoadAddressU32@pc=0x13bc0: a T# load whose pointer goes through a third chased read.
    • Replaying a sibling shader's prepare request offline with agc_shader_replay, the parent refuses at the same site (pc=0xf898). This PR passes it and refuses at a later T# load (pc=0x128d4), so the chase is followed on the title's own shader.
    • The third read (Tamalero's trace), in both shaders: the pointer's high word is read at its own immediate, 0x6c against a low word at 0x54, from one s_buffer_load_dwordx16. ChaseHighRead takes a high word only at readImmediate + 4, so the walk refuses there. That step needs a follow-up. In Tamalero's scratch build with only that check relaxed, both shaders then stop on sampler resource limit exceeded (MaxSamplers = 32).

Checklist

yabanana and others added 22 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.

(cherry picked from commit 0df7d32)
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 43 and the image-table ABI to 4.
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 43.
…ed record pointer

A T# loaded through a record pointer was still refused with "GetImageResource
dword 0 is not a valid runtime value" when the pointer's low word passed
ANDs with lane masks, as a waterfall loop that copies EXEC into the
pointer's SGPR builds them, or when its high word was a Phi of the
record's high word read under several keys.

For a pointer-keyed column, the walk of the low word now takes an AND
with a lane-mask operand (a Ballot word, or an AND or Phi of only Ballot
words and immediates) and keeps walking its other operand, so its
record reads must still match the column and anything else still
refuses. The high word is walked the same way: its record reads must
be the record word at the low word's offset plus 4. A high word that is
one read still gives the column its table and record offset, as before.
Otherwise the table comes from a record read whose V# words are defined
before the descriptor load, and the record offset from the high word,
which the GPU does not read for a pointer-keyed column.

A high-word arm read from another buffer, such as one whose V# is
loaded through a pointer, stays refused. A shader that planned before
plans the same way, so the disk-cache format does not change.
… a pointer-keyed record pointer

A T# loaded through a record pointer was still refused with "GetImageResource
dword 0 is not a valid runtime value" when a Phi of the pointer's low word
merged the record read, a zero, and the waterfall's lane mask with the
handled lane's bit cleared, BitwiseAnd32(CompositeExtractU32x4(Ballot(...), 0),
BitwiseNot32(ShiftLeftLogical32(1, index))), as s_ff1, s_lshl and s_not with
s_and, or s_andn2, build it in the SGPR the pointer reuses. The walk took only
Ballot words and ANDs of them as lane masks, so it walked into the NOT and
refused.

A lane-mask operand is now a Ballot word or the loop's lane bit, a shift of 1
by an index from FindILsb32, FindUMsb32 or ReadFirstLane (through the AND,
add, subtract and select that s_ff1_i32_b64, s_bitset0 and the shift's own
mask put around it), or an AND, OR, NOT or Phi of only those and immediates.
An arm of the pointer that is entirely such an operand is skipped. An AND
with a record read in it still has the read matched against the column, and
anything else still refuses.

agc_driver_pointer_phi_lane_not_table builds that shape in a wave64
waterfall: the low word's Phis merge the record read at +0, a zero and the
lane mask without the handled lane's bit, cleared by s_not and s_and and by
s_andn2, and the high word is one read at +4. Each lane samples the T# of its
record, and every masked word faults outside the snapshot.
agc_driver_pointer_vsharp_select_table reads a record keyed by
ReadFirstLane(SelectU32(LogicalAnd, IAdd32, Phi)), a pointer from it, a V#
through the pointer and the T# through the V#; it passes without this change
and keeps that chain checked, including a record pointer moved to the other
V# without a recompile.

Shaders that planned before plan the same way, and a refusal is never cached,
so the disk-cache format does not change.
…keyed records

A pointer-keyed record pointer now also takes lane-mask ANDs, the
waterfall loop's lane bit and its OR and NOT forms, and a high word
merged from the record's next word. A high word read from a second buffer
stays refused.
The low and high words of a pointer-keyed record pointer were walked
separately, so a high-word arm on a path whose low word is a lane mask
refused the plan even though that pair is no pointer. Ratchet & Clank:
Rift Apart (PPSA01474) stops this way at LoadAddressU32@pc=0x9a30: one
high-word arm reads a second buffer, and on that edge the low word is a
lane mask, the waterfall loop reusing the SGPR pair.

Walk the two words together. Where both are Phis of one block, each
low-word edge takes the high word of the same predecessor. An edge whose
low word is a constant or a lane mask is skipped with its high word. A
record low word still needs every high word it can meet to be a constant,
a lane mask or the same record column at offset + 4, so a second-buffer
high word paired with the record word stays refused.

agc_driver_pointer_phi_high_second_lane_table adds the lane-mask low word
to the second-buffer arm: it plans, each group samples the T# its record
selects, and that arm faults outside the snapshot.
…nter

The two words of the pointer are now walked edge by edge, and an edge
whose low word is a constant or a lane mask is skipped with its high
word. A high word read from a second buffer stays refused on an edge
whose low word is the record word.
Brings boykopovar#8991 (match image-table T# halves offset before and after
readfirstlane).

ResourceTracker.cpp: main's FirstLaneOffset, ParseFirstLaneOffset and
SameFirstLaneOffset go before TryMakeTableColumn, which keeps this
branch's signature with the buffer parameter. Main's first-lane base
check in the column loop merged on its own.
…ane-masks

Brings boykopovar#7395's merge of main bd4d801, with boykopovar#8991. It merged without conflicts.
TryMakeIndirectColumn refused every pointer hop once the record's
pointer words were merged, so a T# read through a pointer loaded from a
pointer-keyed record pointer stayed unplanned (Gran Turismo 7's World
Map compute shaders, issue boykopovar#6191). Only buffer handles still refuse hops:
the materializer keys the records by the innermost pointer before it
follows the hops, and the plan's parent pointer is that innermost one.
…one-key child

From hugodearaujo's change on boykopovar#8939 (Marvel's Spider-Man 2, PPSA03016).
Spider-Man 2's shape: a record keyed by a first-lane read, its dwords
2-3 a pointer, a V# loaded at that pointer plus 0xa0, and the T# read at
offset 0 or 0x40 of that V#'s buffer with no second key. Two records
point at V#s of different T#s; a moved record pointer and a moved V#
must change the result without recompiling.
… pointer-keyed record pointer

A T# read at a pointer that a shader chases conditionally from a
pointer-keyed record pointer, loading a V# at a fixed offset of the
pointer and reading the next pointer from that buffer, stayed unplanned:
the merged pointer's chase arms read another buffer at another offset
than the record word, so PointerRecordRead refused them (Ratchet & Clank:
Rift Apart stops at compute LoadAddressU32 with this shape).

The walk now takes such an arm when its V# is read from a pointer that is
the record pointer or another chase arm, records each distinct chase as a
hop of the column, and refuses a chase that reaches itself or a depth
over 4. The snapshot then also captures every record pointer's chase
closure up to that depth as pointer-keyed entries, each with the T# at
its own offset, so the GPU's lookup finds the pointer the shader reached.
@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@Tamalero @oneandonlydean this draft follows a conditional pointer chase through a V# behind a pointer-keyed record pointer, the shape we read behind the 0x10b84 refusal on #8939. Its branch, fix/shader-pointer-chase-closure at 6a72841, includes #9457, #8939, #7395 and #7223, so one build of it is enough. Would you run Ratchet & Clank: Rift Apart (PPSA01474) on it and post where it stops after 0x10b84, or that it doesn't? If it still stops at 0x10b84, please post the stderr lines around the refusal: they show whether the chase's two words come from Phis in different blocks, a join this PR still refuses.

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 6858 core/shader/recompiler/ShaderDiskCache.hpp L16
PR 7880 core/shader/recompiler/Optimization/src/ResourceTracker.cpp L753, L791, L804
PR 8564 core/shader/recompiler/ShaderDiskCache.hpp L16
PR 8881 core/shader/recompiler/ShaderDiskCache.hpp L16
PR 9889 core/shader/recompiler/ShaderDiskCache.hpp L16
PR 9897 core/shader/recompiler/ShaderDiskCache.hpp L16

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 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 4072 core/shader/recompiler/Recompiler.cpp
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 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 5431 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5609 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6044 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6169 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 6235 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
docs/dev/debt/shader.md
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 6292 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6300 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 6612 core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 6615 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6741 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6788 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6868 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6879 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
docs/dev/debt/shader.md
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 6946 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/ShaderDiskCache.cpp
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
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 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
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 7604 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 7705 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 8077 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
docs/dev/debt/shader.md
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8281 docs/dev/debt/shader.md
PR 8365 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8406 docs/dev/debt/shader.md
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8527 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8542 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8552 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8625 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 8672 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8716 core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 8998 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 8999 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9020 core/libs/prx/libSceAgcDriver/CMakeLists.txt
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 9335 core/libs/prx/libSceAgcDriver/CMakeLists.txt
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 9356 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 9387 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 9508 core/libs/prx/libSceAgcDriver/CMakeLists.txt
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
PR 9700 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9743 docs/dev/debt/shader.md
PR 9748 core/libs/prx/libSceAgcDriver/CMakeLists.txt
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 9892 core/libs/prx/libSceAgcDriver/CMakeLists.txt

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@EagleBearerYS thanks, I'll build your branch at 6a72841 as is and run PPSA01474 on it. If it stops at or near 0x10b84 again, I'll post the stop and the stderr lines around the refusal here. It's queued behind today's PR runs, so it'll be a few hours.

@jotitadev jotitadev 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.

Run on NVIDIA, since the tests so far are llvmpipe only.

Machine: Ubuntu 26.04, i7-8700, GCC 15.2, Release, full build with BUILD_TESTING=ON. GPUs: NVIDIA GeForce RTX 3070 (driver 595.99.02, Linux) and lavapipe (Mesa 26.0.3, LLVM 21.1.8). Head 6a72841 (with its stack) merged into main da54c65.

  • agc_driver_pointer_chase_table (agc_driver_image_table_tests --pointer-chase) passes 3 of 3 on both devices, including the moved link, the link outside the snapshot, and the refused loop and five-step chase.
  • Control: the same tree with this PR's own ResourceTracker.cpp change reverse-applied (the diff from #9457's head to this head) fails on both devices the way the title does: GetImageResource dword 0 is not a valid runtime value; chain: LoadAddressU32@pc=0x000000c0(GetAddressResource(Phi(Phi(ReadConstBuffer@pc=0x00000074(....
  • ^agc suite, excluding the flip, display DCC and lds_atomics64 tests: 875/878 on the 3070 and 877/878 on lavapipe. The failures are the same as on main on this machine: the three sample_order tests on the 3070 (no 2x2 sample-location grid at 2 samples) and multisample_retirement on lavapipe.

So the chase also works on a real GPU with subgroup 32, not only on llvmpipe's subgroup 8.

GPUs tested: NVIDIA GeForce RTX 3070 (595.99.02, Linux), lavapipe

Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@jotitadev thanks for the RTX 3070 run with the reverse-applied control. A real GPU at subgroup 32 was the gap in this draft's testing. It's in Tested and on the GPUs tested line now.

@MarcZX10

Copy link
Copy Markdown
Contributor

The current evidence is strong for the modeled pointer-chase path: the focused test has a reverse-applied negative control, Linux validation-layer A/B shows no new VUIDs, and an independent RTX 3070 run confirms subgroup-32 behavior. The remaining title-specific gate is the promised PPSA01474 run on this exact stack; the body correctly says Unblocks: none yet until that confirms the 0x10b84 stop is passed. Please keep the draft until that result is available, then update the Game bring-up discussion and Unblocks checklist with its link. Also keep the declared stack order and disk-cache version coordinated with the listed overlapping PRs before merge.

@Tamalero

Copy link
Copy Markdown
Contributor

@EagleBearerYS ran it: PPSA01474 (content 01.005.003) gets past the 0x10b84 stop with this PR, and stops on the next T# load, whose pointer goes through a third chased read.

Setup: Linux (CachyOS, kernel 7.2.9), GCC 16.2.1, Release, RTX 4090 (NVIDIA 615.78.08). The same relinked executable for both builds, a cold shader cache, no input (the stop is in the black load after the logos).

Build Result
Control: c00cb0f (this PR's parent, so the same stack without the chase) 321 s, compute 0x116bb67b00, LoadAddressU32@pc=0x10b84, chain through ReadConstBuffer@pc=0xfd94 and @pc=0x10350 (the stop from #8939)
This PR: 6a72841 351 s, no refusal at 0x10b84; new stop in compute 0x116bb67100 at LoadAddressU32@pc=0x13bc0

Same shader on both builds: a second control run with APS5_DUMP_SHADERS=1 stopped on a sibling, compute 0x116bb5e100, at the same site at pc=0xf898 (its chase reads are at 0xeaa8 and 0xf064). Replaying its prepare request offline with agc_shader_replay from each build:

  • control: refused at LoadAddressU32@pc=0xf898;
  • this PR: passes 0xf898, refused at LoadAddressU32@pc=0x128d4, a later T# load.

So the chase is followed on the title's own shader, not only on the test one.

The next stop: in both refusals (0x13bc0 in game, 0x128d4 in the replay), the pointer Phi has the two chase reads you modelled plus a third read:

  • in game: ReadConstBuffer@pc=0x12e9c(GetBufferResource(LoadAddressU32@pc=0x12de8(GetAddressResource, …)));
  • in the replay: ReadConstBuffer@pc=0x11bb0 through a V# loaded at pc=0x11afc from the current pointer.

In the replayed shader that third read is an s_buffer_load_dwordx16, where the two reads you modelled are x4 (0xeaa8) and x8 (0xf064). The pointer also still passes through the lane-mask BitwiseAnd32(…, CompositeExtractU32x4(Ballot, Immediate)) arms. This log can't show whether the refusal comes from the third read's offset or word position, a V# at an offset other than 0x60, or the Phi join you mentioned. The tracker logs nothing before the throw, so the chain below is all the stderr has. If it helps, I can run APS5_DUMP_IR on 0x116bb67100 and post the blocks around the third read.

Full refusal, in game (compute 0x116bb67100)
compute shader 0x116bb67100: shader 0x116bb67100 stage 0: ShaderRecompiler::Recompile: GetImageResource dword 0 is not a valid runtime value; chain: LoadAddressU32@pc=0x00013bc0(GetAddressResource(Phi(Phi(Phi(Phi(Phi(Phi(Phi, Phi), Phi(Phi, Phi)), BitwiseAnd32(Phi(Phi, Phi), CompositeExtractU32x4(Ballot, Immediate))), Phi(Phi(Phi(Phi, Phi), Phi(Phi, ReadConstBuffer@pc=0x00010350)), Phi(Phi(Phi, Phi), Phi(Phi, Phi)))), Phi(Phi(Phi(Phi(Phi, Phi), BitwiseAnd32(Phi, CompositeExtractU32x4)), Phi(Phi(Phi, Phi), Phi(Phi, Phi))), ReadConstBuffer@pc=0x00012e9c(GetBufferResource(LoadAddressU32@pc=0x00012de8(GetAddressResource, Immediate, Immediate, Immediate), LoadAddressU32@pc=0x00012de8(GetAddressResource, Immediate, Immediate, Immediate), LoadAddressU32@pc=0x00012de8(GetAddressResource, Immediate, Immediate, Immediate), LoadAddressU32@pc=0x00012de8(GetAddressResource, Immediate, Immediate, Immediate)), Immediate))), Phi(Phi(Phi(Phi(Phi(Phi, Phi), Phi(Phi, ReadConstBuffer@pc=0x00010350)), Phi(Phi(Phi, Phi), Phi(Phi, Phi))), BitwiseAnd32(Phi(Phi(Phi, BitwiseAnd32), Phi(Phi, Phi)), CompositeExtractU32x4(Ballot(Immediate), Immediate))), Phi(Phi(Phi(Phi(Phi, Phi), Phi(Phi, Phi)), Phi(Phi(Phi, Phi), ReadConstBuffer@pc=0x00010350(GetBufferResource, Immediate))), Phi(Phi(Phi(Phi, Phi), Phi(Phi, ReadConstBuffer@pc=0x0000fd94)), Phi(Phi(Phi, Phi), Phi(Phi, Phi)))))), Phi(Phi(Phi(Phi(Phi(Phi(Phi, Phi), Phi(Phi, Phi)), Phi(Phi(Phi, Phi), ReadConstBuffer@pc=0x00010350(GetBufferResource, Immediate))), Phi(Phi(Phi(Phi, Phi), Phi(Phi, ReadConstBuffer@pc=0x0000fd94)), Phi(Phi(Phi, Phi), Phi(Phi, Phi)))), Phi(Phi(Phi(Phi(Phi, Phi), Phi(Phi, ReadConstBuffer@pc=0x00010350)), Phi(Phi(Phi, Phi), Phi(Phi, Phi))), ReadConstBuffer@pc=0x00012e9c(GetBufferResource(LoadAddressU32@pc=0x00012de8(GetAddressResource, Immediate, Immediate, Immediate), LoadAddressU32@pc=0x00012de8(GetAddressResource, Immediate, Immediate, Immediate), LoadAddressU32@pc=0x00012de8(GetAddressResource, Immediate, Immediate, Immediate), LoadAddressU32@pc=0x00012de8(GetAddressResource, Immediate, Immediate, Immediate)), Immediate))), Phi(Phi(Phi(Phi(Phi(Phi, Phi), Phi(Phi, ReadConstBuffer@pc=0x0000fd94)), Phi(Phi(Phi, Phi), Phi(Phi, Phi))), Phi(Phi(Phi(Phi, Phi), Phi(Phi, Phi)), ReadConstBuffer@pc=0x00010350(GetBufferResource(LoadAddressU32@pc=0x00010338, LoadAddressU32@pc=0x00010338, LoadAddressU32@pc=0x00010338, LoadAddressU32@pc=0x00010338), Immediate))), Phi(Phi(Phi(Phi(Phi, Phi), Phi(Phi, Phi)), Phi(Phi(Phi, Phi), ReadConstBuffer@pc=0x0000fd94(GetBufferResource, Immediate))), Phi(Phi(Phi(Phi, Phi), Phi(Phi, CompositeExtractU32x4)), Phi(Phi(Phi, Phi), Phi(Phi, ReadConstBuffer@pc=0x00001aa8))))))), Immediate, Immediate, Immediate)

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

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@Tamalero thanks for running it on Ratchet & Clank: Rift Apart (PPSA01474), with the parent as a control and the offline replay of the sibling shader. Getting past 0x10b84 on the title's own shader is the run this draft was waiting for, and it's in Tested and on the Unblocks line now. The third chased read at 0x13bc0 is the next step. The IR blocks around it (APS5_DUMP_IR on 0x116bb67100), as you offered, would show whether it's the read's offset or word position, a V# away from 0x60, or the Phi join.

@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 22, 2026
@Tamalero

Copy link
Copy Markdown
Contributor

@EagleBearerYS here is the third read. It's none of the three you listed: the pointer's two words are not adjacent. The V# offset differs too, but the per-site PointerChase already covers that.

Same shader, both builds: a dump run of this PR stopped in compute 0x116bb67b00 at LoadAddressU32@pc=0x13bc0 (the same sites as the 0x116bb67100 refusal above). The control stopped in that same shader at 0x10b84. I replayed its prepare request and its sibling's (0x116bb5e100, stop at 0x128d4) through a copy of this PR's ResourceTracker.cpp with tracing added.

Step V# load V# at Pointer words read
R record read at 0x1aa8
H1 0xfd88 P+0x60 0xfd94: offsets 0x0 / 0x4
H2 0x10338 P+0x60 0x10350: offsets 0x0 / 0x4
H3 0x12de8 P+0x110 0x12e9c: low at 0x54, high at 0x6c

The sibling has the same shape: H1 0xea9c/0xeaa8, H2 0xf04c/0xf064, H3 0x11afc/0x11bb0 (V# at P+0x110, words 0x54/0x6c).

  • Where it fails: ChaseHighRead requires highImmediate == readImmediate + 4. For H3 the high read's immediate is 0x6c against a low of 0x54, so walk() returns false at the ChaseHighRead call, and PointerRecordPairs and then TryMakeIndirectColumn refuse.
  • The shader really splits the pointer: in the sibling's code, H3 is one s_buffer_load_dwordx16 from offset 0x40, and the next pointer is rebuilt from dword 5 (low) and dword 11 (high) of that load before the next s_load through it.
  • What does pass:
    • H3's V# comes off the pointer the walk is already following (the same Phi pair).
    • The unpaired Phis from different blocks and the lane-mask BitwiseAnd32(…, Ballot) arms walk without failing.
    • With only the adjacency check relaxed, in a local scratch build, the tracker finds three sites with no loop and depth 3, within your cap of 4.

So the step is structurally the one you modelled, with two different parameters: a V# immediate of 0x110, which per-site chases already cover, and a high word at its own immediate.

A possible change: keep the high read's own immediate in PointerChase, for example readHighImmediate. ChaseHighRead would still require the same handle and readOffset, and could require the high word to be 4-aligned. ResourceMaterializer would then read the high word there instead of readImmediate + 4u, and the field would go into the disk-cache serialization.

After that: in the relaxed scratch build, both shaders get past the T# and stop on sampler resource limit exceeded (MaxSamplers = 32). That build is only the relaxed check, not a real fix, so I can't yet say the same stop follows your change. I'll run Ratchet on it when it's pushed.

The IR stays local (it's the title's code), but I can trace any value you need.

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

PointerRecordPairs found an existing chase site by its V# alone. A
second chase arm that reads its next pointer through the same V# at
other words therefore kept the first arm's PointerChase, and the
snapshot never followed the second arm: a T# behind a node whose V#
holds two links (words 0 and 1, words 2 and 3), each taken in its own
lanes, faulted as outside the snapshot when the second link was taken.

Sites are now keyed by the V# and the whole PointerChase, so each word
pair of one V# is its own chase in the column and in the snapshot's
closure. This follows the chase work for Ratchet & Clank: Rift Apart
(PPSA01474); its trace has not shown this shape yet.
@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@Tamalero thanks for tracing the third read in both shaders, down to the s_buffer_load_dwordx16 that splits the pointer. Your reading checks out against the code: ChaseHighRead looks for the high word at readImmediate + 4, so a high word at its own immediate (0x6c against a low word at 0x54) refuses there. A follow-up on top of this PR is being built with your readHighImmediate suggestion. I'll link it here when it's up, and a Ratchet & Clank: Rift Apart run on it then would show what comes next. Your trace and the MaxSamplers stop from the relaxed build are in Tested.

@EagleBearerYS
EagleBearerYS marked this pull request as ready for review October 22, 2026 11:13

@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.

The latest report is unusually thorough: it includes the exact nested-pointer regression, positive and negative controls, cache and test coverage, validation-layer comparison, and a real NVIDIA run. I verified that the linked Game bring-up discussion #2172 is the Ratchet & Clank thread and that it now records this PR's later 0x13bc0 stop. I found no additional code issue in this review pass.

Before merge, this stack needs a current base. The PR targets da54c65, while main is 95 commits ahead at 8b3a6ba and those commits touch shader recompiler files changed here. Since this PR depends on open #9457, coordinate landing order, then rebase onto current main with that prerequisite and refresh overlap/dependency metadata. After rebasing, rerun the focused chase-word/table cases and the CONTRIBUTING.md validation-layer comparison on the same GPU for the current base and the updated head; the detailed existing comparison predates the latest head. Product jobs are skipped by path filters.

I independently ran exact-head git diff --check, tools/check_conventions.py against current main, and tools/check_test_registration.py --scripts; all passed (171 scripts registered). I could not configure the full project locally because the host lacks SDL2 development files and the SDL2 submodule is uninitialized, so I did not run guest_ajc/AGC tests here. The author and reviewers report the focused Linux and NVIDIA/lavapipe runs described in the PR.

@MarcZX10

Copy link
Copy Markdown
Contributor

Correction to my review text: “guest_ajc” was a typo; I meant the AGC test suite.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@MarcZX10 thanks. The stack is refreshed: this PR has #9457's head 2a5db7a merged in (#9457 itself carries main cc00785), and then main 3ca07d0 (7928543). Both merges are clean, and this PR's own diff is unchanged. The order stays #8939 → #9457 → this PR.

  • Focused cases: the 18 chase, hop and nested table tests, with main's two image-table tests and the disk-cache tests, pass 10 of 10 on GitHub's Linux runner, and build and pass on Windows with the Vulkan tests skipping (run).
  • Validation, main against this head, in the same run: one GitHub Linux runner, Ubuntu 24.04.5, llvmpipe (Mesa 25.2.8, LLVM 20.1.2), with ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation and ctest -V. The layer loads in both runs. Both log only VUID-VkDeviceCreateInfo-pNext-pNext, and this head adds none.
  • Overlap: rechecked at the new head against the PRs listed in the body. The conflicts are the same: the disk-cache version line, and fix(shader): resolve table column pointers through Phi nodes #7880 in ResourceTracker.cpp.

All of it is in the body.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@EagleBearerYS we ran PPSA01474 on your branch at 6a72841 (cold shader cache, APS5_DUMP_IR=all). Your head has since moved to 7928543; this result is for 6a72841.

The 0x10b84 refusal is gone. The title now stops 195.9 s in, in a compute shader with the same pointer Phis as before (IR ids %82054/%82055 match the 0x10b84 dump):

compute shader 0x116bb5a600: shader 0x116bb5a600 stage 0: ShaderRecompiler::Recompile: GetImageResource dword 0 is not a valid runtime value; chain: LoadAddressU32@pc=0x00013bc0(GetAddressResource(Phi(...

The pointer, in the after srt pass:

Block $2171 pc=0x00013ba8..0x00014400
  %107038 = GetAddressResource %103981, %103980
Block $2153 pc=0x00005bd0
  %103980 = Phi [%103924, $2142], [%82054, $2152]
  %103981 = Phi [%103925, $2142], [%86713, $2152]

Walking the two words edge by edge, the leaves are the record read R (%8580/%8581, pc 0x1a88), the waterfall lane-mask edges, the two chase arms from the 0x10b84 case (V# at pc 0xfd84 and pc 0x1032c, which your change now follows), and one more chase site: ReadConstBuffer@pc=0x12e9c of a V# loaded at pc 0x12de8.

That site is the join you said the PR still refuses: its pointer's two words come from Phis in different blocks.

Block $2136 pc=0x00012de4..0x0001343c
  %101052 = GetAddressResource %86713, %82054
  %101298 = GetBufferResource %101053, %101054, %101055, %101056   ; LoadAddressU32 %101052 x4
  %101301 = ReadConstBuffer %101298, 0x00000000
Block $2052 pc=0x00005b7c
  %86713 = Phi [%86658, $2041], [%82055, $2051]
Block $2041 pc=0x00005b7c
  %86658 = Phi [%82055, $2024], [%82112, $2040]
Block $2019 pc=0x00005b60
  %82054 = Phi [%82009, $2008], [%79724, $2018]
  %82055 = Phi [%82010, $2008], [%79725, $2018]

The low word goes through two extra Phis, at pc 0x5b7c. Their only new arm is %82112, a BitwiseAnd32 with a CompositeExtractU32x4(Ballot, …) at pc 0x104ac: the waterfall loop's remaining-lanes mask. So on one path the low SGPR is reused as a lane mask while the high SGPR keeps %82054. Read as a pointer, the pair is still %82055/%82054, the pair from the 0x10b84 case (R or a chase arm). For comparison, the chase your change follows at pc 0xfd84 takes both words from Phis in the same block (pc 0x5af0).

What we think would cover it: before pairing P's words, resolve a word through Phis whose other arms are lane-mask values (the masks #8939's edge pairing already treats as dead) until it reaches the block of the other word's Phi. Then P is %82055/%82054 again, and the site at pc 0x12e9c is one more chase site within your depth cap. We can't tell from this IR whether another site follows it.

We can run the title again on a head that takes this case. The 1.4 M-line IR dump of this shader is available if you want any other block.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@oneandonlydean thanks for the run and the trace. #9749, stacked on this PR and ready, is the follow-up for exactly this LoadAddressU32@pc=0x13bc0 stop. In Tamalero's Ratchet & Clank: Rift Apart (PPSA01474) runs (comment), a control (#9749 at 6db7b36 with only its own commit 7ab0765 reverted) stops at 0x13bc0 at 294 s, through the same third read, ReadConstBuffer@pc=0x12e9c through the V# at pc=0x12de8. With #9749 the run lasts 425 s, passes 0x13bc0, and stops on sampler resource limit exceeded; the two captured requests replay the same way.

#9749 doesn't add the Phi resolution you propose. Its only tracker change is in ChaseHighRead: it takes the next pointer's high word at its own immediate in the same read, because at that site the high word is at 0x6c against a low word at 0x54, from one s_buffer_load_dwordx16 (Tamalero's trace). Since that commit is the only difference between the control and #9749, the low word's lane-mask Phis at 0x5b7c don't appear to be what stops the walk on the current tree, and the refusal left there was the high-word offset. We haven't checked that against your blocks, though. Since 6a72841 this PR has gained 2c1a650 (one chase site per V# word pair) and main merges; its head is now 7928543.

If you're willing, a run of #9749's head b2d4afc with your setup would settle it. If it still stops at 0x13bc0, we'll add your proposal as a commit on #9749, with a test built from your IR, and then the blocks around 0x12de8 and 0x5b7c from the dump would be what we'd ask for. If it passes, test/9749-sampler-limit (0f8c896, #9749's head plus one commit) is a candidate for the sampler stop after it.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@EagleBearerYS thanks, that settles it: the single-commit control is the evidence that counts, and our Phi-resolution reading was not what stopped the walk. In our dump the two words of that third chase (%101301 / %101305) are both reads of the same V# (%101298, block at pc 0x12de4), which fits one s_buffer_load_dwordx16. The IR printer shows every immediate offset there as 0, so the dump couldn't have shown 0x54 against 0x6c. Please disregard our proposal for #9749.

In our next test window (within the next day) we'll run the title on test/9749-sampler-limit (0f8c896, #9749's head plus the sampler commit) with the same recipe as before (cold shader cache, APS5_DUMP_IR=all). That covers #9749's head as well. If it stops at 0x13bc0 we'll say so, and if it stops on the sampler limit (32 samplers per shader) we'll post the shader and the requests around it.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@oneandonlydean one change before you spend that test window. @Tamalero ran test/9749-sampler-limit (comment), and it doesn't move these shaders. Their extra samplers are 60 columns of one unkeyed table read per lane, not heap S# reads the branch could share, and the same shaders are also over the 64-pair limit (101 to 112 pairs). So that branch isn't worth your run. A branch that raises the sampler and pair limits is being built on #9749's head, and I'll link it here once it's up. If you'd still like to use the window now, a run of #9749's head b2d4afc would be a second confirmation for 0x13bc0. Tamalero's game run and both replays already show it passing.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@EagleBearerYS a correction to my last comment: we won't run test/9749-sampler-limit after all. Tamalero's runs on #9749 already show what that run would tell us, so repeating them here adds nothing. If a test slot is free we may still run #9749's own head (b2d4afc), and we'll post the result if we do.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@oneandonlydean here's the branch I mentioned: test/9749-sampler-masks (b340d22, #9749's head plus one commit). It raises the sampler limit to 64 and the sampled pair limit to 256, and moves FormatVersion to 49, so the shader cache starts cold. I've asked @Tamalero on #9749 to replay their captured requests on it. If you get a test slot, a run of it also covers #9749's head.

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 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.

7 participants