Skip to content

perf(shader): reuse the source of a bitcast round trip - #7677

Open
tibzejoker wants to merge 1 commit into
boykopovar:mainfrom
tibzejoker:perf/reuse-bitcast-source
Open

tibzejoker wants to merge 1 commit into
boykopovar:mainfrom
tibzejoker:perf/reuse-bitcast-source

Conversation

@tibzejoker

@tibzejoker tibzejoker commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

What

Guest registers are 32-bit words. A float result is bitcast to u32 when it is written to a register, and back to f32 when it is read, so emitted modules carry pairs of OpBitcast that return to the type of the first one's source. This change makes a bitcast back to that type return the source itself. The source dominates every use of the first bitcast, so the module stays valid.

To support this, SpirvModule records the result type and the bitcast source of each function instruction. Both Unary and EmitNative bitcasts look them up.

Tested

Windows 11, MinGW GCC 15.2, RTX 4090 (616.92):

  • New test agc_bitcast_round_trip. It recompiles v_cvt_f32_u32 results added with v_add_f32 and requires that no bitcast goes back to the type of a bitcast's source. It fails on main (one of six bitcasts) and passes with this change.
  • ctest -R ^agc_: 727/728. The failure is agc_driver_bda_device, which fails on main too.
  • GTA V (PPSA04264): 72 compute requests dumped with APS5_DUMP_SHADERS were dispatched on synthetic guest memory with main and with this change. Every byte they wrote was identical. SPIR-V words over these 72 went from 1,930,912 to 1,898,324 (-1.7 %); for example compute 0x143978f00 went from 8,545 to 8,457 words, and 0x25b390800 from 647,759 to 642,443.

This is a small size win: every round trip removed is one instruction the driver no longer compiles.

Performance capture

GTA V (PPSA04264), open world from the same save, RTX 4090, one run each, same session. Base = main 47a266c + #6488 + #6848 + a local bench aid (keyboard pad input while an IME session is open), because GTA V does not run on plain main; c0206c244d15 is this PR on top of it. Traces: fork release perf-7673-7677-20261019.

These frame times do not measure this change. The trace buffer of queue worker 0 fills after about 3.7 s (trace_dropped), and in the open world frame time follows where the save respawns. This PR shortens shader compiles: the SPIR-V word counts and the cold compile times under Tested are the measurement.

{"commit":"6537538e26d6","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"330:60"},"gpu":"NVIDIA GeForce RTX 4090","driver":"NVIDIA 616.92","os":"Windows 10.0.26200","resolution":"3840x2160","window":{"spec":"seconds 330:60","first_present":4184,"ms":60074.435},"presents":416,"frame_ms":{"p50":129.613,"p90":188.599,"p99":328.411,"max":548.842},"gpu_busy_ms_per_present":47.351,"worker_busy_pct":{"queue worker 0x0":3120.864,"queue worker 0x20":33.818,"queue worker 0x28":19.713,"queue worker 0x38":29.948,"queue worker 0x30":12.619},"top_phases_ms_per_frame":{"Driver.EndSubmission":4370.075,"Driver.Packet":271.688,"Driver.Packet.memory_wait":136.191,"Driver.Packet.draw":114.943,"Driver.Draw":113.961,"Driver.Draw.shader_compile_and_link":62.672,"Shader.DrawStage":48.422,"Driver.Draw.draw_enqueued":39.814},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":3.742,"events_lost":3289625,"totals_full":false}},"trace":"aps5-trace-6537538e26d6-44248.json.gz"}
{"commit":"c0206c244d15","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"330:60"},"gpu":"NVIDIA GeForce RTX 4090","driver":"NVIDIA 616.92","os":"Windows 10.0.26200","resolution":"3840x2160","window":{"spec":"seconds 330:60","first_present":3955,"ms":60187.407},"presents":451,"frame_ms":{"p50":126.146,"p90":156.364,"p99":281.649,"max":769.832},"gpu_busy_ms_per_present":49.259,"worker_busy_pct":{"queue worker 0x0":3440.583,"queue worker 0x30":15.325,"queue worker 0x38":20.011,"queue worker 0x20":38.649,"queue worker 0x28":22.082},"top_phases_ms_per_frame":{"Driver.EndSubmission":4465.061,"Driver.Packet":250.534,"Driver.Packet.memory_wait":125.851,"Driver.Packet.draw":102.510,"Driver.Draw":101.584,"Driver.Draw.shader_compile_and_link":63.901,"Shader.DrawStage":49.320,"Driver.Draw.draw_enqueued":26.262},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":3.442,"events_lost":3713441,"totals_full":false}},"trace":"aps5-trace-c0206c244d15-28644.json.gz"}

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: NVIDIA GeForce RTX 4090 (616.92, Windows 11); AMD Radeon 860M (32.0.13058.8001, Windows 11, by @LeandroLP23)
  • AI-assisted: yes (Claude Code)

🤖 Generated with Claude Code

https://claude.ai/code/session_01XBXM4pgkMHYEtqN1EQni2P

@github-actions github-actions Bot added perf Performance change perf numbers missing perf pull request without a before and after summary line labels Oct 18, 2026
@github-actions

Copy link
Copy Markdown

This pull request is labeled perf, so it needs the summary line of a capture on main and one with the change, from the same title, scene and machine, as the Performance report form asks. Please paste both aps5-summary.jsonl lines, attach the two traces and add the output of tools/trace_digest.py.

@github-actions

github-actions Bot commented Oct 18, 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 9508 core/libs/prx/libSceAgcDriver/CMakeLists.txt L2536
PR 10973 core/libs/prx/libSceAgcDriver/CMakeLists.txt L2536

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 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1896 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6235 core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 6280 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6743 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6959 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterHelpers.hpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 7275 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7514 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterHelpers.hpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 8014 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8161 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8542 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8707 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8874 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9020 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 9176 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9248 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9287 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9289 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9335 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 9356 core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 9387 core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 9700 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9748 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 9852 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9892 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9897 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9898 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9989 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10193 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterHelpers.hpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 10233 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10442 core/libs/prx/libSceAgcDriver/CMakeLists.txt

@LeandroLP23

Copy link
Copy Markdown
Contributor

AMD run, since the description covers an RTX 4090. Radeon 860M (RDNA 3.5, driver 32.0.13058.8001), Windows 11 26300, WinLibs GCC 15.2.0 r7, Release, BUILD_TESTING=ON. Build: main 8a456334f with #7677, #7673, #7671, #7666, #7676, #7675, #7672, #7668, #7664 and #7658 merged together (each merge clean, built once).

agc_bitcast_round_trip and agc_driver_bda_device pass 3 runs of 3 on AMD's proprietary driver.

AI-assisted

@tibzejoker

Copy link
Copy Markdown
Contributor Author

About the performance-report request: I captured the requested warm-cache traces (GTA V PPSA04264, open world, RTX 4090). Two things make them a poor fit for this change:

  • With the trace on, the queue worker's event buffer fills within about 3.7 s in this scene (trace_dropped: true, about 3.3 M events lost), so the per-phase totals are not usable. Only the frame percentiles survive.
  • In the open world, the frame time depends mostly on where the save respawns: the same base build ran at 9.3 FPS in its warm-up and 5.5 FPS in its traced run. Shader compilation inside the traced minute also varies between runs (62 to 97 ms per frame of Driver.Draw.shader_compile_and_link), depending on which new shaders that run meets.

This change only makes modules smaller. Its effect is on pipeline creation, so I measured that directly: cold NVIDIA pipeline_create, median of 3 agc_compute_pipeline_replay runs, each with a distinct OpSourceExtension so the driver cache misses. The numbers are in the description. If you'd rather have a specific trace or scene, tell me which and I'll capture it. The three traces I took are here, for completeness: https://github.com/tibzejoker/AnyPS5/releases/tag/perf-7673-7677-20261019

AI-assisted (Claude Code).

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@tibzejoker
tibzejoker force-pushed the perf/reuse-bitcast-source branch from 9232951 to 7e3b891 Compare October 19, 2026 05:06
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 19, 2026
@tibzejoker
tibzejoker force-pushed the perf/reuse-bitcast-source branch from 7e3b891 to 5a0dd8b Compare October 19, 2026 07:20
@github-actions github-actions Bot removed needs rebase Conflicts with main, rebase needed perf numbers missing perf pull request without a before and after summary line labels Oct 19, 2026
@adriadam10 adriadam10 self-assigned this Oct 19, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@tibzejoker
tibzejoker force-pushed the perf/reuse-bitcast-source branch from 5a0dd8b to c07b6bd Compare October 20, 2026 09:18
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 20, 2026
@DavidAmsellem

Copy link
Copy Markdown
Contributor

First Linux run of this PR, on AMD with RADV. Head c07b6bdc0 merged onto main 6bef96df7 (clean merge), built in full and compared with the same main, on the same machine.

AMD Radeon RX 7900 XT (RADV NAVI31, gfx1100), Mesa 25.2.8, Linux Mint 22.3 (Ubuntu 24.04 base), kernel 7.0.0-38, GCC 13.3, Release.

Full suite (ANYPS5_REQUIRE_VULKAN=1 ctest -j8) at 56990699d: 1713/1714 with this PR, against 1712/1713 on main. The one failure is the same on both, so this PR adds none: agc_driver_guest_memory_lifetime (#7784).

Comparing the JUnit output with main: the PR's new entry, agc_bitcast_round_trip, runs and passes. No other entry is added, and none is skipped or removed.

This checks correctness only; I did not measure performance.

GPUs tested: AMD Radeon RX 7900 XT (RADV NAVI31, gfx1100), Mesa 25.2.8, Linux Mint 22.3 / kernel 7.0.0-38

AI-assisted: yes

@tibzejoker

Copy link
Copy Markdown
Contributor Author

Thanks @DavidAmsellem for the RADV run on gfx1100: same single failure as main (#7784), and agc_bitcast_round_trip runs and passes.

AI-assisted: yes (Claude Code)

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 20, 2026
@tibzejoker
tibzejoker force-pushed the perf/reuse-bitcast-source branch 2 times, most recently from c07b6bd to 9c81eac Compare October 21, 2026 09:43
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 21, 2026
Registers are 32-bit words, so a float result is bitcast to u32 when it is
written and back to f32 when it is read: the emitted module carries pairs of
OpBitcast that return to the type of the first one's source. A bitcast back
to that type now returns the source itself, which dominates every use of the
first bitcast. The module notes the result type and the bitcast source of
each function instruction; Unary and EmitNative bitcasts look them up.

Test: agc_bitcast_round_trip recompiles v_cvt_f32_u32 results added with
v_add_f32 and requires no bitcast back to the type of a bitcast's source; it
fails on main (one of six) and passes with this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBXM4pgkMHYEtqN1EQni2P
@tibzejoker
tibzejoker force-pushed the perf/reuse-bitcast-source branch from 9c81eac to 9ff723b Compare October 25, 2026 18:23
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 25, 2026

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* build perf Performance change shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants