Skip to content

fix(agc): land cross-queue waits and capture the gated compute group at the gate - #1864

Merged
adriadam10 merged 4 commits into
boykopovar:mainfrom
fisherjoey:fix/agc-compute-gate-capture
Oct 10, 2026
Merged

adriadam10 merged 4 commits into
boykopovar:mainfrom
fisherjoey:fix/agc-compute-gate-capture

Conversation

@fisherjoey

@fisherjoey fisherjoey commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

What

A compute queue worker passes a WAIT_REG_MEM as soon as another queue's worker has recorded the label store it waits on, before the GPU has executed that store. The dispatches behind the wait are then captured from guest memory the title has not written yet. On hardware the CP cannot pass the wait that early, so titles fill the tables a gated dispatch reads after they submit, and GPU latency keeps it ordered. Demon's Souls does this every frame, and it crashes 20-90 s into boot with guest memory is not readable at 0x60, bad V#s or null texture bases on a compute queue. #1787 has the traces.

Two commits:

  1. Land cross-queue waits before a guest read. A wait that a worker passes on another queue's recorded store (not yet in memory) stays on the worker. Before its next dispatch, draw, COND_EXEC or predicated packet, the worker polls until memory satisfies the wait or no unfinished batch writes the label any more. The second condition covers a label the title re-arms after the store lands. A wait on a label the same queue wrote earlier orders no title write, so it isn't kept; LabelStore records the writing queue for that check. The graphics queue is exempt by default, because landing its waits on the compute queues' labels took the attract from 50-54 to 36-39.5 fps, and this title fills its graphics buffers before submitting them. APS5_LAND_WAITS_ALL_QUEUES=1 includes it. A landing wait restarts its timeout only when packets finish, since it runs inside a packet that is itself in flight. A TechnicalDebt entry records the graphics-queue exemption.
  2. Resolve the gated group at the gate. Landing alone left a late read: the worker captured dispatch k of the group 11-25 ms after the gate, waiting on the GPU mutex between dispatches, and by then the title had re-armed the gate and reused the slots (4 of 8 boots still failed). Once a compute queue's wait has passed on memory, the worker now resolves every dispatch up to the next unsatisfied wait before issuing any of them: dispatch-cache lookup, capture and recompile, against a scratch copy of the queue state driven through the SET_* packets. The real dispatch adopts the parked result if the program address and cache key still match. The lookahead stops at a packet that writes guest memory other than a label, at conditional or predicated packets, and at a dispatch that reads a label or buffer an earlier dispatch of the run writes. CPU-path indirect arguments are read at the gate too. If the device later finds the memory changed (guest snapshot differs from registered memory), the dispatch is resolved again from current memory, as on main.

One gap is left. The capture now holds gate-time memory, but the GPU still reads the live import later. Queue 0's next frame overwrites a few structs the group reads, so the device check rejects about 5 dispatches per frame, and those are resolved live (708 in 150 s). Binding gate-time snapshots for those regions would close the gap; it isn't in this PR.

Closes #1787.

Tested

Linux 7.2 (Nobara 44), GCC 16, RTX 5070 Ti, driver 595.91.07.

  • Rebased on current main (ce64d8e). The only conflicts were in the TechnicalDebt list; both sides are kept.
  • Rebased again onto main 0011248, after fix(agc): store labels the GPU cannot reach in stream order #2044 removed the not-imported label outcome (6). PacketMemory.cpp keeps main's outcomes and passes the writing queue to noteLabelStore. Built with ANYPS5_ENABLE_SPIRV_TOOLS=ON: agc_driver_gated_dispatch_capture passes, and ctest -R agc_ -j1 passes 279 of 280 on the RTX 5070 Ti. The one failure, agc_driver_dcc_keys_first_write, also fails on plain main 0011248 on this GPU.
  • ctest -R "agc_driver|guest_agc_driver": 203 of 203 pass, without APS5_WRITE_WATCH_IMPORTS. agc_driver_lds_atomics64 and agc_driver_scalar_sop1 are excluded; they fail on unmodified main on this GPU too (the first one hangs it).
  • python3 tools/check_conventions.py --base origin/main: passes.
  • Demon's Souls (PPSA01341 v1.07), boot to the opening cinematic, with driver threads at high priority (APS5_THREAD_PRIORITY=high, Driver queue workers and the presenter lose their cores to a title's spinning job threads #1788), which makes the race more likely:
failed boots
main, normal priority about 3 of 35
main, high priority 9 of 15
commit 1 only (landing) 4 of 8 (4 of 4 on the day of the runs below)
both commits 0 of 15 (each ran its full 150 s)

The 15 runs include 4 with a 48x24 depth copy that made the crash 2 of 2 before. The group capture costs 25-75 ms per 10 s on queue 0x20, and the parked result is adopted 0.35-0.65 ms after the gate on average. Movie and attract frame rates are unchanged (305-346 presents per 10 s in the movie).

