Skip to content

fix(agc): check GPU-side indirect dispatch group counts against the device limit - #6360

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
hypermoist:fix/agc-gpu-indirect-dispatch-limit
Oct 19, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
hypermoist:fix/agc-gpu-indirect-dispatch-limit

Conversation

@hypermoist

Copy link
Copy Markdown
Contributor

What

A DISPATCH_INDIRECT whose arguments stay on the GPU (vkCmdDispatchIndirect on the imported argument buffer) took its group counts unchecked. The CPU path already refuses counts above maxComputeWorkGroupCount, but the GPU path passed garbage straight to the device: a count buffer that a skipped producer never wrote (PPSA08427 leaves 0xffffffff there when its depth-plane passes are skipped) launched up to 2^32 groups and kept the GPU busy until the title hung.

  • Recorder::RecordDispatchArguments copies the three counts from the import into a 12-byte indirect buffer with a one-thread kernel (Graphics/shaders/DispatchArguments.comp), writing all three as 0 when one exceeds the device limit, the same way RecordMeshArguments already guards mesh indirect records.
  • VulkanDevice::recordDispatch dispatches from that buffer. If the kernel cannot be created, the import is read directly, as before, and stderr says so once.
  • New execution test agc_driver_indirect_dispatch_limit: 0xffffffff groups run no thread; one group still runs every thread.

Tested

Linux (CachyOS, GCC Release, NVIDIA GeForce RTX 3060): the new test passes; the AGC group of ctest passes except agc_driver_store_over_untracked, which fails on unmodified main too. tools/check_conventions.py passes; tools/pr_overlap.py --branch reports no line overlap. The hang was found running PPSA08427 on my local branch, where this change removed it; the game was not rerun on this exact rebase.

Checklist

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

🤖 Generated with Claude Code

https://claude.ai/code/session_01Q8tinbP1MUpUaDsbNWnGnu

…evice limit

A DISPATCH_INDIRECT read on the GPU took its group counts unchecked. A count
buffer that a skipped producer never wrote (PPSA08427: 0xffffffff left by
skipped depth-plane passes) launched up to 2^32 groups and kept the GPU busy
until the title hung. The CPU path already refuses counts above
maxComputeWorkGroupCount.

- Recorder::RecordDispatchArguments copies the three counts from the import
  into a 12-byte indirect buffer with a one-thread kernel
  (DispatchArguments.comp), all three 0 when one exceeds the device limit, as
  RecordMeshArguments does for mesh indirect records.
- VulkanDevice records the dispatch from that buffer; without the kernel the
  import is read directly, as before.
- agc_driver_indirect_dispatch_limit: 0xffffffff groups run nothing, one group
  still runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8tinbP1MUpUaDsbNWnGnu
This was referenced Oct 15, 2026
@github-actions

github-actions Bot commented Oct 15, 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 6905 core/libs/prx/libSceAgcDriver/CMakeLists.txt L269
PR 7449 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp L4689-4690

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 1341 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4040 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4072 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4472 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4891 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5426 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5441 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5480 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5499 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5609 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 5656 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6044 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6146 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6177 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6185 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6186 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6202 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6249 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6501 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6539 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6611 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6615 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6639 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6647 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6683 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6701 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6718 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6747 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6759 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6763 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6786 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6788 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6820 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6868 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6937 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6941 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6946 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6947 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6960 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6963 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6977 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6989 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6995 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7002 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7085 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7154 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7223 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7233 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7332 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7340 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 7369 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7390 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7484 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7505 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7514 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7618 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7622 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp

@savvaniss

Copy link
Copy Markdown
Contributor

Intel and AMD Windows data. On an Intel UHD 770 this fixes the device loss. On an AMD RX 9070 XT the new test fails with and without it, because that device allows 0xffffffff groups in X.

Windows 11 Pro 26300, WinLibs GCC 15.2.0 r7, Release, every execution test built, ANYPS5_NO_SHADER_CACHE=1, serial CTest. Intel UHD Graphics 770 (8086:a780), driver 32.0.101.7088. AMD Radeon RX 9070 XT (1002:7550), driver 32.0.31041.1004, with VkCache emptied first. "Without" means this PR's test files on main c9e6c45de, with none of its other changes.

Reported maxComputeWorkGroupCount: RX 9070 XT [4294967295, 65535, 65535], UHD 770 [65536, 65536, 65536].

UHD 770 RX 9070 XT
This head e54d2f35f merged into main c9e6c45de: agc_driver_indirect_dispatch_limit, ×3 pass fails: indirect dispatch limit: thread 0 ran with 0xffffffff groups
Without, ×3 fails: vkWaitForFences recorder: Vulkan result -4 (device lost) fails, same message as with this PR

On the RX 9070 XT, {0xffffffff, 1, 1} is within the device limit, so the new check correctly lets it through. There the dispatch completes in about 3 s without device loss. The test's fixed count exceeds the limit only on devices whose X limit is lower. An over-limit count derived from the device would make the test hold on AMD too, for example a Y or Z count above 65535 on this GPU.

Full CTest (--parallel 4) on main c9e6c45de with seven open GPU PRs merged (#6258, #6657, #6324, #6246, #6174, #6360, #6598): UHD 770 1381 of 1389, RX 9070 XT 1383 of 1389. main alone passes 1378 of 1385 and 1381 of 1385. The merge adds only two failures: agc_buffer_pool (#6174, both GPUs) and agc_driver_indirect_dispatch_limit (#6360, RX 9070 XT).

AI-assisted: yes (Claude Code)

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 18, 2026
@adriadam10 adriadam10 self-assigned this Oct 18, 2026

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, the approach (clamping the GPU-side counts through a one-thread kernel, same as the mesh arguments guard) looks right. Two things before this can merge:

  • core/libs/prx/libSceAgcDriver/tests/execution/IndirectDispatchLimit.cpp:65: the over-limit row is a fixed {0xffffffff, 1, 1}. On devices whose X limit is 0xffffffff (RX 9070 XT reports [4294967295, 65535, 65535]) that count is valid, so the guard correctly lets it through and the test fails with and without the change, as @savvaniss measured. Derive the over-limit count from the device limits (e.g. maxComputeWorkGroupCount[1] + 1 in Y, or X + 1 when X < 0xffffffff).
  • The branch conflicts with main in Execution/src/VulkanDevice.cpp (recordDispatch) and libSceAgcDriver/CMakeLists.txt. The VulkanDevice.cpp hunk is not a mechanical conflict, so please rebase and re-check that the indirect buffer/offset still feed vkCmdDispatchIndirect correctly.

Not covered by what we can run: the hang on the title after the rebase.

hypermist and others added 2 commits October 19, 2026 18:57
recordDispatch now reads its indirect offset through HostImportOffset,
which adds the import's lead. The unchecked path passes that offset to
vkCmdDispatchIndirect; the clamp kernel reads from import.address plus
the guest offset, since import.address already includes the lead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The over-limit row was a fixed 0xffffffff in X, which is a valid count
on devices that report that X limit (RX 9070 XT). Take the first axis
whose limit is below 0xffffffff and use limit + 1 there; skip when no
count can exceed the limits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@adriadam10
adriadam10 merged commit 8b54728 into boykopovar:main Oct 19, 2026
10 checks passed
TheBlenDed pushed a commit to TheBlenDed/AnyPS5 that referenced this pull request Oct 25, 2026
…atch group counts against the device limit) at e54d2f3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants