Skip to content

fix(agc): read user-data callees up to the 1 MiB raw shader program limit - #10140

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
frankea:fix/user-data-callee-window
Oct 30, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
frankea:fix/user-data-callee-window

Conversation

@frankea

@frankea frankea commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

What

ReadUserDataCallee (6d9321f) reads a compute shader's user-data or table callee from guest memory until it can prove the return, and throws once it has read 16,384 dwords (64 KiB) without one. Persona 3 Reload (PPSA10872) calls a 17,369-dword callee from compute shader 0x2014960000, so the dispatch throws its callee at ... does not return within 65536 bytes before the shader is recompiled; @zaidhawk raised the limit to 32,768 dwords locally to get past it (#9068 comment).

  • The callee window is now 1 MiB, the limit ShaderRegistry already uses for the raw shader program it reads at the same address range (limit = 1024 * 1024 in the snapshot reader). A callee that does not return within it still throws the same message with the new size, and an unreadable page before the return still throws is not readable at.
  • UserDataCalleeLength decodes the callee from its start each time it is asked, and the reader asked after every 4 KiB page, so reading n dwords cost O(n²/1024) decoded dwords. With a window 16 times larger, the reader now asks when the read size has doubled since the last check, at the limit, and when the next page is unreadable, so a callee costs at most about twice its length in decoded dwords. The result is the same: UserDataCalleeLength returns at the first s_setpc_b64 through the link register, so words read past it never change the answer, and the checks before an unreadable page and at the limit keep every callee that returned before.

Tested

Windows 11 (build 26200), NVIDIA GeForce RTX 4090 (driver 616.92), MinGW GCC (WinLibs 15.2) Release build with BUILD_TESTING=ON.

  • New case in agc_driver_compute_user_data_calls (both modes, plain and --registered, wave32 and wave64 where the test runs them): a 20,000-dword callee (v_add_nc_u32 v1, 1, v1, 19,998 s_nop 0, s_setpc_b64 s[14:15]) in 20 committed pages of a reservation whose 21st page is not committed, called with the existing 64-adding callee: every lane gets 3 x (1 + 64). The return lies in the last page, which is read after the last size check, so the case also covers the check before an unreadable page.
  • The 5 ctest entries of agc_driver_compute_user_data_calls (2), agc_driver_compute_table_calls, agc_driver_compute_fs_table_calls and scalar_call:
    • This PR (9473294 on main 513ca7e): 5/5, each test run three times (ctest --repeat until-fail:3).
    • Control: main 513ca7e with only this PR's ComputeUserDataCalls.cpp: both agc_driver_compute_user_data_calls entries fail with main's its callee at 0x1f044a90000 does not return within 65536 bytes; the other three pass.
    • main 513ca7e: 5/5.
  • Validation layer: VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation (Vulkan SDK 1.4.363.0), ANYPS5_NO_SHADER_CACHE=1 and ANYPS5_REQUIRE_VULKAN=1 with ctest -V, on this PR and on main 513ca7e: 5/5 each, neither log has was not found but was requested by env var VK_INSTANCE_LAYERS, and neither has a VUID- line or any other validation message.
  • Not run on Linux, and Persona 3 Reload isn't on this machine. tools/check_conventions.py --base origin/main passes.

Checklist

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The reader change raises the bound to the existing raw-program 1 MiB limit and replaces per-page full rescans with geometric checks; it still checks for a return before reporting an unreadable next page and retains the bounded no-return error. The regression allocates 20,000 dwords across committed pages followed by an unreadable page and checks both registered/unregistered dispatch paths where supported. The author reports repeated focused CTest passes, explicit main-control failures for the new long-callee cases, unchanged-main controls, and validation-layer runs on Windows/RTX 4090. Those runtime results are author-reported; I could not run the GPU test locally, and a local syntax-only compile was blocked by absent decoder/SPIR-V development headers. In the exact worktree, conventions, diff-check, and main-sync checks pass (1 ahead/4 behind current main; merge clean). Hosted eligibility/intake/conventions/labels pass; product jobs are skipped by the workflow.

@github-actions

github-actions Bot commented Oct 23, 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 8140 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp L994-997

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 6188 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6977 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 7682 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 8592 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9432 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9508 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9891 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9893 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9895 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9899 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 10193 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A current overlap was missed in the checklist: open #8140 also edits ReadUserDataCallee in ShaderRegistry.cpp (adding the allowGetpc parameter and using it for table callees). I fetched its current head and pairwise git merge-tree reports a content conflict in this same function. The behavior is distinct, but whichever merges second must reconcile both changes; please coordinate the order with #8140’s author and record the integration dependency/overlap in the checklist. This is required by the repository overlap rule. Your local checks and reported RTX 4090 focused runs remain relevant; I did not run a GPU test locally.

@BKlaasWerkman

Copy link
Copy Markdown
Contributor

Windows run on a second NVIDIA GPU and on Intel.

Windows 11 Pro 10.0.26300, WinLibs GCC 15.2.0 r7, Release. NVIDIA GeForce RTX 3060 Ti (driver 617.42) and Intel Arc Graphics (Meteor Lake, driver 101.9150), selected with ANYPS5_GPU. Head 9473294 merged into main 15e71db (clean merge). ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation (1.4.363); no was not found but was requested line.

AI-assisted: yes (Claude Code)

@adriadam10 adriadam10 self-assigned this Oct 24, 2026
@frankea

frankea commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

@MarcZX10 thanks, I'd missed #8140. The checklist now records the overlap, and I've proposed an order on #8140: it lands first, then I merge main here and pass its allowGetpc through finish. The conflict is limited to ReadUserDataCallee, and that is the whole resolution.

AI-assisted: yes (Claude Code)

@wachi35676

Copy link
Copy Markdown
Contributor

AMD run on Windows, to go with the NVIDIA and Intel ones. Windows 11 10.0.26300, WinLibs GCC 15.2.0, Release; main aeff26b and this PR (9473294) merged onto it (clean); ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 (no validation layer installed here); AMD Radeon RDNA2 iGPU (AMD proprietary 2026.04.17) and NVIDIA RTX 5070 Ti (621.01).

  • agc_driver_compute_user_data_calls (both entries), agc_driver_compute_table_calls, agc_driver_compute_fs_table_calls, scalar_call: 5/5 on both GPUs.
  • Control: with main's ShaderRegistry.cpp, both agc_driver_compute_user_data_calls entries fail on both GPUs with its callee at 0x1c500000000 does not return within 65536 bytes, and the other three pass.
  • A wider set of 248 agc_ entries has the same failures with and without the change.
  • fix(shader): link table callees that read their program counter #8140 is still open, so the merge order you proposed there still applies.

AI-assisted: yes (Claude Code); results above were run on this machine.

@Sam-mon7

Copy link
Copy Markdown
Contributor

Overlap with #11442 in ShaderRegistry.cpp (the overlap comment lists both).

#11442 is small:

  • It moves ReadRawShader's local drain lambda into a file-local DrainShaderWrites(address, bytes).
  • It calls that function before the guest reads in ReadUserDataCallee and ReadGuestBytes, so queued queue-0 writes land before shader callees and call tables are read.

Its review only asks for a sync with current main, so I'd propose #11442 lands first. This PR would then re-apply those two calls when it merges main. If you'd rather go first, I'll rebase #11442 onto this PR instead.

Once we agree, I'll record the order with Depends on on #11442.

@frankea

frankea commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

@Sam-mon7 Agreed: #11442 lands first. When it does, I'll merge main here and keep its DrainShaderWrites calls in ReadUserDataCallee. The checklist now records it as Depends on: #11442.

AI-assisted: yes (Claude Code)

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 9473294 merged onto main ba5b533 · tested 2026-10-29 16:04 UTC · 28 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,209 passed · 5 failed (0 new, 4 also fail on main, 1 unstable on main) · 39 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,253 of 2,891 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (855 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) none
Intermittent validation messages (1, seen once in the parallel run, not reproduced when the test was re-run alone 2 times; not counted against this PR)
agc_driver_depth_clear_hle: VUID-vkCmdCopyBuffer-commonparent (6x)
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_submit_progress: failed in 3 of the last 5 main runs
Failing on main too (4, not counted against this PR)
agc_compute_pipeline_inspection
agc_submit_progress
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
agc_driver_depth_clear_hle: SEGFAULT in the parallel run; also passed only on retry in 41 other runs in the last 7 days
Validation IDs already present on main (4 IDs in 404 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (404 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alpha_to_coverage, agc_driver_array_layer_target_cpu, agc_driver_bc_block_copy, and 396 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
Skipped (39)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_msaa_dcc_compared_import_2: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_4: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_8: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_stencil_ref_export: skipped, the device lacks VK_EXT_shader_stencil_export
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_main: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_fresh: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_labels: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SD
Reproduce
git fetch origin 9473294672f189a4126be2b74f72199a7f181bf2 ba5b5330b37724bca4b99f30faab504653635a04
git checkout ba5b5330b37724bca4b99f30faab504653635a04 && git merge --no-ff 9473294672f189a4126be2b74f72199a7f181bf2
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

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

@adriadam10
adriadam10 merged commit 673fa86 into boykopovar:main Oct 30, 2026
15 checks passed
Sam-mon7 added a commit to Sam-mon7/AnyPS5 that referenced this pull request Nov 1, 2026
Conflicts:
- core/libs/prx/libSceAgcDriver/CMakeLists.txt: main moved AGC test
  registrations into tests/cmake fragments (00dd261). Resolved with
  tools/agc_test_split.py resolve; agc_driver_queued_shader_calls keeps
  ANYPS5_NO_SHADER_CACHE=1;APS5_PIPELINED_DRAWS=8 and TIMEOUT 30 in
  tests/cmake/agc_driver_queued_shader_calls_tests.cmake.
- ShaderRegistry.cpp: ReadUserDataCallee keeps main's boykopovar#10140 early
  return on an unreadable page, with DrainShaderWrites ahead of it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants