Repository navigation
Description
Title and scene
PPSA01748 (01.006.000), the main menu after the intro, presents 1150–1190, no input. The same pattern holds in the first gameplay scene, Star-Lord's quarters on the Milano, at about 1 FPS (figures below).
What is slow
The menu runs at a p50 of 587 ms per frame, while the GPU is busy for only 134 ms per present. Both queue workers are saturated:
- queue worker 0x0 (graphics): busy 78% (482 ms per frame), with 153 ms per frame in slices under 20 µs.
- queue worker 0x23 (compute): waits 92% of the time, almost all in
Driver.Packet.memory_wait. Single waits last up to 477 ms.
Build: main e13027a with #9694 and #10388 applied (local commit abe0d257a). Neither touches the paths below: #9694 changes one coverage check in a metadata pass the menu does not reach, and #10388 only sizes the startup prebuild pool. RTX 4090, NVIDIA 615.78.08, Linux 7.3, i9-14900K.
A separate run of the same build with APS5_PROFILE_DRAW=1, so these numbers carry profiling overhead and are not part of the capture below, breaks the 10-second [packets] lines down by packet type:
| Scene | Queue | Main packets (10 s) |
|---|---|---|
| menu | 0x0 | DRAW_INDEX_INDIRECT_MULTI ×8,196 5,630 ms, DISPATCH_DIRECT ×2,754 1,912 ms, WAIT_REG_MEM_64 ×25 405 ms |
| menu | 0x23 | WAIT_REG_MEM_64 ×345 7,891 ms, DISPATCH_DIRECT ×554 1,989 ms |
| Star-Lord's quarters | 0x0 | DRAW_INDEX_INDIRECT_MULTI ×7,881 4,800 ms, DISPATCH_DIRECT ×2,259 2,663 ms, DMA_DATA ×3,063 632 ms |
| Star-Lord's quarters | 0x23 | WAIT_REG_MEM_64 ×160 8,491 ms, DISPATCH_DIRECT ×301 1,416 ms |
In the quarters, the same run also reports:
[indirect]: 9,842 GPU-side indirect draws and 0 CPU-side, so no indirect arguments are read on the CPU.[draw] driver phases: 10,440 draw packets in 10 s at about 501 µs each, of which capture is 96 µs,Graphics::Draw294 µs, recompile 37 µs and vectors 38 µs.[draws]: about 59,000 draws, 33,204 of them address-based builds, with 0.7–1.0 s in total in their resources.[draw] pipelined: 3,196 packet drains costing 997 ms, by opcode 0x50 ×1,588 and 0x15 ×1,449.[packets]for 0x23 in the menu: of its waits, 1 was satisfied at entry, 280 from the recorder (225 of them "same queue"), and 16 were polled.
I haven't identified which writer the compute queue's waits depend on. If there's a switch that names the awaited label's producer, I can run it.
Summary lines
{"commit":"abe0d257ac4c","dirty":false,"env":{"APS5_TRACE_FRAMES":"1150:40"},"gpu":"NVIDIA GeForce RTX 4090","driver":"NVIDIA 615.78.08","os":"Linux 7.3.0-3-cachyos","cpu":"Intel(R) Core(TM) i9-14900K","cpu_threads":32,"memory_mib":64108,"title_id":"PPSA01748","title":"Marvel's Guardians of the Galaxy","content_version":"01.006.000","resolution":"3840x2160","window":{"spec":"frames 1150:40","first_present":1150,"ms":24540.029},"presents":40,"frame_ms":{"p50":586.747,"p90":771.370,"p99":897.050,"max":897.050},"gpu_busy_ms_per_present":133.690,"worker_busy_pct":{"queue worker 0x0":99.518,"queue worker 0x23":99.226},"top_phases_ms_per_frame":{"Driver.Packet":1213.861,"Driver.Packet.memory_wait":517.411,"Driver.Packet.draw":321.244,"Driver.Draw":321.059,"Driver.Packet.dispatch":269.560,"Driver.Dispatch":241.176,"Graphics.Draw":228.388,"Graphics.Draw.draw_and_resource_release":193.724},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":24.424,"events_lost":1659,"totals_full":false}},"trace":"aps5-trace-abe0d257ac4c-2.json.gz"}Traces
aps5-trace-abe0d257ac4c-2.json.gz is attached in the first comment.
The window ran out of event space on queue worker 0x0 in its last 0.1 s (events_lost 1,659), so the final frame's tail is missing on that thread.
Digest
39 frames over 23952.7 ms (no summary line found)
**Slowest frames** (largest slices inside each, inclusive)
- present 1171: 897.0 ms at 12581 ms: Driver.Packet (queue worker 0x23) 896.6, Driver.Packet (queue worker 0x0) 884.5, Driver.Packet.memory_wait (queue worker 0x23) 787.3, Driver.Packet.draw (queue worker 0x0) 595.7
- present 1167: 860.7 ms at 10177 ms: Driver.Packet (queue worker 0x23) 856.6, Driver.Packet (queue worker 0x0) 847.6, Driver.Packet.memory_wait (queue worker 0x23) 739.3, Driver.Packet.draw (queue worker 0x0) 548.3
- present 1166: 772.7 ms at 9404 ms: Driver.Packet (queue worker 0x23) 771.6, Driver.Packet (queue worker 0x0) 760.0, Driver.Packet.memory_wait (queue worker 0x23) 605.7, Driver.Packet.draw (queue worker 0x0) 461.5
- present 1176: 771.4 ms at 16029 ms: Driver.Packet (queue worker 0x0) 758.7, Driver.Packet (queue worker 0x23) 748.5, Driver.Packet.memory_wait (queue worker 0x23) 654.8, Driver.Packet.draw (queue worker 0x0) 483.0
- present 1165: 768.1 ms at 8636 ms: Driver.Packet (queue worker 0x23) 767.5, Driver.Packet (queue worker 0x0) 755.5, Driver.Packet.memory_wait (queue worker 0x23) 621.5, Driver.Packet.draw (queue worker 0x0) 449.6
**Threads** (ms per frame)
| thread | busy | wait | untraced | in slices < 20 us | largest |
|---|---|---|---|---|---|
| title | 1.6 (0 %) | 11.3 (2 %) | 601.2 | 0.0 | Recorder.Sync 0.9, Recorder.Submit 0.8 |
| title | 10.5 (2 %) | 30.3 (5 %) | 573.4 | 1.5 | Recorder.Sync 3.7, Shader.ResolveAbi 3.2 |
| queue worker 0x0 | 481.8 (78 %) | 121.3 (20 %) | 11.1 | 152.6 | Driver.Packet 601.5, Driver.EndSubmission 0.0 |
| draw committer | 73.9 (12 %) | 53.7 (9 %) | 486.5 | 6.1 | Graphics.Draw 69.3, Recorder.Submit 4.6 |
| presenter | 0.3 (0 %) | 0.2 (0 %) | 613.6 | 0.1 | VideoOut.Flip 0.5, Driver.Present 0.0 |
| title | 7.0 (1 %) | 8.5 (1 %) | 598.7 | 0.8 | Shader.ResolveAbi 3.1, Shader.ResolveGraphicsStagesAbi 2.0 |
| title | 8.8 (1 %) | 4.5 (1 %) | 600.9 | 0.7 | Recorder.Sync 3.6, Shader.ResolveAbi 3.0 |
| title | 6.7 (1 %) | 9.0 (1 %) | 598.5 | 0.8 | Shader.ResolveAbi 3.0, Shader.ResolveGraphicsStagesAbi 1.9 |
| title | 11.0 (2 %) | 5.2 (1 %) | 598.0 | 0.7 | Recorder.Sync 5.5, Shader.ResolveAbi 3.3 |
| title | 7.1 (1 %) | 3.8 (1 %) | 603.3 | 0.8 | Shader.ResolveAbi 2.9, Recorder.Sync 2.2 |
| title | 5.8 (1 %) | 7.0 (1 %) | 601.3 | 0.6 | Shader.ResolveAbi 2.9, Shader.ResolveGraphicsStagesAbi 2.0 |
| title | 7.0 (1 %) | 3.0 (0 %) | 604.2 | 0.6 | Recorder.Sync 3.1, Shader.ResolveAbi 2.2 |
| title | 7.1 (1 %) | 7.3 (1 %) | 599.8 | 0.7 | Shader.ResolveAbi 2.7, Recorder.Sync 2.5 |
| title | 8.6 (1 %) | 6.3 (1 %) | 599.2 | 0.7 | Recorder.Sync 3.5, Shader.ResolveAbi 3.1 |
| title | 6.6 (1 %) | 5.3 (1 %) | 602.3 | 0.7 | Shader.ResolveAbi 2.7, Recorder.Sync 2.2 |
| queue worker 0x23 | 51.0 (8 %) | 562.4 (92 %) | 0.7 | 2.2 | Driver.Packet 607.9, Driver.EndSubmission 5.5 |
| gpu | 134.0 (22 %) | 0.0 (0 %) | 480.2 | 0.0 | batch 134.0 |
**Largest slices** (ms per frame, inclusive, all threads): Driver.Packet 1209.4, Driver.Packet.memory_wait 522.2, Driver.Packet.draw 320.4, Driver.Draw 320.2, Driver.Packet.dispatch 269.0, Driver.Dispatch 240.4, Graphics.Draw 216.1, Driver.Draw.draw_and_resource_release 188.0, Graphics.Draw.draw_and_resource_release 181.5, batch 134.0
**Longest waits**
- Driver.Packet.memory_wait (queue worker 0x23) 476.6 ms at 12827 ms, present 1171
- Driver.Packet.memory_wait (queue worker 0x23) 429.9 ms at 10403 ms, present 1167
- Driver.Packet.memory_wait (queue worker 0x23) 384.6 ms at 16253 ms, present 1176
- Driver.Packet.memory_wait (queue worker 0x23) 357.5 ms at 9662 ms, present 1166
- Driver.Packet.memory_wait (queue worker 0x23) 336.0 ms at 13706 ms, present 1172
- Driver.Packet.memory_wait (queue worker 0x23) 325.8 ms at 8876 ms, present 1165
- Driver.Packet.memory_wait (queue worker 0x23) 305.2 ms at 20578 ms, present 1183
- Driver.Packet.memory_wait (queue worker 0x23) 286.3 ms at 4076 ms, present 1157
- Driver.Packet.memory_wait (queue worker 0x23) 261.5 ms at 6509 ms, present 1161
- Driver.Packet.memory_wait (queue worker 0x23) 244.6 ms at 4368 ms, present 1157
**Largest GPU batches** (GPU ranges inside, and the CPU slices that recorded each, inclusive)
- batch 1175: 20.3 ms at 6992 ms, present 1162: GPU no ranges; recorded by Driver.Packet (queue worker 0x23) 30.8, Driver.Packet.memory_wait (queue worker 0x23) 21.3, Driver.Packet.dispatch (queue worker 0x23) 9.4, Driver.Dispatch (queue worker 0x23) 9.3
- batch 2715: 20.3 ms at 15478 ms, present 1175: GPU no ranges; recorded by GpuMutex.wait.dispatch (queue worker 0x23) 21.0, Driver.Packet (queue worker 0x23) 6.2, Driver.Packet.dispatch (queue worker 0x23) 6.2, Driver.Dispatch (queue worker 0x23) 6.0
- batch 1359: 20.2 ms at 8120 ms, present 1164: GPU no ranges; recorded by Driver.Packet (queue worker 0x23) 51.0, Driver.Packet.memory_wait (queue worker 0x23) 38.8, Driver.Packet.dispatch (queue worker 0x23) 12.1, Driver.Dispatch (queue worker 0x23) 11.9
- batch 3179: 19.3 ms at 18015 ms, present 1179: GPU no ranges; recorded by GpuMutex.wait.label (draw committer) 2.9, Graphics.Draw (draw committer) 1.6, Graphics.Draw.validate (draw committer) 1.4, GpuMutex.wait.draw (draw committer) 0.2
- batch 1742: 18.3 ms at 10194 ms, present 1167: GPU no ranges; recorded by GpuMutex.wait.dispatch (queue worker 0x23) 26.6, Vulkan.Dispatch.shader_resources (queue worker 0x23) 2.4, Resources.Complete (queue worker 0x23) 2.4, Driver.Packet (queue worker 0x23) 1.4
Capture
- The caches were warm (the scene ran once and the title was restarted)
- No other APS5_TRACE_* or APS5_PROFILE_* switch was set and the log was not redirected
- Captures compared here come from the same machine, scene and settings
Tested and written by Claude (AI assistant) on a contributor's machine.
Activity
Tamalero commentedon Oct 24, 2026
Trace:
aps5-trace-abe0d257ac4c-2.json.gz
Tamalero commentedon Oct 24, 2026
The same pattern shows up in a second title on a different engine: LEGO Batman: Legacy of the Dark Knight (PPSA16833, 01.010.000, Unreal Engine).
Build: plain
maine13027a, with onlyAPS5_PREBUILD_THREADS=16 APS5_TRACE_SUMMARY_EVERY=10. Same machine (RTX 4090, NVIDIA 615.78.08, Linux 7.3, i9-14900K). The title boots, reaches the menu and loads the Mountain Climb level with no errors. Standing still at the start of that level:Driver.Packet.memory_waitper frameDriver.Packet.dispatchper frameThis run had no trace window, and no
APS5_PROFILE_DRAW, so I can't yet split the waits by queue or packet type the way the Guardians tables do. If it helps, I can capture a cleanAPS5_TRACE_FRAMESwindow for this scene to compare with the Guardians trace.Tested and written by Claude (AI assistant) on a contributor's machine.
Tamalero commentedon Oct 24, 2026
Follow-up on what queue 0x23 waits for. Same build and menu scene as the report, about 60 s with
APS5_TRACE_GPU=1 APS5_PROFILE_DRAW=1(both add overhead, so only the proportions matter).Almost all of the compute queue's wait time is on one label:
0x2811700380x2811700580x2a62fe6xx–8xxIt's a counter. Each wait expects the next value, and at the start of each wait the label is exactly one behind:
Queue 0x0 does the same on
0x281170058(== 0xc1 (now 0xc0),== 0xc4 (now 0xc3), …).Queue 0x23 mostly waits on its own writes. Per 10 s, the
[packets]line for 0x23 showsfrom recorder 307 (… same queue 255 …)andfrom recorder 308 (… same queue 248 …), with 11–22 polled and 0–1 at entry over the seven intervals. So about 80% of these waits are on a label that the recorder holds as pending from queue 0x23 itself. The title increments the counter after its own work and then waits for it, and each of those waits takes about 108 ms while the GPU is mostly idle (134 ms of GPU per ~590 ms frame in the clean capture).The GPU-mutex waits on these lines are small by comparison:
waited behind (holder/site ms): q0x23/dispatch 61 q0x0/dispatch 29 …andq0x23/dispatch 34 q0x0/draw 14 …per 10 s.Within one queue the GPU executes in order. Would it be possible to satisfy a same-queue wait on a label still pending from that queue in submission order (a barrier) instead of a full CPU round trip for every increment? I can run any diagnostic on this scene that would help.
Tested and written by Claude (AI assistant) on a contributor's machine.
MarcZX10 commentedon Oct 24, 2026
I checked the current wait paths. There is already a recorder-label fast path:
waitMemory()returns when a pending recorder write matches the wait value, and the queue-0 draw pipeline has an additional pending-label shortcut inPacketExecution.cpp. Those fast-path hits do not execute a GPU wait packet or enter the long polling loop. That means the reported 255 same-queue recorder hits are being bypassed already; the 526 waits lasting at least 0.5 ms are likely among the other waits, but the counters shown are not correlated per address or with elapsed wait time, so the report does not yet show which ones dominate the 57.1 s.A useful next capture would correlate each long wait by address/reference with its eventual outcome (
fromRecorder,polled, or timed out) and elapsed time, in one run. In particular, please check whether the repeated waits on0x281170038that exceed 0.5 ms are recorder misses or poll-loop waits. If they are misses, the trace should help identify whether the producer write is not yet recorded/submitted or whether the label lookup refuses it; if they are already recorder hits, that would point elsewhere because those return immediately. This will distinguish a missing GPU-side ordering opportunity from the existing fast path working as designed.MarcZX10 commentedon Oct 24, 2026
I checked the current
WaitMemory.cppdiagnostics. There is no switch that prints a producer for every wait, but a short run withAPS5_TRACE_GPU=1 APS5_TRACE_LATE_LABELS=1 APS5_PROFILE_DRAW=1should narrow this further:APS5_TRACE_GPUprints the address and duration for each wait over 0.5 ms, whileAPS5_TRACE_LATE_LABELSprints any recorder-table hit/refusal examined on the path (up to 200 lines). Please include the paired[gpu] ... waits/done afterlines for0x281170038and any corresponding[late]lines, plus the same interval's[packets]counters. That will tell us whether those long instances found a recorder label or continued into polling. The label-trace switch may add logging overhead, so a brief representative capture is enough.MarcZX10 commentedon Oct 24, 2026
Correction to my previous comment:
APS5_TRACE_LATE_LABELSlogs recorder lookup refusals and late-label hits, not ordinary successful hits. The[gpu]address/duration pairs plus the aggregate[packets]counters will still help separate polling from fast-path behavior; any[late]lines can explain specific rejected candidates. I should not have described that switch as logging every table hit.Tamalero commentedon Oct 24, 2026
@MarcZX10 thanks. Here's the run you asked for: same build and menu scene,
APS5_TRACE_GPU=1 APS5_TRACE_LATE_LABELS=1 APS5_PROFILE_DRAW=1, about 35 s on the menu with no input.[late]lines: none, neither in that window nor anywhere in the log from launch.Waits on
0x281170038: 216 by queue 0x23, 213 of them with adone afterline, for 28.7 s in total (~134 ms each). The first pairs, with the timestamps removed:Queue 0x0's
0x281170058had 53 waits for 2.0 s in the same window.[packets]for queue 0x23, the four 10 s intervals in the window:WAIT_REG_MEM_64So about 80–85% of the waits were satisfied from a recorder label, mostly one queue 0x23 recorded itself, and only 20–26 per interval went to polling, with no refusals and nothing late. The long waits are therefore not polling misses. Yet the
done aftertimes for those same waits are 11–242 ms. If it helps, I can add a timestamp at each step inside one of these waits on a local build (table lookup, the wait on the producer's batch, return) to show where the ~134 ms goes.Tested and written by Claude (AI assistant) on a contributor's machine.
Tamalero commentedon Oct 29, 2026
An update on current
main, with a CPU profile of the menu and a list of what does not change it.Build:
main319621b with #9789 reverted (the title stops at start-up without that, #11513). The same menu as in this issue: p50 495.9 to 532.9 ms in four runs, withAPS5_TRACE_FRAMES=1150:40, no input, warm caches, RTX 4090, NVIDIA 615.78.08, Linux 7.3. The title also still gets through New Game to the Milano on this build, at 0.8 to 2 FPS.CPU profile (
perf record -p, 10 s on the menu, no trace switches set):0x611c30that reads a global flag and executespause. It is called from the title's job-system idle wait (a loop of up to 199 calls with a per-thread counter compared against 500). So 14 worker threads are busy-waiting with no work.So the menu is not throughput-bound on either side. The frame seems to be a chain of round trips: the title waits for queue work, and queue 0x23 waits (
Driver.Packet.memory_wait, 406 to 460 ms per frame in the base runs) on labels that only advance after its own work completes, as in my earlier comments.No change (menu p50, one run each unless noted; base 495.9 / 532.9 / 512.8 / 511.1 ms):
APS5_POLL_REAP=1APS5_LABEL_FLUSH_US=100APS5_DRAIN_COMPLETION_LABELS=1APS5_POLL_TRY_EACH=1APS5_LABEL_SUBMIT_NOW=1(three runs)APS5_LABEL_SUBMIT_NOW=1is the one consistent change: GPU busy per present falls from about 115 ms to about 40 ms in all three runs, but the frame time stays within the spread. So GPU time is not what limits this frame.From #12640: the dispatch cache hits about 10 % of the time on this menu, and neither 8 variants nor 65,536 entries change the frame time (+0 % and +3.5 %).
I can rerun any of these with other switches, or capture traces of the base and
APS5_LABEL_SUBMIT_NOWruns if those would help.Tested and written by Claude (AI assistant) on a contributor's machine.