Skip to content

fix(agc): split oversized dispatches with workgroup offset specialization - #7445

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
elliot1377:fix/agc-dispatch-split-base
Oct 19, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
elliot1377:fix/agc-dispatch-split-base

Conversation

@elliot1377

@elliot1377 elliot1377 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

What

A direct dispatch over maxComputeWorkGroupCount was split into vkCmdDispatchBase chunks starting at a multiple of the limit, which VUID-vkCmdDispatchBase-baseGroupX-00421 forbids. On NVIDIA the split was refused outright.

  • Recompiler: compute modules declare three WorkgroupOffset specialization constants (IDs 8241-8243, default 0), added to WorkgroupId and to GlobalInvocationId (offset × local size). The spanned-wave path derives the global id from the offset WorkgroupId. buildSpecializedModule keeps these constants unmaterialized, and every compute result lists them with value 0, so unsplit dispatches run with offset 0. WorkgroupLocalSize is now shared with the LocalSize execution mode, so the two cannot disagree.
  • Driver: each chunk is recorded with vkCmdDispatch from group 0. Chunks after the first bind a pipeline variant specialized with that chunk's base. Variants share the dispatch's pipeline layout, so bound descriptor sets and push constants stay valid, and they are cached per base on ComputePipelineObjects. No vkCmdDispatchBase call remains, and the NVIDIA throw is removed.
  • A split of a shader with device-memory LDS still throws, as before: its slot base reads the chunk's NumWorkgroups. Recorded in docs/dev/debt/libSceAgcDriver.md, along with the record-time variant build.
  • tests/execution/OversizedDispatch.cpp: the NVIDIA "must throw" check is replaced by a Y-split coverage check that runs on every vendor (limit + 4097 groups, each group writes group_y + 1 at dword group_y). The existing X-split coverage check is unchanged.

Discussed in #7375.

#7390 fixes the same issue and was opened first. It writes each chunk's offset into the ShaderData buffer with vkCmdUpdateBuffer, moves the affected shaders out of push data and bumps RuntimeAbi::Version. This PR leaves unsplit dispatches, push data and the ABI unchanged, at the cost of a pipeline variant per chunk base; it also ran on hardware (below). Only one of the two should land; I'll close this one if #7390 is preferred.

Tested

Windows 11, MinGW-w64 GCC 15.2.0 (WinLibs r7), full build with BUILD_TESTING=ON, rebased on current main.

  • agc_driver_oversized_dispatch passes on both GPUs below, with the Khronos validation layer (vulkan-sdk-1.4.335.0) loaded: 0 validation errors. As a positive control, best-practices warnings were enabled and appeared in the same output.
    • RTX 4050: the Y split ran (69,632 groups, limit 65,535) and every group was covered. On main this dispatch throws. The X split is skipped because the X limit is 2^31-1.
    • UHD Graphics: both X and Y splits ran and were covered.
  • Full ctest on the RTX 4050: 1461/1462 passed. agc_driver_image_plane_address_on_line fails identically on the commit before this change (LOD blend 67.50 vs 60.00); it passes on the Intel GPU.
  • No title tested.
  • After rebasing onto main 3ddbc4acc (conflicts with feat(agc): remap a viewport depth range the device cannot express into the vertex shader #4650's DepthRemapBase = 8240u and readsDepthRemapPush, resolved by keeping both; the offsets stay at 8241-8243): full build OK, and agc_driver_oversized_dispatch passes on the RTX 4050 under the validation layer with 0 errors. The Intel UHD wasn't available for the rerun, so its result above is from before the rebase.

Checklist

@oneandonlydean oneandonlydean self-assigned this Oct 18, 2026
@elliot1377 elliot1377 changed the title fix(agc): split oversized dispatches with workgroup offset specializa… fix(agc): split oversized dispatches with workgroup offset specialization Oct 18, 2026
@github-actions github-actions Bot added the fix error Closes an issue (Fixes #N) label Oct 18, 2026
@github-actions

github-actions Bot commented Oct 18, 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 6249 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp L345
PR 6674 core/shader/recompiler/CMakeLists.txt L241
PR 6786 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp L4698-4700
PR 6959 core/libs/prx/libSceAgcDriver/Graphics/include/PipelineSpecialization.hpp L63-64, L67-69
core/shader/recompiler/Recompiler.cpp L671-672, L699

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/shader/recompiler/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 2434 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/debt/libSceAgcDriver.md
PR 3247 core/shader/recompiler/Recompiler.cpp
PR 4040 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4072 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/Recompiler.cpp
PR 4448 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
docs/dev/debt/libSceAgcDriver.md
PR 4449 docs/dev/debt/libSceAgcDriver.md
PR 4472 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4891 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5021 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/Recompiler.cpp
PR 5058 docs/dev/debt/libSceAgcDriver.md
PR 5371 core/shader/recompiler/PipelineSpecialization.hpp
PR 5372 docs/dev/debt/libSceAgcDriver.md
PR 5441 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5480 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5499 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/debt/libSceAgcDriver.md
PR 5500 core/shader/recompiler/PipelineSpecialization.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 5609 core/shader/recompiler/Recompiler.cpp
PR 5656 core/shader/recompiler/Recompiler.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6146 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6177 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/Recompiler.cpp
PR 6188 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6194 docs/dev/debt/libSceAgcDriver.md
PR 6202 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6223 docs/dev/debt/libSceAgcDriver.md
PR 6235 core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 6292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6300 docs/dev/debt/libSceAgcDriver.md
PR 6360 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6529 docs/dev/debt/libSceAgcDriver.md
PR 6539 core/shader/recompiler/Recompiler.cpp
PR 6543 docs/dev/debt/libSceAgcDriver.md
PR 6548 docs/dev/debt/libSceAgcDriver.md
PR 6549 docs/dev/debt/libSceAgcDriver.md
PR 6563 docs/dev/debt/libSceAgcDriver.md
PR 6611 core/shader/recompiler/Recompiler.cpp
PR 6612 core/shader/recompiler/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6615 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6639 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6647 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6679 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 6683 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6718 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6727 docs/dev/debt/libSceAgcDriver.md
PR 6762 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6763 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6767 core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp
PR 6788 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6801 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6808 docs/dev/debt/libSceAgcDriver.md
PR 6820 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6868 docs/dev/debt/libSceAgcDriver.md
PR 6879 core/shader/recompiler/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
PR 6924 core/shader/recompiler/Recompiler.cpp
PR 6941 core/shader/recompiler/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6946 core/shader/recompiler/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6947 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterHelpers.hpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6958 docs/dev/debt/libSceAgcDriver.md
PR 6960 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6966 docs/dev/debt/libSceAgcDriver.md
PR 6977 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6989 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6995 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7002 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7008 docs/dev/debt/libSceAgcDriver.md
PR 7085 core/shader/recompiler/PipelineSpecialization.hpp
docs/dev/debt/libSceAgcDriver.md
PR 7154 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7201 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterHelpers.hpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 7223 core/shader/recompiler/Recompiler.cpp
PR 7233 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7254 docs/dev/debt/libSceAgcDriver.md
PR 7256 docs/dev/debt/libSceAgcDriver.md
PR 7258 docs/dev/debt/libSceAgcDriver.md
PR 7292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7332 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7340 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7369 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7390 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/tests/execution/OversizedDispatch.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7395 core/shader/recompiler/Recompiler.cpp
PR 7410 core/shader/recompiler/Recompiler.cpp
PR 7432 docs/dev/debt/libSceAgcDriver.md
PR 7484 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/Recompiler.cpp
PR 7505 docs/dev/debt/libSceAgcDriver.md
PR 7604 docs/dev/debt/libSceAgcDriver.md
PR 7618 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7622 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/CMakeLists.txt
PR 7642 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/CMakeLists.txt
core/shader/recompiler/PipelineSpecialization.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterHelpers.hpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7650 docs/dev/debt/libSceAgcDriver.md

@github-actions github-actions Bot added build shader core/shader docs agc libSceAgc* labels Oct 18, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Thanks for this. We're going with this approach for #7375: plain vkCmdDispatch chunks with the base as specialization constants is correct, costs nothing on in-limit dispatches, and the reshaped-wave path already uses the adjusted WorkgroupId.

Two things before it can merge:

  1. Specialization IDs. fix(agc): render layered mesh targets within device limits #6959 (open) assigns MeshLayerBase = 8237u and MeshLayerCount = 8238u in the same PipelineSpecialization.hpp. IsWorkgroupOffset(id) in buildSpecializedModule runs on every module, so after both land a mesh module's 8237/8238 would be read as workgroup offsets and its supplied values would throw "a workgroup offset is supplied at dispatch, not prepared". A both-sides merge compiles, so only a run would show it. Could you move the three WorkgroupOffset IDs to 8241-8243? 8240 is taken by another open PR (DepthRemapBase), and 8241 onward are free.
  2. A hardware run. The Tested section is empty. Could you fill in the template (What / Tested / GPUs tested / checklist) and state what you ran it on, ideally a dispatch over maxComputeWorkGroupCount on NVIDIA, where main's split was refused?

We'll run the full CI and our game checks on the result.

@elliot1377

Copy link
Copy Markdown
Contributor Author

Thanks for the review!

  1. Specialization IDs: moved the three WorkgroupOffset IDs to 8241-8243 in 1551e7a. Nothing else hardcodes them. I rebuilt and reran agc_driver_oversized_dispatch on both GPUs below: it still passes, with 0 validation errors.
  2. Hardware run: sorry, the template was empty when the PR was first opened; it's filled in now. The run you asked for is in Tested. On an RTX 4050 Laptop GPU (driver 617.42, Windows 11), the new Y-split check dispatches 1×69,632 groups against a 65,535 limit, the split main refuses on NVIDIA, under the Khronos validation layer (vulkan-sdk-1.4.335.0). There are 0 validation errors, and every group writes its own result. On an Intel UHD (driver 101.7092) both the X and Y splits run and are fully covered. I haven't run it on AMD or lavapipe.

The full ctest on the RTX 4050, run before the ID move, passes 1461/1462. The one failure, agc_driver_image_plane_address_on_line, fails the same way on the commit before this change and passes on the Intel GPU.

@screamm

screamm commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Cross-validation of 1551e7a on NVIDIA. Scratch-merged on main a62c724 (clean), full Release build with BUILD_TESTING=ON.
Host: Windows 11 Pro 26220, NVIDIA GeForce RTX 4070 (driver 596.21, Vulkan 1.4.329), Ryzen 7 5700X, MinGW-w64 GCC 15.2.0 (WinLibs r7).

  • agc_driver_oversized_dispatch 3x: 3/3 pass. The Y split runs (1x69,632 groups against a 65,535 limit, every dword checked). The X split is skipped (X limit 2147483647).
  • Khronos validation layer: not run, the layer is not installed on this host. Your 0-error result under the layer is not cross-checked here.
  • Negative control: I reverted the 10 non-test source files to a62c724, kept the new test and rebuilt. It fails 3/3 with Vulkan dispatch: workgroup count 1x69632x1 exceeds device limits 2147483647x65535x65535 and NVIDIA drops workgroups of a vkCmdDispatchBase split. After restoring: 3/3 pass.
  • agc_driver_image_plane_address_on_line: 3/3 pass on main 3e3e8b9 and 3/3 on this PR. Your RTX 4050 failure (67.50 vs 60.00) does not reproduce on the 4070 with 596.21.
  • Full ^agc_driver_ pass (-j1 --timeout 300): 528/531, 2 skipped (sparse_dma_buf_import, stencil_ref_export, same as main). The 1 failure is agc_driver_bda_device (vkWaitForFences graphics: Vulkan result 2). It is not from this PR: it fails the same way with this PR's sources reverted to a62c724 and passes on 3e3e8b9, so it came in on main between those two commits (not bisected).
  • Non-GPU suite (937 tests, -j8): 936 pass. hardware_oracle_compare timed out under load and passes on a serial rerun.
  • tools/check_conventions.py --base a62c724c3 passes. git diff --check is clean, no env var added.
  • Titles, 60 s each, APS5_PROFILE_DRAW=1 APS5_PROFILE_GPU=1 APS5_TRACE_SUMMARY_EVERY=10, PPSA99005 (PS5 OpenGL Showcase) and PPSA99420: no stop line and no exceeds device limits on either build. Every run was still running at 60 s. PPSA99005 creates 2 compute pipelines on both builds, so no dispatch was split there and the split path is not exercised by these titles.
    • PPSA99005 start-up: in 3/3 PR runs the first ~30 s run at frame p50 160-172 ms (GPU busy 55-77 ms per present), then settle at main's 112-119 ms. None of 5 main runs (3 on 3e3e8b9, 2 on a62c724) shows this. Frames in 60 s: 402/412 on the PR against 440-508 on main.
    • I don't know the cause: the offset constants are 0 on every unsplit pipeline, and other GPU work on the host may play a part. It might be worth a look in your game checks.
    • PPSA99420 is the same within noise (2743 vs 2777 frames, p50 ~17 ms).
  • Diff review:
    • One variant per chunk base, cached and destroyed with the pipeline objects. Only the three offset IDs are replaced, and the first chunk uses the base pipeline.
    • The offsets are correct on all three axes (WorkgroupId + base, GlobalInvocationId + base * LocalSize; the spanned-wave path derives from the offset WorkgroupId).
    • The device-memory LDS slot base is the only remaining raw WorkgroupId read, and that case still throws.
    • recordDispatch rebinds the base pipeline on every dispatch, so a variant left bound after a split cannot leak into the next dispatch.

Not checked: X and Z splits on NVIDIA (the X limit is 2^31-1 and the test has no Z case), AMD, lavapipe, Linux, and any title that issues an oversized dispatch.

Main a62c724, PR head 1551e7a.

Written with Claude Code assistance (validation by a subagent, reviewed in this session).

@LeandroLP23

Copy link
Copy Markdown
Contributor

AMD on Windows: the tests this PR names pass on an RDNA 3.5 GPU.

Radeon 860M (RDNA 3.5, driver 32.0.13058.8001), Windows 11 26300, WinLibs GCC 15.2.0 r7, Release. main 6efb7261b with this PR (1551e7af0) merged, together with #7296, #7446, #7377, #6708, #6748, #6785, #6706, #6613, #7449, #7391, #7489, #7484, #7514. They were built once, so the result is for the combination; each merge was clean.

  • agc_driver_image_plane_address_on_line, agc_driver_oversized_dispatch: pass, 3 runs of 3.
  • Every agc_* test on the combined build: 713 of 716 pass. The 3 that fail, agc_driver_host_import_lifetime, agc_driver_quad_pixel_mask and agc_driver_layered_volume_vertex_no_gpl, fail the same way on unchanged main here, so none comes from these PRs.
  • The Radeon 860M reports a maxComputeWorkGroupCount of 4294967295, so the X split case prints oversized dispatch X split: skipped, the device reports a 4294967295 workgroup limit, which never splits a 129199x1x1 dispatch: on this device the split path is not reached.

AI-assisted

@elliot1377

Copy link
Copy Markdown
Contributor Author

Thanks a lot for the cross-check and the negative control, @screamm, and @LeandroLP23 for the AMD run.

About the slower PPSA99005 start-up: I couldn't find a per-run cost in the change. Unsplit pipelines only gain the three offset values (all 0), and the pipeline journal stores and replays them with the rest of the result. What does change is the recompiler sources, so SourceVersion changes. The shader disk cache and pipelines.journal from the main builds don't apply to this build, and its first run recompiles every shader and pipeline.

That would explain a slow first ~30 s on the first PR run, but not on runs 2 and 3 if they reused the same cache directory. Could you check two things?

  1. Were the three PR runs consecutive, with the same shader cache directory, and no main run in between? pipelines.journal holds one source version, so a main run in between would discard it.
  2. On PR runs 2 and 3, what do the [pipeline-journal] and [shader-disk-cache] lines say (replayed compute, rejected, failed)? You had APS5_PROFILE_DRAW=1 on, so they should be in the logs.

If runs 2 and 3 replayed everything and were still slow, I'll dig further.

@DavidAmsellem

Copy link
Copy Markdown
Contributor

Tested on AMD on Linux, which this PR had not covered.

  • GPU: AMD Radeon RX 7900 XT (RADV NAVI31, gfx1100), radv, Mesa 25.2.8-0ubuntu0.24.04.4
  • OS: Linux Mint 22.3 (Ubuntu 24.04 base), kernel 7.0.0-38, GCC 13.3
  • Built 1551e7af0 merged into main 0bc3003f8, Release, BUILD_TESTING=ON

agc_driver_oversized_dispatch passes.

Full ctest with ANYPS5_REQUIRE_VULKAN=1: 1478 of 1480. Both failures also happen on main
without this PR and are unrelated to it:

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

A Linux run on NVIDIA and lavapipe, plus a standalone check of the NVIDIA behaviour that main's throw describes, for the open question in #7375.

Machine: Ubuntu 26.04, i7-8700, GCC 15.2, Release, BUILD_TESTING=ON, every execution test built. NVIDIA GeForce RTX 3070 (driver 595.99.02) and lavapipe (Mesa 26.0.3, LLVM 21.1.8) on the same host. Head 1551e7af0 merged onto main a373e85e5 (clean). ANYPS5_NO_SHADER_CACHE=1, -j1 --timeout 30. No validation layer on this host.

agc_driver_oversized_dispatch RTX 3070 lavapipe
head 1551e7af0 pass, 3 runs pass
the PR's test on main (production files from main) fails: workgroup count 1x69632x1 exceeds device limits 2147483647x65535x65535 and NVIDIA drops workgroups of a vkCmdDispatchBase split pass

On lavapipe main passes the new Y-split check too, because the out-of-range base it uses is only caught by the validation layer, as #7375 says.

#7375 leaves open whether NVIDIA drops workgroups for a valid nonzero base as well. In a standalone Vulkan program (no AnyPS5 code), I recorded one vkCmdDispatchBase per case into a pipeline created with VK_PIPELINE_CREATE_DISPATCH_BASE_BIT. Each workgroup counted itself into a buffer slot indexed by gl_WorkGroupID, and every invocation compared gl_GlobalInvocationID with gl_WorkGroupID * gl_WorkGroupSize + gl_LocalInvocationID. I ran it with local size 1 and 64, 3 runs each on the RTX 3070.

Case (base, count) RTX 3070 lavapipe
Y: 1000, 1000 / 61438, 4097 / 1, 64 (valid) every group once, none outside the range same
X: 1000, 1000 and Z: 1000, 1000 (valid) every group once same
Y: 65535, 4097 and Z: 65535, 4097 (the base main uses for the second chunk, invalid) every group once same
Y: 65535, 1 (invalid) the group runs once same

None of the cases had a mismatched global ID. So on Ampere with this Linux driver, vkCmdDispatchBase drops no workgroups, even with the out-of-range base. The drop that 288ee89 measured on an RTX 5070 Ti does not reproduce here, and it may be specific to that GPU or driver. This does not change this PR, which stops using vkCmdDispatchBase and keeps every base valid. It only means the removed "NVIDIA drops workgroups" message was not true of every NVIDIA device.

Not run: Windows, AMD, Intel, the validation layer, any title.

GPUs tested: NVIDIA GeForce RTX 3070 (595.99.02) and lavapipe (Mesa 26.0.3), Ubuntu 26.04

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

…tion

A dispatch over maxComputeWorkGroupCount was split into vkCmdDispatchBase
chunks whose base reached the limit, which VUID-vkCmdDispatchBase-baseGroupX-00421
forbids, and NVIDIA refused the split outright.

Compute modules now declare WorkgroupOffset specialization constants (default 0)
that the recompiler adds to WorkgroupId and GlobalInvocationId and keeps
unmaterialized. Each chunk is recorded with vkCmdDispatch from group 0, bound to
a pipeline variant specialized with the chunk's base. The NVIDIA throw is gone;
device-memory LDS splits still throw.

Fixes boykopovar#7375
8237 and 8238 are taken by MeshLayerBase and MeshLayerCount in boykopovar#6959 and 8240 by DepthRemapBase in another open PR, so buildSpecializedModule would read a mesh module's IDs as workgroup offsets once both land.
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@elliot1377
elliot1377 force-pushed the fix/agc-dispatch-split-base branch from 1551e7a to 40d4cc0 Compare October 19, 2026 04:41
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@oneandonlydean
oneandonlydean merged commit 323ece2 into boykopovar:main Oct 19, 2026
4 checks passed
@screamm

screamm commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Answers from the saved run records: the order was main, PR, main, PR, PR, main (then two runs of main a62c724c3). So PR runs 1 and 2 had a main run between them; PR runs 2 and 3 were consecutive, and run 3 still had the slow first ~30 s (p50 160-168 ms). Each run started from a freshly created output folder, and I did not record where the shader cache sat, so I cannot say the runs shared one. The [pipeline-journal] and [shader-disk-cache] lines were not kept in the saved logs, so I can't give the replayed/rejected counts. The host also had other GPU work running at the time. I have not scheduled a re-run that captures those lines; if you want one, say so and I will look at it in a later wave.

Written with Claude Code assistance.

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

Labels

agc libSceAgc* build docs fix error Closes an issue (Fixes #N) shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AGC: splitting a dispatch over maxComputeWorkGroupCount with vkCmdDispatchBase uses a base at or past the limit

6 participants