Repository navigation
Conversation
When games submit compute or graphics shaders whose program addresses were not yet bound in the SH registers at registration time, libSceAgcDriver defers preparation until first use. When the draw or dispatch subsequently arrives, the shader must compile synchronously or await background work, causing runtime frame stutter. Pre-warm deferred shaders in the background immediately when PM4 opcode 0x76 (SET_SH_REG) or 0x63 (SET_SH_REG_INDIRECT) writes the stage program address and associated prerequisites (resource and thread configuration), allowing compilation to overlap with earlier command processing.
CnRJay
left a comment
There was a problem hiding this comment.
I don't think this does what it intends, and it adds work to a very hot packet.
1. The state is planned before it is final. CanPlanDeferredShader only checks that registers are present (contains). QueueState keeps every register from earlier packets and submissions, so they are almost always present. They just hold the previous draw's values. The context registers it reads (0x1b3, 0x1b4, 0x1b6, 0x2d5) are usually written by SET_CONTEXT_REG after the SET_SH_REG that sets the program address and before the draw. The plan is then keyed on stale state. The draw doesn't match it and prepares again at use, so the shader is compiled twice and the stall this PR targets remains.
2. The overlap is very small. SET_SH_REG for a stage's program is normally a few packets before the draw or dispatch that uses it, in the same command buffer and on the same thread. PlanRegistered (decode plus request build) now runs synchronously on the queue worker. The background job then starts microseconds before the draw blocks on pending. The gap between the two packets is all that can be hidden. Hiding a 35–110 ms compile needs it to start much earlier, for example from the resolve-ahead pass, or at sceAgcDriverResolve*Abi time as main already does when the state is known.
3. Every SET_SH_REG pays for it. Titles write the program registers on nearly every draw. Each such packet now does a registry lookup (RegisteredProgram) and takes the snapshot's mutex, even when nothing is deferred, and does it for up to five stages per packet.
4. Errors are swallowed. catch (...) { return false; } around RegisteredProgram and PlanRegistered hides real failures. The draw would throw them later anyway, but the repository's rule is exact or throw. If a prewarm must be best-effort, it should at least not catch std::bad_alloc or the "front entry matching both type 2 and type 4" ambiguity, which main treats as an error.
5. Duplicated ABI knowledge. The program and resource register numbers per stage type are hard-coded twice (in checkDeferredShadersOnShRegWrite and in CanPlanDeferredShader), separately from the code that already resolves them in the registry.
6. The measurement. The table shows the deferred count at registration changing between runs of the same branch (127/124/98), and no frame times. CONTRIBUTING asks a perf( PR for the aps5-summary.jsonl lines and traces of main and of the change. A trace would also show whether the draw still waits on pending (point 2) or prepares again (point 1).
|
putting this pr on hold while i re-evaluate telemetry and profiling on full in-engine scenes. |
|
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.
|
Summary
When games register compute or graphics shaders whose program addresses were not yet present in the shader registers at registration time,
libSceAgcDriverdefers preparation until the shader is used. When the subsequent draw or dispatch packet executes, the shader must either be compiled synchronously on the queue worker thread or block until asynchronous preparation finishes, introducing visible compile hitches during gameplay.This change triggers background preparation of deferred shaders immediately when PM4 opcode
0x76(SET_SH_REG) or opcode0x63(SET_SH_REG_INDIRECT) writes the stage's program registers and stage prerequisites (resource count and workgroup/thread configuration). This allows compilation inRegistrationPreparePoolto overlap with earlier command processing instead of stalling at the moment of draw/dispatch.Differential Unit Test
DeferredShaderPrewarmedOnSetShRegtotests/execution/PreparedShaders.cpp.prepared->entrieswithout blocking subsequent packet handling.Standard #218 Multi-Sample Telemetry (Astro Bot -
PPSA21567)Tested on native Linux x86_64 with NVIDIA GeForce RTX 5060 Ti across 3 alternating runs on
origin/main(commit14036cb0e) vsmain + PR:origin/mainmain + PRorigin/mainmain + PRorigin/mainmain + PRUnit Cost Breakdown:
preparing it at use: 0).