Repository navigation
perf(agc): replace the indexed 16-byte copy kernel with a buffer copy - #7968
Conversation
|
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.
|
MarcZX10
left a comment
There was a problem hiding this comment.
I reviewed the matcher and range checks. The HLE is restricted to the exact instruction sequence, descriptor layout, launch shape and record format; record visibility, pending labels, descriptor bounds, overlap and the 16 MiB cap are checked before the shared copy path runs. The test fixture also compares actual destination contents for both HLE and rejected-kernel cases. The performance report distinguishes the GPU-time reduction from the unchanged frame-time bottleneck. I did not independently run the Windows tests or title capture.
|
I reproduced a parameter-visibility failure in An address-based GPU dispatch writes Calling Minimal correction and standalone regression are available for reuse; that single commit adds the wait, its include and
Windows 11 10.0.26300, GCC 15.2.0 UCRT, Release, NVIDIA RTX 3060 Laptop GPU, driver 617.42. All runs used Prepared with AI assistance. |
|
Thanks for the reproduction and the fix. You are right: the record checks covered snapshot writes, pending storage results and labels, but not a deferred address-based batch that writes the record, and the indirect-dispatch path already waits for exactly that ( Your commit is on the branch as it is (c60f655, your authorship kept). On Windows 11 (10.0.26200), RTX 2060 SUPER, NVIDIA 617.42, GCC 15.2.0, with On the cost: |
|
Cost of the wait on Ghostrunner 2 (PPSA09911), RTX 2060 SUPER, NVIDIA 617.42, Windows 11 (10.0.26200): no measurable cost. Two runs of the same build (local base:
The recorder's totals over each run: 8,758 syncs waiting 17.1 s for the GPU without the wait, 7,822 syncs waiting 16.5 s with it (the |
c60f655 to
af7808a
Compare
a9ba4e4 to
7fa819b
Compare
Recognise the compute kernel that copies count 16-byte elements from a source V# to a destination V# at offsets read from a one-record constant buffer, and run it through the copy HLE like the other two copy kernels. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 1b2d6c5)
7fa819b to
17bc1bc
Compare
…/agc-indexed-copy-kernel # Conflicts: # core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/BufferCopy.cpp
|
Re-checked on current main |
|
GTA III PPSA03527/01.003.000 follow-up: the paired title capture finished after this PR merged. Both arms reach the first mission, with Claude stationary beside the Kuruma and blue marker at Callahan Bridge. The same position, camera and HUD were checked in renderer exports immediately before capture (04:18 vs 04:19). Pinned pre-merge main Same Windows 10.0.26300 machine, RTX 3060 Laptop GPU/NVIDIA 617.42, i5-11400H, 3840x2160. Each side ran the mission once, then restarted with its warm cache and the same private save/profile seed and scripted game-local input. No other TRACE/PROFILE switch and no redirected native log. Each capture covers presents 4560:20, with no dropped events or dirty source. All 61 loaded PRXs and the complete runtimes were verified; original game files/names stayed unchanged.
Limits: baseline varies substantially. An earlier 120-present warm run had p50 461.943 ms and GPU 403.451 ms/present, but overflowed its detailed trace; this prompted the explicit shorter window on both arms. Candidate's preceding mission warmup was p50 210.055 ms/GPU 96.156 ms. The direction is encouraging, but this pair does not establish a repeatable speed factor. The initial cold-current baseline also exited 0xC0000005 before the mission; warmed runs did not reproduce it and its cause remains unknown. Controls and save/resume were not tested here; 173 ms median remains far from smooth gameplay. The complete runtime pair passed the same 30 focused cases and 11 copy-byte comparisons with actual Khronos synchronization validation, no skips or validation errors and unchanged 30-second individual limits. The candidate's indexed-copy and GPU-parameter tests also passed. Linux and other GPU vendors were not run locally. Traces and full Raw summary lines{"commit":"ef6b0b59833a","dirty":false,"env":{"APS5_DUMP_FRAMES":"151","APS5_DUMP_FRAMES_EVERY":"30","APS5_DUMP_SCALE":"4","APS5_LAZY_SHADER_PREPARE":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_PIPELINE_JOURNAL_THREADS":"2","APS5_TRACE_FRAMES":"4560:20"},"gpu":"NVIDIA GeForce RTX 3060 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","cpu":"11th Gen Intel(R) Core(TM) i5-11400H @ 2.70GHz","cpu_threads":12,"memory_mib":32509,"title_id":"PPSA03527","title":"Grand Theft Auto III \u00e2\u0080\u0093 The Definitive Edition","content_version":"01.003.000","resolution":"3840x2160","window":{"spec":"frames 4560:20","first_present":4560,"ms":20135.541},"presents":20,"frame_ms":{"p50":999.354,"p90":1200.11,"p99":1242.261,"max":1242.261},"gpu_busy_ms_per_present":720.962,"worker_busy_pct":{"queue worker 0x0":99.424,"queue worker 0x20":97.097},"top_phases_ms_per_frame":{"Driver.Packet":1975.185,"Driver.Packet.memory_wait":954.715,"Driver.Packet.dispatch":551.048,"Driver.Dispatch":542.058,"Resources.Complete":534.656,"Vulkan.Dispatch":460.549,"Vulkan.Dispatch.shader_resources":458.31,"Graphics.Draw":391.282},"trace_dropped":false,"trace":"aps5-trace-ef6b0b59833a-22716.json.gz"}
{"commit":"29fe923626ce","dirty":false,"env":{"APS5_DUMP_FRAMES":"151","APS5_DUMP_FRAMES_EVERY":"30","APS5_DUMP_SCALE":"4","APS5_LAZY_SHADER_PREPARE":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_PIPELINE_JOURNAL_THREADS":"2","APS5_TRACE_FRAMES":"4560:20"},"gpu":"NVIDIA GeForce RTX 3060 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","cpu":"11th Gen Intel(R) Core(TM) i5-11400H @ 2.70GHz","cpu_threads":12,"memory_mib":32509,"title_id":"PPSA03527","title":"Grand Theft Auto III \u00e2\u0080\u0093 The Definitive Edition","content_version":"01.003.000","resolution":"3840x2160","window":{"spec":"frames 4560:20","first_present":4560,"ms":3471.663},"presents":20,"frame_ms":{"p50":172.67,"p90":194.293,"p99":217.448,"max":217.448},"gpu_busy_ms_per_present":98.501,"worker_busy_pct":{"queue worker 0x0":97.882,"queue worker 0x20":95.561},"top_phases_ms_per_frame":{"Driver.Packet":334.331,"Driver.Packet.memory_wait":155.688,"Graphics.Draw":83.613,"Graphics.Draw.draw_and_resource_release":66.759,"Driver.Packet.draw":64.743,"Driver.Draw":64.139,"Driver.Packet.device_lock":43.638,"Driver.Draw.shader_compile_and_link":38.733},"trace_dropped":false,"trace":"aps5-trace-29fe923626ce-2208.json.gz"}Prepared with AI assistance. |
What
Unreal Engine 4 titles copy GPU buffers with a compute kernel that moves 16-byte elements from a source V# to a destination V#, with the element count and the source and destination offsets in a one-record constant buffer. The copy HLE knew two other copy kernels (the hashed dword copy and the stream copy) but not this one, so it ran as a real dispatch: 64-thread groups, one 16-byte load and store per thread, over host-imported guest memory. On Ghostrunner 2 (PPSA09911) that was about 28 ms of GPU time per dispatch, four dispatches per frame.
MatchesIndexedCopyKernelrecognises the kernel by its 19 instruction words and the descriptors (stride 16, not swizzled, no add-tid, raw out-of-bounds mode, type 0, equal word 3 on source and destination, a record descriptor of stride 16 with at least one record).IndexedCopyRangereads{count, sourceOffset, destinationOffset}from the record, clamps the count to the launched threads, refuses windows past either descriptor's records, copies above 16 MiB and unaligned bases, andDriver::copyBufferthen takes the same path as the other kernels (the record is read only when nothing is pending over it, and the copy goes throughVulkanDevice::CopyBufferwith the existing CPU and GPU limits, overlap and label checks). The two dispatch call sites treat the kernel like the other copy kernels.Tested
Windows 11 (10.0.26200), MinGW-w64 GCC 15.2.0 UCRT, Release.
agc_driver_indexed_copy: the kernel matches; every one-word alteration, a truncated kernel, other strides, a swizzled descriptor, differing formats, add-tid, raw out-of-bounds, another descriptor type, an empty record descriptor, another user-SGPR count, 32-thread groups and a dispatch without the group id do not; range arithmetic with offsets, count below and above the launch, windows past the records, 0 elements, 0 groups, 16 MiB accepted and above refused, misaligned bases refused; eight dispatches over real memory, five taking the HLE path and three (windows past the records, above 16 MiB) running the kernel, all with the expected destination contents.agc_driver_indexed_copy_gpu_parameters(from eliasbruno124-dev's review): an address-based dispatch writes the copy's record in the same submission; the HLE now waits for leased writers (FinishLeaseWork, asreadDispatchArgumentsdoes) before reading it. Failed at destination word 16 before the wait, passes with it.agc_driver_stream_copy,agc_driver_indexed_copyandagc_driver_indexed_copy_gpu_parameterswithVK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ANYPS5_NO_SHADER_CACHE=1 ANYPS5_REQUIRE_VULKAN=1(ctest -V) onmain(stream copy only, the other two are new) and on this branch: all pass, the layer was found, noVUID-lines in either log.Performance, same machine, same session, warm caches, idle in-game scene (death screen),
APS5_TRACE_SECONDS=120:60, no other switches. The base ismain4b462ef plus #7687, becausemainalone stops this title before gameplay (#7017); this change does not depend on #7687.main + #7687 (db0cb30aa):
{"commit":"db0cb30aaaad","dirty":false,"env":{"APS5_PLAYGO_UNLISTED_CHUNKS":"installed","APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"120:60"},"gpu":"NVIDIA GeForce RTX 2060 SUPER","driver":"NVIDIA 617.42","os":"Windows 10.0.26200","resolution":"3840x2160","window":{"spec":"seconds 120:60","first_present":1166,"ms":60247.923},"presents":188,"frame_ms":{"p50":309.886,"p90":336.916,"p99":594.174,"max":595.994},"gpu_busy_ms_per_present":208.564,"worker_busy_pct":{"queue worker 0x0":98.983,"queue worker 0x20":99.495},"top_phases_ms_per_frame":{"Driver.Packet":632.832,"Driver.Packet.memory_wait":315.737,"Resources.Complete":148.403,"Graphics.Draw":143.937,"Graphics.Draw.draw_and_resource_release":125.999,"Driver.Packet.draw":124.129,"Driver.Draw":123.192,"Driver.Packet.dispatch":105.326},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":13.457,"events_lost":931329,"totals_full":false},"draw committer":{"events_full_after_s":28.593,"events_lost":290723,"totals_full":false}},"trace":"aps5-trace-db0cb30aaaad-10428.json.gz"}this change (7d798795d):
{"commit":"7d798795d78c","dirty":false,"env":{"APS5_PLAYGO_UNLISTED_CHUNKS":"installed","APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"120:60"},"gpu":"NVIDIA GeForce RTX 2060 SUPER","driver":"NVIDIA 617.42","os":"Windows 10.0.26200","resolution":"3840x2160","window":{"spec":"seconds 120:60","first_present":1246,"ms":60258.336},"presents":175,"frame_ms":{"p50":306.110,"p90":394.146,"p99":745.884,"max":4203.981},"gpu_busy_ms_per_present":96.262,"worker_busy_pct":{"queue worker 0x0":98.372,"queue worker 0x20":99.384},"top_phases_ms_per_frame":{"Driver.Packet":679.618,"Driver.Packet.memory_wait":336.187,"Graphics.Draw":160.820,"Driver.Packet.draw":149.034,"Driver.Draw":147.289,"Graphics.Draw.draw_and_resource_release":119.477,"Driver.Packet.device_lock":109.898,"Driver.Draw.shader_compile_and_link":78.745},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":9.388,"events_lost":1909697,"totals_full":false},"draw committer":{"events_full_after_s":24.118,"events_lost":418959,"totals_full":false}},"trace":"aps5-trace-7d798795d78c-13420.json.gz"}GPU busy per present 208.6 ms → 96.3 ms. The frame time does not move (p50 309.9 → 306.1 ms): both queue workers stay at 99 %, and the compute queue spends most of each frame in
Driver.Packet.memory_waiton labels the graphics queue writes, so this title is bound by that path, not by the GPU. The max of 4204 ms in the second capture is one shader compile at the start of the window (Driver.Draw.shader_compile_and_link). Both traces are attached; their event buffers filled after 13 s and 9 s of the window (trace_dropped), the summary counters are complete.Digest of each trace:
python3 tools/trace_digest.py <trace>output attached asdigest-base.txtanddigest-change.txt.tools/pr_overlap.pyreports no open pull request changing the same lines. It could not compare eight pull requests that conflict withmainand touch the same files: #6292 and #6615 change the type ofcountsMutexinBufferCopy.cpp; #5925, #7201, #7233, #7332, #7446 and #7642 change other parts ofDispatch.cpp. None of them touches the copy-kernel recognition.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code