Repository navigation
feat(shader): link compute shaders' user-data callees at dispatch - #3294
Conversation
|
Windows run, since the PR has no Windows results yet. Windows 11 (10.0.26200), WinLibs GCC 15.2.0 r7, Release, NVIDIA RTX 4070 Laptop GPU (driver 616.92), head
|
|
Additional platform validation of standalone PR head Linux: passed — Arch Linux in WSL2 (kernel 6.6.87.2), GCC 16.2.1 20260810; CMake 4.3.1 / Ninja; Release, 6 checks, 0 failures, 0 CTest skips. Built the selected targets and dependencies; this is focused validation, not a full build or full-suite run. Checks: Command after building: Vulkan: Mesa lavapipe 26.2.4 / LLVM 23.1.1 software Vulkan; no native Linux hardware-GPU result. No title/gameplay validation. These results cover the standalone head, not a merge with current main. |
6221496 to
f84cee1
Compare
|
Checked the build and the new tests ( Two things added in our own commit on top of your branch:
Merging once CI is green. |
|
Rebased onto current |
f84cee1 to
34107df
Compare
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
|
Rebased onto current |
34107df to
8c619f1
Compare
…s dispatches link A compute program whose s_swappc_b64 calls a function address held in user data cannot be prepared at registration, so each one was recompiled on the queue thread at its first dispatch. PPSA23566 (UFC 6) defers 104 such programs, all calling from the same program counter, at 0.3 to 2 s each, and the title's 1 s frame wait expired behind them. Such a program now keeps its registration plan. When a dispatch links a callee (boykopovar#3294), the callee is remembered by user data and link register (at most four per pair), and every waiting program whose calls all have a remembered callee is linked with the newest ones and prepared on the registration pool. A dispatch of a program being prepared waits for it as for any registration preparation; a callee that differs at the dispatch is linked and prepared there as before. APS5_NO_CALLEE_PREPARE=1 turns it off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s dispatches link A compute program whose s_swappc_b64 calls a function address held in user data cannot be prepared at registration, so each one was recompiled on the queue thread at its first dispatch. PPSA23566 (UFC 6) defers 104 such programs, all calling from the same program counter, at 0.3 to 2 s each, and the title's 1 s frame wait expired behind them. Such a program now keeps its registration plan. When a dispatch links a callee (boykopovar#3294), the callee is remembered by user data and link register (at most four per pair), and every waiting program whose calls all have a remembered callee is linked with the newest ones and prepared on the registration pool. A dispatch of a program being prepared waits for it as for any registration preparation; a callee that differs at the dispatch is linked and prepared there as before. APS5_NO_CALLEE_PREPARE=1 turns it off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What
Closes #3271.
A compute shader that calls through a user-data pointer pair with
s_swappc_b64can't be built today. #1836 defers it at registration, but a dispatch of it still throwscomputed/data-dependent s_swappc_b64 call target ... is not statically resolvable. These shaders come from Ghost of Yōtei, Astro Bot, Epic Mickey and PPSA15186 (#1817, #2207), Stray (#2749, 371 shaders) and the free Unity catalog title PPSA28319 (68 of its 250 compute shaders, #3271).This links each callee at dispatch, which is when its address is known.
Finding the calls.
FindUserDataCalls(GraphBuilder) returns eachs_swappc_b64whose source pair:s_mov_b32× 2 ors_mov_b64in the same block.A user-data register written later counts only if no branch from after the write leads back to the copy.
Linking, in
InvocationFor. The driver reads each callee from guest memory at the user-data address, up to itss_setpc_b64on the link pair (UserDataCalleeLength).LinkUserDataCallsthen:s_call_b64to its copy (same link value,pc + 4);s_code_end.The prepared source is the linked code, so a different callee gets a different prepared entry;
InvocationForchecks the prepared code before reusing an entry. Shaders without such calls are unchanged.Calls sharing a link pair (GraphBuilder). These shaders make up to 13 calls, all through
s[14:15], writing it withs_movbefore each one. Calls may now share a link pair when their regions are disjoint, each closed by the first return after its target. Outside every region the pair may be overwritten whole (s_mov_b32/s_mov_b64, an SOP2 result, a scalar load), but not read. A single call keeps its current rules, except that those overwrites are allowed outside its region once it has exactly one return.Refused, with a message that names the shader and the call:
sl00.FindUserDataCalls,UserDataCalleeLengthandLinkUserDataCallsare added toshaderCacheReviewedNames. They only build the code that goes into theRecompileRequest, and the disk cache already keys on that code. TechnicalDebt gets an entry for the user-data callees and an update to the static scalar call rules.Tested
Windows 11 (26200), WinLibs GCC 15.2.0 r7, CI configure flags,
main304c745.New tests:
scalar_callgainsCheckUserDataCalls:s[14:15], inside a loop) and its translation;agc_driver_compute_user_data_calls(new, and_registered) dispatches through the driver (PM4SET_SH_REGandDISPATCH_DIRECT). The compute shader's loop calls two callees throughs[0:1]ands[2:3]three times. It checks all 64 lanes:It runs unregistered at wave64 and wave32, and registered at wave32, where the shader is deferred at registration as on
main. Last, an unmapped callee address must fail withis not readable.Results:
--repeat until-fail:3: the two new tests,scalar_call,scalar_swappc,agc_shader_control_flow,agc_driver_prepared_shaders_deferred*andagc_shader_disk_cache.guest_filesystem, which needs Developer Mode for its symlinks here.main961b25f, each of which has its own pending fix:prepared_shaderswithoutdepthRangeUnrestricted(agc_driver_prepared_shaders fails on GPUs without VK_EXT_depth_range_unrestricted (Intel on Windows); fix branch ready #3207, test(agc-driver): set D3D clip range for shared-code draw #3269);vop1_float_unary(fix(shader): compute V_FRACT_F32 from the exact fraction of |x| #1250) andvopc_compare(Shader float mode (IEEE_MODE, DX10_CLAMP, FLOAT_MODE) is per shader; the recompiler assumes one #981);flat_apertures(test(agc): skip flat aperture 64-bit atomic rows without shaderBufferInt64Atomics #3286).Each of these mutations makes a test fail on the RTX 4070:
InvocationFornot statically resolvablelane 0 is 30, expected 207) andscalar_callInvocationForreuses a prepared entry without comparing the linked codethe first callee replaced ... lane 0 is 207, expected 195scalar_callscalar_callscalar_callConflict check against the 10 open PRs that touch
ShaderRegistry.cpp: this adds no conflicting file to any of them. None of them touchGraphBuilderorScalarCall.cpp.tools/check_conventions.py --base origin/mainpasses.Not run: a title. I have no PS5 dumps, and the catalog title isn't run here. The 68 Unity shaders are only known to reach this call: their callees come from the title at run time.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code