Repository navigation
fix(agc): defer shaders that cannot be prepared at registration to their first use - #1836
Conversation
|
Tested on a third title: Stray (PPSA02100 v1.05, Unreal Engine 4) stops at the same point on Setup: Linux (Nobara 44, kernel 7.2), RTX 5070 Ti, NVIDIA 595.91.07. Base is Without this PR, Stray exits about 10 s after launch, before any window, on its first Stack from the core: With this PR, registration gets through 111 deferred shaders:
The run then stops on a different error that is not related to this PR: One note: the PR no longer merges cleanly onto current AI-assisted: yes |
|
Title evidence — the deferral is what unblocks a real title. PPSA26344 (Ghost of Yōtei 01.008): decrypted
With this PR the run does not terminate: the shaders it cannot prepare are deferred and the title continues. Measured ~2 minutes in: the process is alive with 29 threads at ~100% CPU, the GPU is active, the shader disk cache is past 68 writes, and the frame path prints no error (the earlier run's Note #757 is a prerequisite for reaching this point: on Merge note: one conflict, Raw log kept. Written with AI assistance. |
|
PPSA12544 (Hollow Knight: Silksong) on Windows (RTX 5090) hits the "program address registers are missing from the header" case of this PR during startup. It registers a type 4 shader whose header has only these registers:
If it helps, this is the regression I used, for void testHeaderWithoutProgramAddress() {
alignas(256) static const std::array<std::uint32_t, 64> code{0xbf810000};
struct Header {
Shader shader{};
std::array<ShaderRegister, 3> registers{};
};
alignas(8) static Header header{};
header.shader.file_header = 0x34333231;
header.shader.version = 0x18;
header.shader.header_size = sizeof(Header);
header.shader.shader_size = sizeof(code);
header.shader.code = code.data();
header.shader.type = 4;
header.registers = {{{0x08a, 0x60000002}, {0x08b, 0x00030008}, {0x0ca, 0x03000002}}};
header.shader.sh_registers = header.registers.data();
header.shader.num_sh_registers = header.registers.size();
AgcDriverRegisterShader_nid_postfix(&header.shader);
}I haven't tested this PR's branch itself yet, since it currently conflicts with (Written with AI assistance: Claude Code.) |
7b050b6 to
177c4b2
Compare
|
Rebased onto @lubaxe thanks for the Silksong header and the regression test. I added it to
As @Dextroid17 says, this PR only moves the failure from registration to first use. Inlining user-data Thanks to all three of you for testing it on Stray, Ghost of Yōtei and Silksong. AI-assisted: the rebase, the added test and this reply were done by Claude (Claude Code) on my machine. |
|
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.
|
…orting The registered-shader decode ran before the deferral decision, so a single instruction the decoder does not implement ended the title at registration instead of deferring the shader to its first use. Give the decoder's unsupported-instruction failures their own type (UnsupportedInstructionError): the five family-scoped SOP opcode defaults and the MIMG opcode lookup (which now names the opcode) raise it, and the decode in PrepareRegistered defers the shader with that reason when it hits one. Any other exception still throws, so the corrupt-code contract is unchanged. PPSA26344 hits this: unsupported MIMG opcode 229 (0xE5), issue boykopovar#1941. The failing word is reproducible offline from the title's own shader store (cache_ps5/all_shaders.xpps, code word 260), which is how the fixture was checked before it was written. Scope: this defers the scalar-family and MIMG opcode gaps. The vector (VOP*) and memory (SMEM/MUBUF/MTBUF/FLAT/DS) opcode lookups, the modifier-level "not supported" throws and the malformed/truncated throws still abort. Widening the deferral to those is a policy call for the maintainers. Stacked on boykopovar#1836 (the deferral machinery); re-stacked on its current head so this diff no longer carries boykopovar#1836's earlier broad catch. Once boykopovar#1836 lands only the decode change remains. Written with AI assistance.
…orting The registered-shader decode ran before the deferral decision, so a single instruction the decoder does not implement ended the title at registration instead of deferring the shader to its first use. Give the decoder's unsupported-instruction failures their own type (UnsupportedInstructionError): the five family-scoped SOP opcode defaults and the MIMG opcode lookup (which now names the opcode) raise it, and the decode in PrepareRegistered defers the shader with that reason when it hits one. Any other exception still throws, so the corrupt-code contract is unchanged. PPSA26344 hits this: unsupported MIMG opcode 229 (0xE5), issue boykopovar#1941. The failing word is reproducible offline from the title's own shader store (cache_ps5/all_shaders.xpps, code word 260), which is how the fixture was checked before it was written. Scope: this defers the scalar-family and MIMG opcode gaps. The vector (VOP*) and memory (SMEM/MUBUF/MTBUF/FLAT/DS) opcode lookups, the modifier-level "not supported" throws and the malformed/truncated throws still abort. Widening the deferral to those is a policy call for the maintainers. Stacked on boykopovar#1836 (the deferral machinery); re-stacked on its current head so this diff no longer carries boykopovar#1836's earlier broad catch. Once boykopovar#1836 lands only the decode change remains. Written with AI assistance.
…preparable-shaders fix(agc): defer shaders that cannot be prepared at registration to their first use
…orting The registered-shader decode ran before the deferral decision, so a single instruction the decoder does not implement ended the title at registration instead of deferring the shader to its first use. Give the decoder's unsupported-instruction failures their own type (UnsupportedInstructionError): the five family-scoped SOP opcode defaults and the MIMG opcode lookup (which now names the opcode) raise it, and the decode in PrepareRegistered defers the shader with that reason when it hits one. Any other exception still throws, so the corrupt-code contract is unchanged. PPSA26344 hits this: unsupported MIMG opcode 229 (0xE5), issue boykopovar#1941. The failing word is reproducible offline from the title's own shader store (cache_ps5/all_shaders.xpps, code word 260), which is how the fixture was checked before it was written. Scope: this defers the scalar-family and MIMG opcode gaps. The vector (VOP*) and memory (SMEM/MUBUF/MTBUF/FLAT/DS) opcode lookups, the modifier-level "not supported" throws and the malformed/truncated throws still abort. Widening the deferral to those is a policy call for the maintainers. Stacked on boykopovar#1836 (the deferral machinery); re-stacked on its current head so this diff no longer carries boykopovar#1836's earlier broad catch. Once boykopovar#1836 lands only the decode change remains. Written with AI assistance.
What
Since #639,
RegisterShaderprepares every registered shader up front, and the first one that can't be prepared ends the run. Before that, such a shader only failed when it was dispatched, so titles that never dispatch it, or dispatch it late, kept running.Several titles now stop at registration on compute shaders that call a user-data function pointer with
s_swappc_b64, which is not implemented (#1817): Astro Bot, Ghost of Yōtei, Rise of the Ronin, Epic Mickey: Rebrushed, Stray and GTA V. Silksong and Ratchet & Clank: Rift Apart also register headers without their program address registers.This change marks those shaders
deferredinstead of preparing them, and prepares them when they are first used. The deferral is decided before any preparation starts, and only in two cases:s_swappc_b64whose target the call analysis can't resolve.ShaderRecompiler::UnresolvableSwappcTargetis the condition thatGraphBuilder::Buildthrows on today. It is shared with the analysis through a newisFetchCallhelper, so fetch-shader calls are still accepted.Every other error, including any
GraphBuilder,StructurizerorPrepareShaderfailure, still throws at registration. There is no retry after an error.SourceHandleForandInvocationForprepare a deferred shader at dispatch, the path unregistered compute shaders already take. So the original error is raised there if the shader is really used.Tested
Linux (CachyOS, GCC), RX 9070 XT, Mesa 26.2.4 RADV. Head 52b77c2 on
main43972e6.agc_driver_prepared_shaders_deferredregisters a compute shader made ofs_swappc_b64 s[4:5], s[4:5].its s_swappc_b64 at program counter 0x0 calls a computed target.not statically resolvable.agc_driver(tests/Submit.cpp,testHeaderWithoutProgramAddress, contributed by @lubaxe) registers a type 4 header withoutSPI_SHADER_PGM_LO/HI. Onmainit fails withmissing static shader ABI register 201.ctest: 512/513. The failing test isimport_audit, which can't read the commit hash in my worktree and is unrelated.tools/check_conventions.pypasses.UnresolvableSwappcTargetis added toshaderCacheReviewedNames: it only decides whether to prepare at registration and doesn't change what a recompile produces.computed/data-dependent s_swappc_b64 call target at program counter 0x00000074.s_swappc_b64and 1 for missing program registers.rectangle ABI refers to a replaced shader header, which fix(agc): accept bound copies of a registered shader header that differ only in user_data #1837 fixes. No frame is presented yet.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