Those runs were measured on an older main (c1241ad) with #861 and a few other local patches. The settle condition and the landing timeout in commit 1 came after them, when a landing wait on a re-armed label hung the title past the main menu, so they weren't in the build behind the 15 runs.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 8, 2026
@fisherjoey
fisherjoey force-pushed the fix/agc-compute-gate-capture branch from bf6f123 to d9a69e4 Compare October 8, 2026 22:29
@fisherjoey
fisherjoey force-pushed the fix/agc-compute-gate-capture branch from d9a69e4 to f8a7323 Compare October 8, 2026 23:05
@fisherjoey

Copy link
Copy Markdown
Contributor Author

Added a test, agc_driver_gated_dispatch_capture (tests/execution/GatedDispatchCapture.cpp, f8a7323). Queue 0x28 runs a long dispatch and then writes a label. Queue 0x20 waits on that label, then dispatches a shader that loads a pointer from a table and stores the dword it points to. The test fills the table 300 ms after submitting, while the 0x28 dispatch is still running on the GPU.

On main (f145139 with only the test cherry-picked), RTX 5070 Ti, 3 of 3 runs fail:

a dispatch behind a cross-queue WAIT_REG_MEM read its table before the producer's label reached memory, so it missed the table the CPU filled after submitting

On the branch it passed 3 of 3 runs and under ctest, and also passes on lavapipe. If the label lands before the fill, the run proves nothing, so the test doubles the producer loop and tries again; it returns 77 if no run is conclusive.

Also rebased on f145139. The TechnicalDebt conflict keeps both sides.

@popcoder565

Copy link
Copy Markdown
Contributor

Native Windows run, since no Windows results were posted. Setup: Windows 10 (19045), RTX 3060 Laptop (NVIDIA 616.92), MinGW-w64 GCC 15.2.0 (WinLibs r7), Release. Head f5dd9ce4, merged onto main 8b393b6 in one build with 31 other PRs that had no Windows run.

No title was run.

🤖 Addressed by Claude Code

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Please drop APS5_NO_LAND_WAITS (WaitMemory.cpp:62) and APS5_NO_GROUP_CAPTURE (DispatchAhead.cpp:13). Switches that turn a fix off aren't wanted here; the fix should just be right.

@fisherjoey

Copy link
Copy Markdown
Contributor Author

Done in 6c292c4. Both switches are gone and the fixes always run now.

  • WaitMemory.cpp: removed landWaitsEnabled() and the APS5_NO_LAND_WAITS read. notePendingWait no longer checks it.
  • DispatchAhead.cpp: removed groupCaptureEnabled() and the APS5_NO_GROUP_CAPTURE read. Its callers in Dispatch.cpp, IndirectDispatch.cpp and resolveGroupAhead no longer check it.
  • Driver.hpp: dropped both declarations.
  • TechnicalDebt.md and the PR description no longer mention them.

agc_driver_gated_dispatch_capture never used either switch, so it's unchanged and passes.

ctest -R agc_: 257 of 258 pass. The one failure is agc_driver_dcc_keys_first_write, which fails on main too (NVIDIA). check_conventions.py --base origin/main passes.

AI-assisted: yes

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

Copy link
Copy Markdown
Collaborator

It now conflicts with main in PacketMemory.cpp since #2044 merged; could you rebase?

@fisherjoey
fisherjoey force-pushed the fix/agc-compute-gate-capture branch from 6c292c4 to 38e25e8 Compare October 10, 2026 06:00
@fisherjoey

Copy link
Copy Markdown
Contributor Author

Rebased onto main 0011248. New head: 38e25e8.

The conflict was in PacketMemory.cpp, where #2044 removed the not-imported label outcome (6). I kept main's outcomes and only added this PR's change there: noteLabelStore gets the writing queue. Nothing else changed.

Tested on an RTX 5070 Ti (NVIDIA 595.91.07), built with ANYPS5_ENABLE_SPIRV_TOOLS=ON: agc_driver_gated_dispatch_capture passes, and ctest -R agc_ -j1 passes 279 of 280, including #2044's new test. The one failure, agc_driver_dcc_keys_first_write, also fails on plain main 0011248 on this GPU.

AI-assisted: yes

@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 10, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Conflicts with main again in Dispatch.cpp (#2229) and TechnicalDebt.md; please rebase.

@adriadam10
adriadam10 force-pushed the fix/agc-compute-gate-capture branch from 38e25e8 to db1bd55 Compare October 10, 2026 08:33
@adriadam10

Copy link
Copy Markdown
Collaborator

Rebased onto current main as requested ("conflicts with main again in Dispatch.cpp (#2229) and TechnicalDebt.md; please rebase"):

  • TechnicalDebt.md: kept both sides.
  • Dispatch.cpp: main's Htile depth-clear note now sits in the non-resolving branch next to fillBuffer/copyBuffer, so the lookahead path stays free of side effects.

Built and ran the libSceAgcDriver ctest scope on the rebased head: 315/315 pass, including agc_driver_gated_dispatch_capture. Merging once CI is green.

@github-actions

github-actions Bot commented Oct 10, 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
#1157 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp L79
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp L1365

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AGC: a compute dispatch is captured before the title has written its tables when a cross-queue wait passes on a recorded label

4 participants