Skip to content

[HOLD] perf(agc): pre-warm deferred shaders on SET_SH_REG packet execution - #5115

Draft
Kikoarl wants to merge 1 commit into
boykopovar:mainfrom
Kikoarl:perf/agc-set-sh-reg-prewarm
Draft

Kikoarl wants to merge 1 commit into
boykopovar:mainfrom
Kikoarl:perf/agc-set-sh-reg-prewarm

Conversation

@Kikoarl

@Kikoarl Kikoarl commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Summary

When games register compute or graphics shaders whose program addresses were not yet present in the shader registers at registration time, libSceAgcDriver defers 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 opcode 0x63 (SET_SH_REG_INDIRECT) writes the stage's program registers and stage prerequisites (resource count and workgroup/thread configuration). This allows compilation in RegistrationPreparePool to overlap with earlier command processing instead of stalling at the moment of draw/dispatch.

Differential Unit Test

  • Added DeferredShaderPrewarmedOnSetShReg to tests/execution/PreparedShaders.cpp.
    • Verifies that deferred shaders are rejected for pre-warming if prerequisites (e.g. program addresses or resource registers) are absent.
    • Verifies that writing program registers triggers background preparation.
    • Verifies that the artifact is successfully populated in prepared->entries without blocking subsequent packet handling.
    • Verifies that already pre-warmed shaders reject redundant preparation attempts.

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 (commit 14036cb0e) vs main + PR:

Run Branch Deferred Shaders at Registration Synchronous Compiles / Waits at Use Runtime Stalls Eliminated
1 origin/main 128 Synchronous compile / wait on dispatch Baseline
1 main + PR 127 0 (pre-warmed ahead of dispatch) ~35-110 ms per compile
2 origin/main 128 Synchronous compile / wait on dispatch Baseline
2 main + PR 124 0 (pre-warmed ahead of dispatch) ~35-110 ms per compile
3 origin/main 128 Synchronous compile / wait on dispatch Baseline
3 main + PR 98 0 (pre-warmed ahead of dispatch) ~35-110 ms per compile

Unit Cost Breakdown:

  • Each deferred compute shader recompilation in Astro Bot takes between 35 ms and 110 ms when triggered on-demand during dispatch execution.
  • By triggering preparation upon SH register configuration during PM4 packet execution, compilation overlaps with buffer transfers and scene setup, completely eliminating synchronous stall points (preparing it at use: 0).

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

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

@adriadam10 adriadam10 self-assigned this Oct 14, 2026
@Kikoarl Kikoarl changed the title perf(agc): pre-warm deferred shaders on SET_SH_REG packet execution [HOLD] perf(agc): pre-warm deferred shaders on SET_SH_REG packet execution Oct 14, 2026
@Kikoarl

Kikoarl commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

putting this pr on hold while i re-evaluate telemetry and profiling on full in-engine scenes.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

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 4448 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 5431 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 5958 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6134 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 6188 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6292 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6326 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6366 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 6375 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6501 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6538 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6598 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6612 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6615 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6616 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6639 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6656 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6660 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6679 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6794 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6801 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6867 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 6946 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6947 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6963 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6966 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 6977 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 7008 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 7153 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 7307 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 7622 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 7644 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp

This branch has not been deployed

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

Labels

agc libSceAgc* needs rebase Conflicts with main, rebase needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants