Skip to content

perf(agc): cut per-draw heap allocations - #6312

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
meh7an:perf/agc-cut-draw-allocations
Oct 19, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
meh7an:perf/agc-cut-draw-allocations

Conversation

@meh7an

@meh7an meh7an commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

What

A draw allocated several short-lived lists and copies on the heap. On the S2 page of Sonic Origins Plus's main menu, operator new cost the queue worker 5.56 samples per frame (APS5 sampler, 15 s). Among its callers:

  • Driver::draw: a copy of the decode's programs, and lists grown one push at a time;
  • MovedReadOnlyBuffers: the moved words, copied per entry;
  • the program decode: user data grown word by word;
  • CachedFragmentOutputs and KnownValidationFailure: a fresh validation key each, and the output set copied out of the memo;
  • DecodeVertexStageInfo: a vector per guest read;
  • ShaderMemory: a map node per initial region;
  • the resource materializer's lists.

This change removes or reuses those allocations:

  • Driver::draw reads the decode's programs in place. Only a draw that patches user words works on a copy: a mesh draw, an indirect draw, or a cached draw found across moved user-data pointers (perf(agc): find cached draws across moved user-data pointers #4943). The region, link and snapshot lists are reserved once.
  • A draw's fragment outputs are a mask with a bit per color target instead of a std::set, in the memo, the recipe and the draw's inputs.
  • The validation key is built in a per-thread buffer (ThreadOwned), shared by KnownValidationFailure and CachedFragmentOutputs.
  • MovedBuffer::words is a span into the compiled stage's guest descriptor instead of a copy. That descriptor outlives PrepareDrawBindings, the only reader. The moved-word cache copies the words it keeps.
  • DecodeRead keeps its 4 or 16 bytes inline.
  • ShaderMemory keeps its initial regions in a sorted vector instead of a map.
  • The program decode reserves the user data, including the 8 words a merged stage inserts.
  • MovedReadOnlyBuffers and PrepareDrawBindings reserve their lists with the first entry.
  • The resource materializer keeps its value and active-source lists per thread (HostThreadLocal) and refills them.
  • Two tests keep the moved words alive for the span: drawDescriptorWriteTests and descriptorSnapshotTests.

With this change, per frame:

  • operator new 5.56 to 4.77, malloc 6.40 to 5.51;
  • KnownValidationFailure 0.23 to 0.06, CachedFragmentOutputs 0.38 to 0.27;
  • MovedReadOnlyBuffers 1.05 to 0.72, and the program decode 1.09 to 0.88.

Tested

Windows 11 (10.0.26300), WinLibs GCC 15.2.0 (UCRT), Release, NVIDIA GeForce RTX 2080 Ti, driver 617.14.

  • Rebased onto main ea4cc11. perf(agc): find cached draws across moved user-data pointers #4943 now patches a relocated draw's user words into its programs, so such a draw also works on a copy. On that main, with this change, the full ctest suite passes 1337 of 1339. The one not run is agc_driver_gpu_indirect_arguments_misaligned_writer_ahead, which main itself does not compile with MinGW. select_tests fails in its setup on Windows without the symlink privilege (WinError 1314 from symlink_to), as on main.
  • On main 8856f18, two snapshot tests of agc_driver_recorder_tests's unregistered default path, resourceReadTests and misalignedRegionTests, failed on main itself, with or without this change.
  • Sonic Origins Plus, main menu, S2 page (the 3D background), 1233x694 window.
    • A sampled pair (APS5 sampler, 15 s) took the profile numbers above. The FPS shown in the title went from 8.28 to 8.45 (median of six readings).
    • Captured with APS5_TRACE_SECONDS=200:15 on the same machine in one session, caches warm. Traces attached.
    • Baseline 824084e, this change 96f4f98 (the summaries name the commits before them, see the note):
Metric Baseline This PR Change
frame_ms.p50 131.150 127.308 -2.9%
frame_ms.p90 160.761 138.635 -13.8%
mean_frame_ms 136.487 130.125 -4.7%
gpu_busy_ms_per_present 84.783 86.914 +2.5%
Driver.Packet (ms per frame) 134.278 127.944 -4.7%
Driver.Draw (ms per frame) 122.212 115.489 -5.5%

The p90 gap mostly comes from slower frames in the baseline's window. The median and Driver.Draw are the steadier numbers.

Note: measuring base. These numbers come from my fork's dev, on main 0a56aaa, the last main before #5596. On Windows, later mains compare direct imports on every use, which keeps this menu at 3-4 FPS and hides a change of this size.

Both carry these commits from my fork:

Two differences from this PR:

The first sampled run of this change caught a burst of buffer fills (Driver::fillBuffer, 8.4 samples per frame against 0.15 in the baseline). Neither this change nor the code it touches makes those fills. I ran the change again: fillBuffer was back at 0.16, and the numbers above are from that run.

The comparison tool marks the window's first present (2422 vs 2360) as a configuration difference: the window opens 200 s into each run. Both summaries set trace_dropped, because the worker filled its event buffer at about 2,700 draws per frame. The phase totals above stay complete.

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
  • Unblocks: none
  • GPUs tested: NVIDIA GeForce RTX 2080 Ti, driver 617.14, Windows 11
  • AI-assisted: yes

Traces and summary lines (baseline 824084e, this change 96f4f98):
alloc-traces.zip

@github-actions

github-actions Bot commented Oct 15, 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 6523 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp L719

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 907 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 1191 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 1341 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 1368 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 1395 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 4040 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 4072 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 4650 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5021 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 5371 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 5404 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
PR 5431 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5441 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5499 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5500 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5609 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6177 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6185 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6186 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6188 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
core/libs/prx/libSceAgcDriver/Execution/src/ShaderMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6192 core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6194 core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6202 core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6223 core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6235 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6280 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 6451 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6472 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6481 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6486 core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6488 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 6498 core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6529 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6530 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6535 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6539 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6548 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6563 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6571 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6606 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6612 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6639 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6679 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
PR 6683 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6718 core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6747 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6759 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6762 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6763 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6801 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6808 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6820 core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6868 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6931 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
PR 6937 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6941 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
PR 6946 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
core/libs/prx/libSceAgcDriver/Execution/src/ShaderMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6947 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
core/libs/prx/libSceAgcDriver/Execution/src/ShaderMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 6959 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6971 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6977 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 7154 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 7201 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
PR 7223 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 7254 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7256 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7258 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7332 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7390 core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 7395 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 7410 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7505 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7550 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 7604 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCapture.cpp
core/libs/prx/libSceAgcDriver/Execution/src/ShaderMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp

@github-actions github-actions Bot added shader core/shader agc libSceAgc* labels Oct 15, 2026
This was referenced Oct 15, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 16, 2026
@meh7an
meh7an force-pushed the perf/agc-cut-draw-allocations branch 2 times, most recently from 144cca6 to ecda2b1 Compare October 16, 2026 20:12
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 16, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 18, 2026
@adriadam10 adriadam10 self-assigned this Oct 18, 2026
@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
@adriadam10
adriadam10 force-pushed the perf/agc-cut-draw-allocations branch from ecda2b1 to 941b158 Compare October 19, 2026 01:01
@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 removed the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@adriadam10
adriadam10 merged commit 72b0ab3 into boykopovar:main Oct 19, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* perf numbers missing perf pull request without a before and after summary line perf Performance change shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants