Skip to content

perf(shader): reuse a memoized result by patching its copied words - #10423

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
meh7an:perf/shader-masked-result-memo
Oct 25, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
meh7an:perf/shader-masked-result-memo

Conversation

@meh7an

@meh7an meh7an commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

What

When the Recompile(request, capture) result memo misses, the draw's snapshot often differs from a cached one only in words that materializeResult copies into the result verbatim:

  • the base and record-count words of guest buffer V#s, which Populate writes into GuestBuffers bindings;
  • the user data, written into the push constants or the ShaderData binding;
  • the flattened SRT, written into the FlattenedSrt binding.

On Sonic Origins Plus's Sonic 2 menu page, the memo hits about 26 % of lookups on main, and every miss builds a whole result again. On that page the queue worker bounds the frame, and materializeDrawStage plus the release of the results it built take about half of its samples. With this PR, 72 % of the page's lookups become patched hits and only 1.5 % still miss.

This PR adds a second, masked table per source:

  • Its key is snapshotHash with each buffer's words 0-2 reduced to the class the binding plan already keys on (dwords[1] & 0xffff0000, dwords[3], and present plus alignment), and with the user data and the flattened SRT counted by their size only. Everything else in the key is unchanged.
  • On an exact miss, a masked hit copies the cached result and writes the current snapshot's words into it, at positions recorded from the binding plan when that result was first materialized (MemoPatch). poisonedSrtReads is recomputed from the snapshot.
  • A full miss materializes as before and is inserted into both tables. A patched result goes into the exact table as before.
  • A result whose user-data words could overlap the dispatch-limit, image-metadata or runtime-state words of its shader data is never inserted into the masked table.

Why the masked words are safe to patch: preparedBindingPlan keys only on the buffer classes above, ResolveImageTablesShared reads snapshot.tables, and Populate copies the masked words verbatim (GuestBuffersDescriptor, ShaderDataDwordsFor, the FlattenedSrt binding). Throws that depend on these words depend only on their sizes and classes, which stay in the key.

  • APS5_NO_MASKED_RESULT_MEMO=1 turns the masked table off.
  • APS5_VERIFY_MASKED_RESULT_MEMO=1 also materializes every patched hit in full and compares the bindings, push constants, specialization and module, printing the first twenty differences. Its count is in the [recompile] result memo line of APS5_PROFILE_DRAW, which now also shows the patched hits and the time per patch.
  • agc_shader_table_snapshot_reuse expects no table resolution for a patched result and checks that the result holds the moved buffer's descriptor. agc_shader_table_snapshot_reuse_unmasked runs the same test with the masked table off, so the shared-resolution path stays covered.

Related open PRs:

Tested

Windows 11 Pro 10.0.26300, NVIDIA GeForce RTX 2080 Ti, driver 617.14, i9-9900K, on main e7f46f5.

Sonic Origins Plus (PPSA05324), Sonic 2 page of the main menu, main's defaults (no APS5_* opt-ins).

  • Three interleaved traced rounds per build, APS5_TRACE_SECONDS=200:15, after one warm-up boot each:
frame p50 (ms) GPU busy per present (ms) Driver.Packet (ms/frame) Driver.Draw.shader_compile_and_link (ms/frame)
main 114.8 / 82.4 / 80.4 67.9 / 68.1 / 68.1 111.9 / 85.1 / 80.2 57.6 / 41.1 / 41.2
this PR 105.0 / 77.3 / 76.7 67.7 / 67.5 / 67.8 102.6 / 78.9 / 78.8 44.8 / 36.3 / 36.0
  • The median frame p50 goes from 82.4 to 77.3 ms (-6.2 %), and each round is faster than its main round. The queue worker is about 97 % busy in both, so the page stays bound by it. The title's own FPS reading stays within 12.8-13.3 for both.
  • APS5_VERIFY_MASKED_RESULT_MEMO=1 with APS5_PROFILE_DRAW=1 on the same page: 0 mismatches. Per 10 s: 112,397 exact hits (26.2 %), 310,079 patched (72.3 %), 6,411 misses (1.5 %).
  • Full window captures of every reading render the same scene on both builds. The button-prompt glyph damage in both comes from perf(agc): skip the staging, write-back and label-run leading barriers a covering barrier already orders #7489, fixed by fix(agc): keep the write-back ready barrier while hole copies are pending #8908, which merged after this base.
  • ctest (2063 tests) on main plus this PR: everything passes except agc_driver_sample_order, agc_driver_sample_order_grid_reference and agc_driver_sample_order_recipe_depth_locations, which fail the same way on plain main on this machine. The same suite under APS5_VERIFY_MASKED_RESULT_MEMO=1 gives the same result with 0 mismatches.
  • ctest --test-dir build -R "^agc_shader_|^agc_driver_draw_cache" -V with ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1 and VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation: 48/48 on main, 49/49 on this PR (the extra one is agc_shader_table_snapshot_reuse_unmasked). Neither log has was not found but was requested by env var VK_INSTANCE_LAYERS, and neither has a VUID- line.

The two summaries and their digests (the median round of each build; all six traces are attached):

main

seconds 200:15 from present 3460: 172 presents, frame p50 82.4 / p90 107.9 / p99 125.1 / max 127.7 ms, GPU busy 68.1 ms per present
NVIDIA GeForce RTX 2080 Ti, NVIDIA 617.14, Windows 10.0.26300, 3840x2160, commit e7f46f5, dropped: yes

{"commit":"e7f46f5c8937","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"200:15"},"gpu":"NVIDIA GeForce RTX 2080 Ti","driver":"NVIDIA 617.14","os":"Windows 10.0.26300","cpu":"Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz","cpu_threads":16,"memory_mib":32684,"title_id":"PPSA05324","title":"Sonic Origins","content_version":"02.002.000","resolution":"3840x2160","window":{"spec":"seconds 200:15","first_present":3460,"ms":15052.949},"presents":172,"frame_ms":{"p50":82.435,"p90":107.941,"p99":125.124,"max":127.668},"gpu_busy_ms_per_present":68.113,"worker_busy_pct":{"queue worker 0x0":97.328},"top_phases_ms_per_frame":{"Driver.Packet":85.107,"Driver.Packet.draw":66.837,"Driver.Draw":65.806,"Graphics.Draw":65.247,"Graphics.Draw.draw_and_resource_release":54.844,"Driver.Draw.shader_compile_and_link":41.123,"Shader.DrawStage":30.566,"Driver.Present":27.467},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":6.007,"events_lost":392686,"totals_full":false},"draw committer":{"events_full_after_s":13.602,"events_lost":28410,"totals_full":false}},"trace":"aps5-trace-e7f46f5c8937-6396.json.gz"}

Slowest frames (largest slices inside each, inclusive)

  • present 3620: 127.7 ms at 13985 ms: batch (gpu) 67.4, VideoOut.Flip (presenter) 27.1, VideoOut.Flip.present (presenter) 27.0, Driver.Present (presenter) 27.0
  • present 3548: 125.1 ms at 7675 ms: Graphics.Draw (draw committer) 86.2, batch (gpu) 67.5, Graphics.Draw.draw_and_resource_release (draw committer) 58.3, VideoOut.Flip (presenter) 27.5
  • present 3562: 117.3 ms at 8944 ms: Graphics.Draw (draw committer) 75.2, batch (gpu) 66.8, Graphics.Draw.draw_and_resource_release (draw committer) 57.1, Recorder.Sync (draw committer) 29.6
  • present 3490: 117.1 ms at 2630 ms: Driver.Packet (queue worker 0x0) 80.4, Graphics.Draw (draw committer) 69.9, Driver.Packet.draw (queue worker 0x0) 69.8, batch (gpu) 69.2
  • present 3578: 113.9 ms at 10345 ms: batch (gpu) 66.8, Graphics.Draw (draw committer) 62.4, Graphics.Draw.draw_and_resource_release (draw committer) 56.4, VideoOut.Flip (presenter) 26.4

Threads (ms per frame)

thread busy wait untraced in slices < 20 us largest
title 1.4 (2 %) 0.0 (0 %) 86.2 0.7 Shader.ResolveGraphicsStagesAbi 0.8, Shader.ResolveAbi 0.5
queue worker 0x0 18.8 (21 %) 0.0 (0 %) 68.7 78.9 Driver.Packet 18.8, Driver.EndSubmission 0.0
draw committer 37.2 (43 %) 0.5 (1 %) 49.8 51.9 Graphics.Draw 35.5, Recorder.Submit 1.2
title 0.3 (0 %) 0.0 (0 %) 87.2 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.1
title 0.4 (0 %) 0.0 (0 %) 87.1 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 87.1 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
presenter 0.7 (1 %) 27.0 (31 %) 59.9 0.0 VideoOut.Flip 27.4, Driver.Present 0.2
title 0.4 (0 %) 0.0 (0 %) 87.1 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 87.1 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.3 (0 %) 0.0 (0 %) 87.2 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.1
title 0.3 (0 %) 0.0 (0 %) 87.2 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 87.1 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.5 (1 %) 0.0 (0 %) 87.0 0.1 Shader.ResolveGraphicsStagesAbi 0.3, Shader.ResolveAbi 0.2
title 0.3 (0 %) 0.0 (0 %) 87.2 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.1
title 0.4 (0 %) 0.0 (0 %) 87.1 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (1 %) 0.0 (0 %) 87.1 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
gpu 68.0 (78 %) 0.0 (0 %) 19.5 0.0 batch 68.0

Largest slices (ms per frame, inclusive, all threads): batch 68.0, Graphics.Draw 35.5, Graphics.Draw.draw_and_resource_release 29.8, Driver.Present 27.5, VideoOut.Flip 27.4, VideoOut.Flip.present 27.3, Driver.Present.render_fence_wait 26.9, Vulkan.Retire 26.9, Vulkan.Retire.wait 26.9, Driver.Packet 18.8

Longest waits

  • Driver.Present.render_fence_wait (presenter) 32.9 ms at 1 ms, present 3460
  • Driver.Present.render_fence_wait (presenter) 30.8 ms at 257 ms, present 3463
  • Driver.Present.render_fence_wait (presenter) 30.4 ms at 2909 ms, present 3493
  • Driver.Present.render_fence_wait (presenter) 30.3 ms at 5558 ms, present 3524
  • Driver.Present.render_fence_wait (presenter) 30.1 ms at 5476 ms, present 3523
  • Driver.Present.render_fence_wait (presenter) 30.1 ms at 5637 ms, present 3525
  • Driver.Present.render_fence_wait (presenter) 30.0 ms at 3878 ms, present 3504
  • Driver.Present.render_fence_wait (presenter) 29.8 ms at 2829 ms, present 3492
  • Driver.Present.render_fence_wait (presenter) 29.8 ms at 1409 ms, present 3476
  • Driver.Present.render_fence_wait (presenter) 29.6 ms at 1497 ms, present 3477

Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)

  • batch 774: 13.2 ms at 970 ms, present 3470: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 1.2, Driver.Packet.dispatch (queue worker 0x0) 0.6, Driver.Dispatch (queue worker 0x0) 0.5, Driver.Packet.pipeline_drain (queue worker 0x0) 0.4
  • batch 1916: 13.1 ms at 2369 ms, present 3486: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 1.1, Driver.Packet.dispatch (queue worker 0x0) 0.6, Driver.Dispatch (queue worker 0x0) 0.5, Vulkan.Dispatch (queue worker 0x0) 0.3
  • batch 8438: 12.9 ms at 10345 ms, present 3577: GPU no ranges; recorded by no CPU slices
  • batch 9297: 12.9 ms at 11381 ms, present 3589: GPU no ranges; recorded by no CPU slices
  • batch 2132: 12.8 ms at 2628 ms, present 3489: GPU no ranges; recorded by Graphics.Draw (draw committer) 0.9, Graphics.Draw.draw_and_resource_release (draw committer) 0.9, Resources.Prepare (draw committer) 0.1, Recorder.Submit (draw committer) 0.1

this PR

seconds 200:15 from present 3481: 185 presents, frame p50 77.3 / p90 95.0 / p99 112.3 / max 120.0 ms, GPU busy 67.5 ms per present
NVIDIA GeForce RTX 2080 Ti, NVIDIA 617.14, Windows 10.0.26300, 3840x2160, commit 54d7c78, dropped: yes

{"commit":"54d7c780a583","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"200:15"},"gpu":"NVIDIA GeForce RTX 2080 Ti","driver":"NVIDIA 617.14","os":"Windows 10.0.26300","cpu":"Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz","cpu_threads":16,"memory_mib":32684,"title_id":"PPSA05324","title":"Sonic Origins","content_version":"02.002.000","resolution":"3840x2160","window":{"spec":"seconds 200:15","first_present":3481,"ms":15037.835},"presents":185,"frame_ms":{"p50":77.32,"p90":94.965,"p99":112.311,"max":119.978},"gpu_busy_ms_per_present":67.469,"worker_busy_pct":{"queue worker 0x0":97.129},"top_phases_ms_per_frame":{"Driver.Packet":78.867,"Graphics.Draw":62.354,"Driver.Packet.draw":60.497,"Driver.Draw":59.489,"Graphics.Draw.draw_and_resource_release":52.254,"Driver.Draw.shader_compile_and_link":36.326,"Driver.Present":27.46,"VideoOut.Flip":27.373},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":6.274,"events_lost":374602,"totals_full":false},"draw committer":{"events_full_after_s":12.901,"events_lost":43909,"totals_full":false}},"trace":"aps5-trace-54d7c780a583-32184.json.gz"}

Slowest frames (largest slices inside each, inclusive)

  • present 3501: 120.0 ms at 1621 ms: Driver.Packet (queue worker 0x0) 91.5, Graphics.Draw (draw committer) 86.4, Driver.Packet.draw (queue worker 0x0) 81.1, Driver.Draw (queue worker 0x0) 80.1
  • present 3519: 112.3 ms at 3061 ms: Driver.Packet (queue worker 0x0) 75.5, batch (gpu) 70.9, Graphics.Draw (draw committer) 65.1, Driver.Packet.draw (queue worker 0x0) 65.1
  • present 3585: 111.7 ms at 8399 ms: batch (gpu) 68.6, Graphics.Draw (draw committer) 62.9, Graphics.Draw.draw_and_resource_release (draw committer) 58.0, VideoOut.Flip (presenter) 27.9
  • present 3611: 110.9 ms at 10539 ms: batch (gpu) 68.9, Graphics.Draw (draw committer) 65.3, Graphics.Draw.draw_and_resource_release (draw committer) 58.9, VideoOut.Flip (presenter) 28.2
  • present 3601: 109.6 ms at 9703 ms: batch (gpu) 68.5, Graphics.Draw (draw committer) 62.7, Graphics.Draw.draw_and_resource_release (draw committer) 58.3, VideoOut.Flip (presenter) 27.9

Threads (ms per frame)

thread busy wait untraced in slices < 20 us largest
title 1.3 (2 %) 0.0 (0 %) 79.9 0.7 Shader.ResolveGraphicsStagesAbi 0.8, Shader.ResolveAbi 0.5
queue worker 0x0 17.5 (22 %) 0.0 (0 %) 63.8 77.1 Driver.Packet 17.5, Driver.EndSubmission 0.0
draw committer 33.0 (41 %) 0.4 (1 %) 47.9 48.3 Graphics.Draw 31.6, Recorder.Submit 1.0
title 0.3 (0 %) 0.0 (0 %) 80.9 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 80.9 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 80.9 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
presenter 0.6 (1 %) 27.0 (33 %) 53.7 0.1 VideoOut.Flip 27.4, Driver.Present 0.2
title 0.3 (0 %) 0.0 (0 %) 81.0 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.1
title 0.3 (0 %) 0.0 (0 %) 81.0 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.1
title 0.4 (1 %) 0.0 (0 %) 80.9 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 80.9 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (1 %) 0.0 (0 %) 80.9 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (1 %) 0.0 (0 %) 80.8 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 80.9 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (1 %) 0.0 (0 %) 80.9 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 80.9 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
gpu 67.8 (83 %) 0.0 (0 %) 13.5 0.0 batch 67.8

Largest slices (ms per frame, inclusive, all threads): batch 67.8, Graphics.Draw 31.6, Driver.Present 27.5, VideoOut.Flip 27.4, VideoOut.Flip.present 27.3, Driver.Present.render_fence_wait 26.9, Vulkan.Retire 26.9, Vulkan.Retire.wait 26.9, Graphics.Draw.draw_and_resource_release 26.7, Driver.Packet 17.5

Longest waits

  • Driver.Present.render_fence_wait (presenter) 33.4 ms at 1 ms, present 3481
  • Driver.Present.render_fence_wait (presenter) 31.8 ms at 2751 ms, present 3515
  • Driver.Present.render_fence_wait (presenter) 30.9 ms at 2825 ms, present 3516
  • Driver.Present.render_fence_wait (presenter) 30.3 ms at 4213 ms, present 3533
  • Driver.Present.render_fence_wait (presenter) 30.2 ms at 4290 ms, present 3534
  • Driver.Present.render_fence_wait (presenter) 30.1 ms at 1622 ms, present 3501
  • Driver.Present.render_fence_wait (presenter) 30.1 ms at 410 ms, present 3486
  • Driver.Present.render_fence_wait (presenter) 30.0 ms at 14879 ms, present 3664
  • Driver.Present.render_fence_wait (presenter) 29.9 ms at 4134 ms, present 3532
  • Driver.Present.render_fence_wait (presenter) 29.8 ms at 1742 ms, present 3502

Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)

  • batch 5180: 13.5 ms at 5908 ms, present 3553: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 1.1, Driver.Packet.dispatch (queue worker 0x0) 0.6, Driver.Dispatch (queue worker 0x0) 0.5, Vulkan.Dispatch (queue worker 0x0) 0.3
  • batch 10301: 13.3 ms at 11789 ms, present 3625: GPU no ranges; recorded by no CPU slices
  • batch 9587: 13.3 ms at 10961 ms, present 3615: GPU no ranges; recorded by no CPU slices
  • batch 344: 13.2 ms at 409 ms, present 3485: GPU no ranges; recorded by Graphics.Draw (draw committer) 0.9, Graphics.Draw.draw_and_resource_release (draw committer) 0.9, Recorder.Submit (draw committer) 0.1, Resources.Prepare (draw committer) 0.1
  • batch 11153: 13.2 ms at 12729 ms, present 3637: GPU no ranges; recorded by Graphics.Draw (draw committer) 1.0, Graphics.Draw.draw_and_resource_release (draw committer) 1.0, Resources.Prepare (draw committer) 0.1, Resources.Complete (draw committer) 0.1

Checklist

Traces, one zip per round, each holding the main and the PR capture:

masked-memo-traces-round1.zip
masked-memo-traces-round2.zip
masked-memo-traces-round3.zip

@github-actions

github-actions Bot commented Oct 24, 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 L2486
core/shader/recompiler/Recompiler.cpp L389-390, L1011, L1396, L1414-1418

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
docs/dev/ENVIRONMENT.md
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 5609 core/shader/recompiler/Recompiler.cpp
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/libs/prx/libSceAgcDriver/tests/ShaderMemory.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
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6488 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
PR 6539 core/shader/recompiler/Recompiler.cpp
PR 6642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6858 core/shader/recompiler/Recompiler.cpp
PR 6924 core/shader/recompiler/Recompiler.cpp
docs/dev/ENVIRONMENT.md
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
PR 7275 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7410 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
PR 7449 docs/dev/ENVIRONMENT.md
PR 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 7514 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
docs/dev/ENVIRONMENT.md
PR 7677 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7684 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 8004 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
docs/dev/ENVIRONMENT.md
PR 8014 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8077 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/ENVIRONMENT.md
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8874 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8896 docs/dev/ENVIRONMENT.md
PR 9020 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
PR 9248 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 9335 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
PR 9345 docs/dev/ENVIRONMENT.md
PR 9356 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
docs/dev/ENVIRONMENT.md
PR 9385 docs/dev/ENVIRONMENT.md
PR 9387 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
docs/dev/ENVIRONMENT.md
PR 9400 docs/dev/ENVIRONMENT.md
PR 9669 docs/dev/ENVIRONMENT.md
PR 9676 docs/dev/ENVIRONMENT.md
PR 9700 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9748 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
PR 9889 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 9892 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9897 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 9898 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 9940 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10026 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10159 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10193 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
docs/dev/ENVIRONMENT.md
PR 10233 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 10333 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10442 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 10443 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 10750 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md

