Repository navigation
fix(shader): follow a conditional pointer chase through a V# behind a pointer-keyed record pointer - #9633
fix(shader): follow a conditional pointer chase through a V# behind a pointer-keyed record pointer#9633EagleBearerYS wants to merge 107 commits into
Conversation
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.
|
@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, |
|
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.
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.
|
|
@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
left a comment
There was a problem hiding this comment.
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.cppchange 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(.... ^agcsuite, excluding the flip, display DCC andlds_atomics64tests: 875/878 on the 3070 and 877/878 on lavapipe. The failures are the same as onmainon this machine: the threesample_ordertests on the 3070 (no 2x2 sample-location grid at 2 samples) andmultisample_retirementon 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.
|
@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. |
|
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 |
|
@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).
Same shader on both builds: a second control run with
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 the replayed shader that third read is an Full refusal, in game (compute 0x116bb67100)Tested and written by Claude (AI assistant) on a contributor's machine. |
|
@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 ( |
|
@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 Same shader, both builds: a dump run of this PR stopped in compute 0x116bb67b00 at
The sibling has the same shape: H1 0xea9c/0xeaa8, H2 0xf04c/0xf064, H3 0x11afc/0x11bb0 (V# at P+0x110, words 0x54/0x6c).
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 After that: in the relaxed scratch build, both shaders get past the T# and stop on 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.
Main's 1D array layer rows add a loop to the table snapshot that records each table's registered image views, keyed by the table's index. This branch copies a column's records in a helper that also serves nested child columns, which have no table index, so the loop stays in the snapshot of the top-level table after the copy.
✅ NVIDIA GPU test: passedPR head
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validated but not compared (20, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 510 tests)Skipped (45)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
…t-words-on-c2 # Conflicts: # core/libs/prx/libSceAgcDriver/tests/cmake/agc_driver_image_table_tests.cmake # core/shader/recompiler/ShaderDiskCache.hpp
…at, 72 boykopovar#9633 now takes 70, so this PR takes 72, above it and every other open PR's number.
✅ AMD GPU test: passedPR head
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (8, not counted against this PR)Validated but not compared (2, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 537 tests)Skipped (58)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
XairRrR
left a comment
There was a problem hiding this comment.
Re-checked — approved
The head moved to ce7a09f, a merge of main onto b9fab3f. The merged tree builds, the focused chase, pointer, nested and disk-cache tests pass, and reverting this PR's own production diff over #9457 turns the three chase tests red with the title's refusal.
What changed since my last review
The text of my previous review isn't available to this run, so this review covers the whole change again. It does not compare against earlier findings.
- The newest commit, ce7a09f, is a merge of
main. Between b9fab3f and ce7a09f it changes no chase code inOptimization/orControlFlowInfo.hpp. - What this PR adds over #9457: I isolated it as
git diff 0a4f7cdbed b9fab3f82e, where b9fab3f is this branch's merge of #9457's 0a4f7cd.- Production code:
ControlFlowInfo.hpp,ResourceTracker.cpp,ResourceMaterializer.cpp,ShaderDiskCache.{cpp,hpp}and one debt entry. - Tests: three chase tests in
ImageTable.cpp.
- Production code:
- Disk-cache format:
FormatVersionis 70 here and 52 onmain.
What I ran
Head ce7a09f3f13259ed5129371c1ac22f33ba4be5df, main 4f41f4c375f5abb487dbd184fbc150f31994d762, merged tree f3d829311ada5c98ebc5032edd2f862735f62fb5. Debian 13 VM, GCC 14.2.0, Release, lavapipe (Mesa 26.1.6, LLVM 19).
git diff --check 4f41f4c375f5abb487dbd184fbc150f31994d762...ce7a09f3f13259ed5129371c1ac22f33ba4be5df
python3 tools/check_conventions.py --base 4f41f4c375f5abb487dbd184fbc150f31994d762
ctest --test-dir build -j8 --test-load 32 -R "agc_driver_pointer_|agc_driver_nested_|agc_shader_disk_cache|agc_shader_nested_|agc_driver_image_table" --output-on-failure
# negative control 1: reverse this PR's own production diff over #9457
git diff 0a4f7cdbed b9fab3f82e -- core/shader | git apply -R
ninja -C build agc_driver_image_table_tests
ctest --test-dir build -R "agc_driver_pointer_(chase|hop|vsharp)" --output-on-failure
# negative control 2: key chase sites by V# handle only (undo 2c1a65045c), on the merged tree
ninja -C build agc_driver_image_table_tests
ctest --test-dir build -R agc_driver_pointer_chase --output-on-failure
# restore the merged files, rebuild, rerun
ninja -C build agc_driver_image_table_tests agc_shader_disk_cache_tests
ctest --test-dir build -R "agc_driver_pointer_chase|agc_shader_disk_cache$"
git status --porcelain
| Check | Result |
|---|---|
git diff --check |
clean |
check_conventions.py |
All conventions checks passed |
Configure, build, libs (script) |
ok |
| Selected tests (script, 2234 run) | 0 new failures, 0 timeouts; 7 known failures that also fail on main (agc_driver_division, denormal-mode tests and others), none of them table tests |
| Validation layer (script, 788 tests) | layer loaded, missing in none, 0 tests with a new VUID- |
| 30 focused tests | 30/30 pass, each under 9 s |
| Control 1: this PR's own production diff reversed | agc_driver_pointer_chase_table, _chase_words_table and _chase_edited_table fail; hop and vsharp tests pass |
| Control 2: chase sites keyed by handle only | agc_driver_pointer_chase_words_table fails ("the T# behind one of the two links faulted"); the other two pass |
| Worktree after restore | clean, chase and disk-cache tests pass again |
Control 1 output (truncated)
1/7 Test #1832: agc_driver_pointer_chase_table .................***Failed 0.12 sec
ShaderRecompiler::Recompile: GetImageResource dword 0 is not a valid runtime value; chain: LoadAddressU32@pc=0x000000c0(GetAddressResource(Phi(Phi(ReadConstBuffer@pc=0x00000074(...
2/7 Test #1834: agc_driver_pointer_chase_edited_table ..........***Failed 0.12 sec
ShaderRecompiler::Recompile: GetImageResource dword 0 is not a valid runtime value; chain: LoadAddressU32@pc=0x000000b4(GetAddressResource(Phi(ReadConstBuffer@pc=0x00000074(...
3/7 Test #1833: agc_driver_pointer_chase_words_table ...........***Failed 0.12 sec
4/7 Test #1830: agc_driver_pointer_vsharp_select_table ......... Passed
5/7 Test #1836: agc_driver_pointer_vsharp_fixed_offset_table ... Passed
6/7 Test #1835: agc_driver_pointer_vsharp_fixed_table .......... Passed
7/7 Test #1831: agc_driver_pointer_hop_table ................... Passed
57% tests passed, 3 tests failed out of 7
Control 2 output
1/3 Test #1833: agc_driver_pointer_chase_words_table ....***Failed 0.41 sec
AGC graphics: pointer table with two chase links in one V#: the T# behind one of the two links faulted:
2/3 Test #1834: agc_driver_pointer_chase_edited_table ... Passed
3/3 Test #1832: agc_driver_pointer_chase_table .......... Passed
Previous findings
The previous review's text isn't available, so its findings can't be checked one by one. Every blocking-class check below passes on the current head.
New findings
Blocking
None.
Non-blocking
- The chase closure keeps working after the entry limit (
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp:1150-1156). Oncerecords.keys >= limit,ChaseClosurestill callsChasefor every remaining frontier pointer and chase. Each call adds up to six 4-byte entries totables.ranges(lines 1127 and 1134-1135). With 65536 records, several chase sites and depth 4, that can be a few million range entries per snapshot.- The ranges for links already followed are needed for invalidation. The work after
outsideis set adds no entries. - Suggested fix: stop the closure once the limit is reached, and add the V# as one 16-byte range.
- The ranges for links already followed are needed for invalidation. The work after
- The description is out of date in two places.
- It still says "The format stays 55" for 6c13924, while the head is at 70.
- Under Tested it says "a chase in a loop and a five-step chase are refused. Disabling each rule turns its case red". Those cases were removed in ee8767f, so the loop and depth-cap refusals in
PointerRecordPairs(ResourceTracker.cpp:1170-1192) no longer have a test. Removing them follows the test policy, but the description should say so.
Code reading notes (no action needed)
- Over-approximation is safe: the closure applies every chase site to every frontier pointer, not only the sites reachable from that pointer. Mixed low/high pairs (
looseHandles) are also accepted. Both are safe because the GPU looks entries up by the full 64-bit pointer, and each entry's T# is read at that pointer's own offset. An extra entry is never wrong, and a missing one faults as outside the snapshot. - Decoder checks:
decodePlanchecks the chase list (at most 64 entries), the depth (pointerChases.empty() == (depth == 0), at mostMaxPointerChaseDepth, pointer-keyed only) and the edit shift (at most 31).
Not verified
- No hardware GPU here, only lavapipe. NVIDIA, AMD RDNA2 and RX 6300 runs are reported by others in the discussion.
- Windows was not built.
- The Ratchet & Clank: Rift Apart and Ghostrunner results are author- and contributor-reported. I did not run any title.
- The whole stack's diff (#7223, #7395, #8939, #9457) is in this merge. I reviewed only this PR's own code over #9457 in detail.
✅ NVIDIA GPU test: passedPR head
Unstable on main (3, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validated but not compared (2, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 530 tests)Skipped (48)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
|
Thanks for the re-check.
|
boykopovar#7223 has merged. The conflicts are this branch's additions on top of it: its tests in the image-table test lists, pointerKeyed in the image-table column and its equality check, the plan encoder's field count and list version, and FormatVersion 63. agc_shader_memory_tests.cmake merged without a conflict but registered agc_shader_nested_snapshot_retry twice: boykopovar#7223's block after agc_shader_descriptor_phis and this branch's before it. The merge keeps this branch's block, which adds agc_shader_nested_sampler_snapshot_retry, at boykopovar#7223's place.
e94fb3f is boykopovar#7395 with its merge of main after boykopovar#7223. The two conflicts are this branch's own changes over boykopovar#7395: FormatVersion 66, and its wording of the pointer-keyed image-table debt entry.
4769d4a is boykopovar#8939 with boykopovar#7395's merge of main after boykopovar#7223. The one conflict is this branch's FormatVersion 67.
6e403b4 is boykopovar#9457 with the stack's merges of boykopovar#7395's main merge after boykopovar#7223. The conflicts are this branch's own: FormatVersion 70 and its pointer-keyed chase debt entry.
✅ NVIDIA GPU test: passedPR head
Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validated but not compared (1, not in the main baseline's validation pass)Validation IDs already present on main (13 IDs in 554 tests)Skipped (50)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
…ords-on-c2 f498717 is boykopovar#9633 with the stack's merges of boykopovar#7395's main merge after boykopovar#7223. The conflicts are this branch's own: FormatVersion 72 and its wording of the pointer-keyed chase debt entry.
What
On Ratchet & Clank: Rift Apart (PPSA01474), with #8939, a compute shader stops with
GetImageResource dword 0 is not a valid runtime valueatLoadAddressU32@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)).
PointerRecordReadthen refuses the chased arms as reads of another buffer at offset 0.The change:
GetAddressResource(P)at an immediate offset, where P resolves to R or to another chase arm.PointerChase {offset, immediate, readOffset, readImmediate}). Since 2c1a650, two links read through one V# at different words each get their own site, and so their own snapshot entry. No serialized field changes there.Overlap: checked with
git merge-treeagainst every open PR touching these files, onmainda54c65, and rechecked at 7928543 (withmain3ca07d0) against the PRs below: the conflicts are the same. #9508's other conflict there, inGuestMemory.cpp, is withmainitself.main'sTryMakeIndirectColumn. The stack's walk takes Phis whose arms differ, so the two compose. Whichever lands second carries the other's case through the same loop.ShaderDiskCache.hpp. This PR moves it to 70, above fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457's 67.mainis at 56 since feat(shader): resolve nested image tables through runtime pointers #7223. Ours take one number each: feat(shader): raise the image limit to 128 and name the counts in its refusals #10489 57, feat(agc): look up table images at run time through a device image table on the GPU heap (APS5_GPU_PATH) #7642 62, feat(shader): read samplers through nested and pointer-keyed descriptor tables #7395 63, fix(shader): take lane-mask arms and a merged high word in a pointer-keyed record pointer #8939 66, fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457 67, fix(shader): follow a conditional pointer chase through a V# behind a pointer-keyed record pointer #9633 70, fix(shader): plan several T#s read through one pointer-loaded V#, keeping the V# real for their other uses #9790 71, fix(shader): read a chased pointer's high word at its own offset #9749 72 and fix(shader): index a pointer-loaded V#'s T# array by a word read from the same buffer #9969 73. The other open PRs seen changing it hold 51 (fix(agc): preserve proven base formatted buffer read extents #14944), 53 (fix(shader): serve ds_ordered_count in wave launch order #12944), 56 (perf(agc): GPU descriptor heap on VK_EXT_descriptor_heap (APS5_GPU_HEAP) #9508, the same asmain), 65 (perf(agc): bound instance-indexed guest buffers by the draw's instance range #14032), 68 (fix(shader): narrow a goto junction Phi to the arm its branch allows #14636), 69 (feat(shader): flatten oversized 1D image arrays to 2D #14871) and 74 (fix(shader): skip a bit-field low word in a pointer-keyed table's Phi #14853). Whichever lands later takes the next number, and the format this PR ends at is settled when its base merges.Tested
agc_driver_pointer_chase_tablefails ×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_tableandagc_shader_disk_cachepass. GitHub's Windows runner builds it, and its Vulkan tests skip there.agc_driver_pointer_chase_words_tablefails 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. Withagc_driver_pointer_chase_table,_hop_table,_phi_high_table,_vsharp_fixed_tableandagc_driver_nested_image_table, it passes 10 of 10 on Linux, all six passing and none skipped; on Windows they build, and all six skip, as the runner has no Vulkan device. libc'sprecise_waitfailed on both arms of that run.VUID-VkDeviceCreateInfo-pNext-pNext, and the commit adds noVUID-message.main3ca07d0 merged in (both clean; this PR's own diff is unchanged), on GitHub's runners (run): the 18nested_/pointer_table tests,agc_driver_image_table_vouched_lookups,agc_driver_image_table_vouch_queued_drawsand the disk-cache tests pass 10 of 10 on Linux, none skipped. On Windows they build and pass, with the Vulkan tests skipping.precise_wait, failed withmain's implementation too. On Windows libkernel's one failure isguest_raise_in_system_call, a 30 s timeout in a libkernel test this PR doesn't touch.mainagainst this head, in the same run: one GitHub Linux runner, Ubuntu 24.04.5, llvmpipe (Mesa 25.2.8, LLVM 20.1.2), withANYPS5_REQUIRE_VULKAN=1,ANYPS5_NO_SHADER_CACHE=1,VK_LAYER_KHRONOS_validationandctest -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 onlyVUID-VkDeviceCreateInfo-pNext-pNext, and this head adds noVUID-message.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.PointerRecordPairsno longer have a test.GetImageResource dword 0 is not a valid runtime value; chain: LoadAddressU32@pc=0x000000c0(GetAddressResource(Phi(Phi(ReadConstBuffer….^agcand^shadershow 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.VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation,ANYPS5_NO_SHADER_CACHE=1,ANYPS5_REQUIRE_VULKAN=1andctest -V, withVK_LOADER_DEBUG=layerto 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.agc_driver_pointer_chase_table, and fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457'sagc_driver_pointer_hop_tableon the same pointer-keyed path. fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457's tree has only the second.was not found.VUID-VkDeviceCreateInfo-pNext-pNext. This PR adds noVUID-message.mainda54c65.agc_driver_pointer_chase_tablepasses 3 of 3 on both devices, including the moved link, the link outside the snapshot, and the refused loop and five-step chase (at that head; ee8767f later removed those two checks).ResourceTracker.cppchange 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 andlds_atomics64tests: 875 of 878 on the 3070 and 877 of 878 on lavapipe. The failures are the same as onmainon that machine: the threesample_ordertests on the 3070, andmultisample_retirementon lavapipe.check_conventions.pypasses, with no comment lines added.LoadAddressU32@pc=0x10b84, the stop from fix(shader): take lane-mask arms and a merged high word in a pointer-keyed record pointer #8939.LoadAddressU32@pc=0x13bc0: a T# load whose pointer goes through a third chased read.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.s_buffer_load_dwordx16.ChaseHighReadtakes a high word only atreadImmediate + 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 onsampler resource limit exceeded(MaxSamplers = 32).main0db47b2. The only conflict wasShaderDiskCache.hpp'sFormatVersionline:mainmoved to 45 (fix(shader): normalize offset perspective barycentrics after interpolation #7831), and this branch keeps its 47. On GitHub's runners (run), the pointer, nested, image-table and disk-cache sets (27 tests) pass 10 runs in a row on Linux. On Windows the set has 29, with two Windows-only disk-cache tests: they build, 5 pass 10 runs in a row, and the other 24, which need a Vulkan device, skip. The libc and libkernel suites pass on both, run withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners. An earlier run linked here didn't run this set, so it's replaced.main379b1dc). The one conflict was inResourceTracker.cpp: fix(shader): take lane-mask arms and a merged high word in a pointer-keyed record pointer #8939's new check on a pointer-keyed Phi edge sits in the paired loop this PR moves into itswalklambda, so the check moves there with it. That's the only change to this PR's own diff.FormatVersionstays at 48, one above fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457's 47.agc_shader_bindless,agc_shader_memoryand the^agc_driver_pointer_,^agc_driver_nested_,^agc_driver_image_tableand^agc_shader_disk_cachetests. Linux: 29 run, 29 pass, 0 skip, 0 fail. Windows: 31 run, 7 pass, 24 skip (Vulkan device tests; the runner has none), 0 fail.APS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.mainc9b2660). The only conflict wasFormatVersion, which goes to 51, one above feat(shader): read samplers through nested and pointer-keyed descriptor tables #7395's 50. This PR's own diff is otherwise unchanged. On GitHub's runners, 10 runs (run) of the pointer, nested, image-table and disk-cache sets (agc_shader_bindless,agc_shader_memory,^agc_driver_pointer_,^agc_driver_nested_,^agc_driver_image_table,^agc_shader_disk_cache): Linux: 29 run, 29 pass, 0 skip, 0 fail. Windows: 31 run, 7 pass, 24 skip (no Vulkan device), 0 fail. The libc and libkernel suites pass on both, run withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.mainc8d34aa). Clean:FormatVersionstays at 51, one above feat(shader): read samplers through nested and pointer-keyed descriptor tables #7395's 50, and this PR's own diff is unchanged: the merge brings in exactly what feat(shader): read samplers through nested and pointer-keyed descriptor tables #7395's merge did (249 files). On GitHub's runners, 10 runs (run) of the pointer, nested, image-table and disk-cache sets (agc_shader_bindless,agc_shader_memory,^agc_driver_pointer_,^agc_driver_nested_,^agc_driver_image_table,^agc_shader_disk_cache): Linux: 29 run, 29 pass, 0 skip, 0 fail. Windows: 31 run, 7 pass, 24 skip (no Vulkan device), 0 fail. The libc and libkernel suites pass on both (143 and 206 tests on Linux, 170 and 222 on Windows), run withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.mainaeff26b,ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1, no validation layer.pointer_andnested_table tests,image_table,vouch,disk_cacheand theagc_shader_tests, 68 entries, pass on both GPUs. On the subgroup-64 AMD device all 18 pointer and nested modes run, with no per-wave case skipped.main+ fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457 and this PR's tests kept,agc_driver_pointer_chase_tableandagc_driver_pointer_chase_words_tablefail on both GPUs withGetImageResource dword 0 is not a valid runtime value(chainLoadAddressU32@pc=0x000000c0(GetAddressResource(Phi(Phi(...), and the rest pass.agc_entries, the same ones fail with and without the stack on both GPUs, none of them table tests.main0cd07dc (run).main0cd07dc, with fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457's resolution of mrfelek's fix(shader): plan an image table for a T# read at a first-lane offset #10228. Clean here, and this PR's changed lines are the same.exec_arguments,guest_timed_join_import_libkernel_sysandguest_timed_join_import_libkernel_webfail at their setup in the verify's Windows build (FileNotFoundErrorstarting the title, or copying the libraries they load), and pass inmain's own Windows job at 1829def (job). The suites ran withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.guest_raise_exceptionfailed after "address waking round 38: the handler did not run within 5 s", asmain9fc181d does in the same verify setup (run); that is libkernel (Windows): guest_raise_exception slowed about 3x and sometimes aborts at "address waking" since 540364a24 (#10666) #11813, and this PR doesn't touch libkernel.exec_arguments,guest_timed_join_import_libkernel_sysandguest_timed_join_import_libkernel_webfail at their setup in the verify's Windows build (FileNotFoundErrorstarting the title, or copying the libraries they load), and pass inmain's own Windows job at 1829def (job). The suites ran withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.main38ccd50 and with it fix(agc): sample cube descriptors with fewer than six slices as six faces #11507'sFormatVersion49, and the edited-V# chase on top (run). The merge is clean,FormatVersionstays 55 (see What), and this PR's diff over fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457 is the same apart from the new commit.LoadAddressU32@pc=0x0002ef78withGetImageResource dword 0 is not a valid runtime value.agc_driver_pointer_chase_edited_tablemodels that arm, for taken and skipped chase steps and a moved link. On GitHub's Linux runner it fails without the change, with that message, and passes 10 runs in a row with it, on fix(shader): follow a conditional pointer chase through a V# behind a pointer-keyed record pointer #9633's earlier head a0308d1 (run); on b5a199b the same, 3 runs (run). The validation comparison on llvmpipe adds noVUID-message: both sides report onlyVUID-VkDeviceCreateInfo-pNext-pNext(run). TheSovietWaffle's replay of the title's request, the last bullet, gets past 0x2ef78 with it.agc_driver_image_sample_short_cuberan 10 times with no failure. Linux: 13 run, 13 pass, 0 skip, 0 fail. Windows: 15 run, 6 pass, 9 skip (the Vulkan device tests), 0 fail. Suites: libc and libkernel pass on Linux (166 and 265). On Windows libc passes (194), and libkernel passes 284 of 287:exec_arguments,guest_timed_join_import_libkernel_sysandguest_timed_join_import_libkernel_webfail at their setup in the verify's Windows build (FileNotFoundErrorstarting the title, or copying the libraries they load), withmain's code too, and pass inmain's own Windows job at 1829def (job). The suites ran withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.guest_filesystemonce, which aborted right after creating its test junctions, andguest_raise_exceptionat round 46 (libkernel (Windows): guest_raise_exception slowed about 3x and sometimes aborts at "address waking" since 540364a24 (#10666) #11813). A rerun of that job passed both, and both pass on 6c13924.agc_driver_nested_image_table's two checks that a valid shader is refused (a V# with another buffer consumer and a T# with another consumer), feat(shader): read samplers through nested and pointer-keyed descriptor tables #7395's merge of feat(shader): resolve nested image tables through runtime pointers #7223's 0dbf547 (yabanana's merge ofmainf7e84c4) and the format commits below this PR. Clean.agc_shader_bindlessandagc_shader_memoryran 10 times with no failure. Linux: 30 run, 30 pass, 0 skip, 0 fail. Windows: 32 run, 8 pass, 24 skip (the Vulkan device tests; the Windows runner has no Vulkan device), 0 fail. The libc and libkernel suites run 191 and 291 tests on Linux and 220 and 314 on Windows, with 0 failed. They ran withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.APS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.FormatVersion, kept at 56, and the debt docs, kept from both sides.agc_shader_replay --preparewithANYPS5_NO_SHADER_CACHE=1ran on theshader_200c520000.prepare.reqdumped onmain74acfad. Windows 10 22H2, WinLibs GCC 15.2.0, Release; CPU replay only, no title run.LoadAddressU32@pc=0x0002ef78onGetImageResource.image resource limit exceeded, the 64-image cap that frankea's replay reached on feat(shader): raise the sampler and sampled-pair limits past the 32-bit sampler masks #10000.main3741ca9, 2,541 of 3,121 tests selected.main, and 2 are unstable onmain.VK_LAYER_KHRONOS_validation, over 948 tests on the GPU, the one new validation ID isVUID-vkCmdCopyBuffer-commonparentinagc_driver_depth_clear_hle. That is agc: the driver's idle flush stores another device's pending storage images on its own command buffer (commonparent, lost device) #10791's idle store with two live devices (fix: fix(agc): keep a device's idle stores and storage write-backs on its own recorder #10940), in a test the same run lists as unstable onmain, and it isn't in this PR's code. 18 of the stack's nested and pointer table tests ran with the layer but aren't inmain's baseline, so they weren't compared.main4eaf19b, fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457's merge of fix(shader): take lane-mask arms and a merged high word in a pointer-keyed record pointer #8939 and its move to 67. Three conflicts. InShaderDiskCache.cpp's static checks, this PR'sTableColumn(144 bytes, 22 fields) and fix(shader): split scalar-address descriptor phis #7694'srequiredBlockseach growDescriptorSourceby 24 bytes; both sides had written the same224there, which git kept, so the merge sets 248, with 4 fields.FormatVersionmoves from 56 to 70 in the format commit, above fix(shader): follow pointer hops and fixed-offset V# reads behind a record pointer #9457's 67, a number no other open PR seen uses. Inagc_driver_image_table_tests.cmake, this PR's three pointer-chase registrations are kept. This PR's own diff is otherwise unchanged. The image-table, disk-cache, BDA anchor-read, shader-memory, nested and pointer tests, with fix(shader): split scalar-address descriptor phis #7694'sagc_shader_descriptor_phis, pass 10 runs in a row. Linux: 33 run, 33 pass, 0 skip, 0 fail. Windows: 35 run, 10 pass, 25 skip, 0 fail. Suites: libc and libkernel pass on Linux (210 and 310) and on Windows (237 and 331). The suites ran withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners. TechLevelZero's NVIDIA bot ran this head merged ontomain88e804b on an RTX 2000 Ada (report): 2,571 passed, 5 failed, none new (3 also fail onmain, 2 unstable onmain), 45 skipped, with no new validation IDs (991 tests on the GPU).main4f64f81 (run). Clean:FormatVersionstays 70, and this PR's own diff is unchanged. TheResourceMaterializer.cppresolution, with feat(agc): sample 1D arrays past maxImageArrayLayers as rows of a 2D image #12246's view loop moved back intoSnapshot, comes from feat(shader): read samplers through nested and pointer-keyed descriptor tables #7395's merge (see feat(shader): read samplers through nested and pointer-keyed descriptor tables #7395). The image-table, disk-cache, BDA anchor-read, shader-memory, nested and pointer tests, with fix(shader): split scalar-address descriptor phis #7694'sagc_shader_descriptor_phis, pass 10 runs in a row. Linux: 33 run, 33 pass, 0 skip, 0 fail. Windows: 35 run, 10 pass, 25 skip (no Vulkan device), 0 fail. Suites: libc and libkernel pass on Linux (217 and 318) and on Windows (244 and 339). The suites ran withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.main4f41f4c (×10, the PR's own tests ×10).ShaderDiskCache.hpp'sFormatVersion, wheremainis now at 52 (feat(shader): raise the sampler and sampled-pair limits past the 32-bit sampler masks #10000) and this PR keeps its 70; anddocs/dev/debt/shader.md, where this PR's entries andmain's new entries are adjacent and both stay. This PR's own lines are unchanged apart from theFormatVersionline's old value.agc_driver_image_table,agc_driver_many_direct_samplersand every test matching^agc_shader_memory,^agc_shader_disk_cache,^agc_shader_sampler_limits,^agc_shader_nestedor^agc_shader_table. Linux: 11 run, 11 pass, 0 skip, 0 fail. Windows: 13 run, 11 pass, 2 skip (no Vulkan device), 0 fail. The libc and libkernel suites run 300 and 322 tests on Linux and 327 and 342 on Windows, with 0 failed. The suites ran withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.^agc_driver_pointer_and^agc_driver_nested_test, the image-table tests in this PR's diff overmain, which the first run's names left out. Linux: 18 run, 18 pass, 0 skip, 0 fail. Windows: 18 run, 0 pass, 18 skip (no Vulkan device), 0 fail.mainc2eafe4 with yabanana's feat(shader): resolve nested image tables through runtime pointers #7223 through fix(shader): take lane-mask arms and a merged high word in a pointer-keyed record pointer #8939 and feat(shader): read samplers through nested and pointer-keyed descriptor tables #7395 (×10).ShaderDiskCache.hpp'sFormatVersion, which stays 70 (mainis at 56 since feat(shader): resolve nested image tables through runtime pointers #7223), anddocs/dev/debt/shader.md, where this PR's pointer-keyed chase entry stays. This PR's own lines are unchanged apart from theFormatVersionline's old value.agc_driver_many_direct_samplersand every test matching^agc_driver_pointer_,^agc_driver_nested_,^agc_shader_nested_,^agc_driver_image_table,^agc_shader_memory,^agc_shader_disk_cacheor^agc_shader_sampler_limits, which takes in every test in this PR's diff overmain. Linux: 33 run, 33 pass, 0 skip, 0 fail. Windows: 35 run, 10 pass, 25 skip (no Vulkan device), 0 fail. The libc and libkernel suites run 301 and 325 tests on Linux and 331 and 343 on Windows, with 0 failed. The suites ran withAPS5_SKIP_TIMING_TESTS=1, which relaxesprecise_wait's median bounds on GitHub's hosted runners.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)LoadAddressU32@pc=0x0002ef78with feat(shader): raise the sampler and sampled-pair limits past the 32-bit sampler masks #10000, in TheSovietWaffle's replay (the next stop is the 64-image cap)