@github-actions github-actions Bot added build shader core/shader docs agc libSceAgc* labels Oct 24, 2026
@adriadam10 adriadam10 self-assigned this Oct 24, 2026

@imellag imellag 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 read the change against the code it relies on and found no case where a patched result can differ from a full one:

  • The masked key keeps the same buffer words as preparedBindingPlan's key (dwordCount, dwords[1] & 0xffff0000, dwords[3], present plus alignment), and GuestBuffersDescriptor copies words 0 to 3 as they are, so the plan, the module and the binding shape cannot change between donor and patched result.
  • ShaderDataDwordsFor puts the user words at 0 to n-1, and the dispatch limit, the image metadata and the runtime state all start at DispatchThreadLimitDword() or later, so userDataRegisters.size() <= DispatchThreadLimitDword() covers the three overlaps the description lists. The memory offset words before it are not written by Populate.
  • Push data and a ShaderData binding never coexist (Populate fails on that), so patching only one of them is enough.
  • partialThreads(request) is in both hashes, mergeTargetImage only appends the target image, and the SPIR-V is a SharedSpirv, so copying the donor does not copy the module.

macOS data, Apple M4 Pro, macOS 27.0.1, MoltenVK 1.4.2, x86-64 Release under Rosetta 2, on a local stack based on main 8a85a5c848 with #8658's macOS port, with this PR's commit on top:

  • The 47 tests run by agc_shader_memory_tests and agc_driver_graphics_tests, with APS5_VERIFY_MASKED_RESULT_MEMO=1: no masked result memo mismatch line. agc_shader_table_snapshot_reuse and agc_shader_table_snapshot_reuse_unmasked pass with the masked memo on and off. Two other tests fail on my stack, identically with APS5_NO_MASKED_RESULT_MEMO=1, because of local patches unrelated to this PR.
  • Stray (PPSA02100) in gameplay, APS5_PROFILE_DRAW=1, 10 s windows, the same build with and without APS5_NO_MASKED_RESULT_MEMO=1: 72,000 to 89,000 misses become 24,000 to 27,000 misses plus 76,000 to 85,000 patched hits (61 % of lookups; exact hits stay at about 17.5 %). The materialization time falls from 364 to 426 ms to 125 to 184 ms, and the patching takes 228 to 276 ms (about 3 µs per patched hit, against about 5 µs per miss there), so on this scene the queue worker saves little, and the frame is bound by the GPU, so frame rate did not change measurably. I did not run the verifier in the game.

On the two open PRs that change the same memo, both of which conflict with this one in Recompiler.cpp, there are two interactions beyond the rebase:

  • #9049 recycles the least recently used exact entry only when the memo holds its only reference (victim->result.use_count() == 1). Here a full miss inserts the same result into memo and, when its patch is valid, into maskedMemo, so that victim usually still has a reference from the masked table and is not recycled. The two may add up less than expected unless the recycling also looks at the masked table.
  • #9989 bounds the bytes the result memos hold and trims their entries. A result that only maskedMemo still references would stay alive outside that budget unless the masked table is counted and trimmed too.

AI-assisted: yes

@adriadam10
adriadam10 merged commit 75227c7 into boykopovar:main Oct 25, 2026
15 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Re-checked against current main: the merge is clean and main's changes in the PR's files (plan loading in getSource, float32DenormPreserve, an unrelated ShaderMemory test, CMake and ENVIRONMENT.md entries) are far from the PR's hunks. The masked key still keeps everything the binding plan reads, agc_shader_table_snapshot_reuse fails on main with the new expectations and _unmasked keeps the old path covered; merged.

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

Labels

agc libSceAgc* build docs perf Performance change shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants