You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
PPSA21567: submission stops on "unsupported PM4 packet type: header 0x00000001" where a RELEASE_MEM header is expected #1458
On main6b57386 (with #285 and #943), with #759 merged locally, PPSA21567 (Astro Bot, v01.019.000) stops at submission validation about 20 s after start, the same way in every run:
[gpu] at DWORD 8714 header 0xc0064900 RELEASE_MEM 8 dwords: 06000514 01010000 00000000 00000000 00000000 00000000 0000abcd
[gpu] at DWORD 8722 header 0xc0064900 RELEASE_MEM 8 dwords: 00000528 60010000 0367e820 00000004 00000000 00000000 00000000
[gpu] dwords 8722..8738: c0064900 00000528 60010000 0367e820 00000004 00000000 00000000 00000000 00000001 06000514 01010000 00000000 00000000 00000000 00000000 0000abcd
terminate called after throwing an instance of 'std::runtime_error'
what(): AGC driver: unsupported PM4 packet type: header 0x00000001 at DWORD 8730 of 10340
Before this point the buffer is a run of RELEASE_MEM pairs between PUSH_MARKER/POP_MARKER. The first packet of each pair writes 0x4_0367xxxx; the second has payload 06000514 01010000 0 0 0 0 0000abcd. At DWORD 8730 the second packet's payload is there, but its header is 0x00000001 where 0xc0064900 is expected.
What I checked:
It is the guest command buffer itself. With a local trace in copySegment, the submission is one segment, 0x502002500, 10340 words, with no INDIRECT_BUFFER or COND_INDIRECT_BUFFER jump. So the word is 0x00000001 in guest memory when copyCommands runs at submit.
Not established: who writes 1 there, or whether the title fills that header after submitting the buffer (the console's CP reads it later than copyCommands does).
Found the writer of the 1: it is the driver's deferred completion-label store, not the title.
Hardware watchpoint (gdb, watch -l on 0x502002500 + 8730*4 = 0x50200ad68, armed 12 s in):
Write macOS support #1, title thread: a 0x94-byte memcpy to 0x50200ad64 (the title writing new commands there). Afterwards the word is 0x00000000.
Write fix(build): register relinker tests with CTest #2, a driver thread: libc memcpy with rdi = 0x50200ad68, value 1, called from Recorder::finish → Recorder::AfterCompletions(...)::{lambda()#2} → GuestMemory::StoreOwnBytes → memcpy
(libSceAgcDriver.prx +0x2ab561, +0x295e5f, +0x203987, +0x2038a1, +0x1fd598).
The submission copy then reads 1 as the header.
What is queued there (a local print in AfterCompletions for addresses in 0x502000000–0x502100000). The title queues label stores into its own command-buffer range, in pairs per frame, and none is stored on the GPU:
The same happens for other addresses in that buffer (0x502002d68, 0x502002f38, 0x502003040, …): 10516 such completion stores in one 300 s run. With storedOnGpu 0 the value only lands when the batch's completions run in finish. By then the title has already rewritten that memory as the next command buffer, so the late store overwrites a packet header. On hardware the label would be written in stream order, before the title moves on.
Not established: why astrobot (93faece) does not stop here. With udmabuf.size_limit_mb=64 on both, so the same imports fail, astrobot ran 240 s without this error. So the difference is in the driver, not the import limit.
AI-assisted: yes (Claude Code ran the watchpoint and the instrumented build and wrote this; the runs are on my machine).
astrobot takes the same path here. WriteLabelOnGpu's import == nullptr branch (VulkanDevice.cpp:1255 on main) and Recorder::AfterCompletions are the same code in both. astrobot has no udmabuf import (815d274 isn't in it), so on RADV it can't import this range either and defers the same store. The difference is pacing: astrobot holds Driver::Submit while a submission with a flip is queued and not yet started (throttleSubmit, 40cd74e), so the title doesn't get far enough ahead to reuse that memory before the batch is reaped. That hides the bug but doesn't fix it. Two runs should confirm it (not run here): astrobot with APS5_MAX_QUEUED_FLIPS=off should stop at the same DWORD, and main with APS5_DRAIN_COMPLETION_LABELS=1 should get past it.
The fix belongs in that branch. A label the GPU can't store has to be stored at its place in the stream, not when the batch is reaped. With the current code, the exact way is the drain path: wait for the recorded work, then store on the CPU. That means the deferred AfterCompletions(..., false) goes away. Recorder.hpp already describes this hazard for the storedOnGpu case, and the plain store has the same problem.
This is a complex GPU command buffer issue. Based on my review of recent PRs related to the AGC driver and shader recompiler, I can provide some context:
The fact that oneandonlydean's astrobot branch runs past this point suggests there may be a difference in how command buffers are being processed or validated between the two implementations. The issue correctly identifies that the word 0x00000001 appears where a RELEASE_MEM header (0xc0064900) is expected, and that this happens consistently at the same DWORD offset.
Some observations based on similar issues I've seen:
The fact that it's the guest command buffer itself (not an indirect buffer) suggests the issue is in how the title constructs or submits its command buffers
The pattern of RELEASE_MEM pairs between PUSH_MARKER/POP_MARKER is consistent with synchronization operations
The possibility that the title fills the header after submitting the buffer (as noted at the end of the issue) is worth investigating - this would be a race condition where AnyPS5 reads the buffer before the title has finished writing it
I'd suggest comparing how oneandonlydean's implementation handles command buffer submission and validation to see if there are any differences that might explain why it works.
To reproduce without root I capped the host import budget with APS5_HOST_IMPORT_MIB=64 (host import budget: 0 MiB live of 64 MiB). That stops at the same place: unsupported PM4 packet type: header 0x00000001 at DWORD 8730 of 10340.
main with the budget at 64, 100 s per run, headless gamescope
run
result
deferred storedOnGpu=0 stores queued into 0x502000000–0x502100000
default, 3 runs
3/3 stop at DWORD 8730
930 per run
APS5_DRAIN_COMPLETION_LABELS=1, 3 runs
0/3 stop, run to the timeout
0
So the drain path gets past it, and it removes every deferred store into that range. This matches your reading.
astrobot with APS5_MAX_QUEUED_FLIPS=off: not confirmed
Runs: two of 100 s and one of 360 s. None stopped, and none had kernel faults.
In these conditions the branch runs at only 2–3 presents/s, against about 25 without the cap. That is probably slow enough that the title never gets ahead, so this doesn't refute the pacing explanation. It doesn't confirm it either.
Separately, one earlier astrobot run with flips off and no budget cap ended after about 25 s in a gfx ring timeout with no page fault (vkQueueSubmit recorder: Vulkan result -4). I haven't tied that to this bug.
AI-assisted: the runs and this write-up were done by Claude (Claude Code) on my machine.
Linux (CachyOS, GCC, Release), RX 9070 XT, RADV / Mesa 26.2.4, headless gamescope. I used the same repro conditions as before, APS5_HOST_IMPORT_MIB=64, so the DCB range stays unimported. Before, every run stopped about 20 s in on unsupported PM4 packet type: header 0x00000001 at DWORD 8730 of 10340.
Activity
EdouardSence commentedon Oct 8, 2026
Found the writer of the
1: it is the driver's deferred completion-label store, not the title.Hardware watchpoint (gdb,
watch -lon0x502002500 + 8730*4 = 0x50200ad68, armed 12 s in):memcpyto0x50200ad64(the title writing new commands there). Afterwards the word is0x00000000.libc memcpywithrdi = 0x50200ad68, value1, called fromRecorder::finish→Recorder::AfterCompletions(...)::{lambda()#2}→GuestMemory::StoreOwnBytes→memcpy(
libSceAgcDriver.prx+0x2ab561, +0x295e5f, +0x203987, +0x2038a1, +0x1fd598).The submission copy then reads
1as the header.What is queued there (a local print in
AfterCompletionsfor addresses in0x502000000–0x502100000). The title queues label stores into its own command-buffer range, in pairs per frame, and none is stored on the GPU:The same happens for other addresses in that buffer (
0x502002d68,0x502002f38,0x502003040, …): 10516 such completion stores in one 300 s run. WithstoredOnGpu 0the value only lands when the batch's completions run infinish. By then the title has already rewritten that memory as the next command buffer, so the late store overwrites a packet header. On hardware the label would be written in stream order, before the title moves on.Not established: why
astrobot(93faece) does not stop here. Withudmabuf.size_limit_mb=64on both, so the same imports fail,astrobotran 240 s without this error. So the difference is in the driver, not the import limit.AI-assisted: yes (Claude Code ran the watchpoint and the instrumented build and wrote this; the runs are on my machine).
oneandonlydean commentedon Oct 8, 2026
astrobottakes the same path here.WriteLabelOnGpu'simport == nullptrbranch (VulkanDevice.cpp:1255on main) andRecorder::AfterCompletionsare the same code in both.astrobothas no udmabuf import (815d274 isn't in it), so on RADV it can't import this range either and defers the same store. The difference is pacing:astrobotholdsDriver::Submitwhile a submission with a flip is queued and not yet started (throttleSubmit, 40cd74e), so the title doesn't get far enough ahead to reuse that memory before the batch is reaped. That hides the bug but doesn't fix it. Two runs should confirm it (not run here):astrobotwithAPS5_MAX_QUEUED_FLIPS=offshould stop at the same DWORD, and main withAPS5_DRAIN_COMPLETION_LABELS=1should get past it.The fix belongs in that branch. A label the GPU can't store has to be stored at its place in the stream, not when the batch is reaped. With the current code, the exact way is the drain path: wait for the recorded work, then store on the CPU. That means the deferred
AfterCompletions(..., false)goes away.Recorder.hppalready describes this hazard for thestoredOnGpucase, and the plain store has the same problem.klosixgaming-arch commentedon Oct 8, 2026
This is a complex GPU command buffer issue. Based on my review of recent PRs related to the AGC driver and shader recompiler, I can provide some context:
The fact that oneandonlydean's
astrobotbranch runs past this point suggests there may be a difference in how command buffers are being processed or validated between the two implementations. The issue correctly identifies that the word0x00000001appears where a RELEASE_MEM header (0xc0064900) is expected, and that this happens consistently at the same DWORD offset.Some observations based on similar issues I've seen:
I'd suggest comparing how oneandonlydean's implementation handles command buffer submission and validation to see if there are any differences that might explain why it works.
EdouardSence commentedon Oct 8, 2026
Results for the two runs you suggested, plus one correction on the repro conditions.
Repro conditions
udmabuf.size_limit_mbat the default 64, which I hadn't noticed.main(6b57386 + feat(agc): implement deferred faults for reads through null SRT pointers #759, same build as before) did not stop in 7 runs of 100 s each, headless and windowed.APS5_HOST_IMPORT_MIB=64(host import budget: 0 MiB live of 64 MiB). That stops at the same place:unsupported PM4 packet type: header 0x00000001 at DWORD 8730 of 10340.mainwith the budget at 64, 100 s per run, headless gamescopestoredOnGpu=0stores queued into 0x502000000–0x502100000APS5_DRAIN_COMPLETION_LABELS=1, 3 runsSo the drain path gets past it, and it removes every deferred store into that range. This matches your reading.
astrobotwithAPS5_MAX_QUEUED_FLIPS=off: not confirmedastrobot88d7bb6 + feat(agc): import shared direct memory through udmabuf on Linux #943 + perf(agc): write draw descriptors from retained CPU metadata #1334 + fix(agc): keep RADV's descriptor pool overrun inside the pool's pages #1065, with the budget at 64.astrobotrun with flips off and no budget cap ended after about 25 s in a gfx ring timeout with no page fault (vkQueueSubmit recorder: Vulkan result -4). I haven't tied that to this bug.AI-assisted: the runs and this write-up were done by Claude (Claude Code) on my machine.
oneandonlydean commentedon Oct 10, 2026
#2044 now stores labels the GPU can't reach in stream order. Could you retest on current main?
EdouardSence commentedon Oct 10, 2026
Retested on
maina29bae3: fixed.Linux (CachyOS, GCC, Release), RX 9070 XT, RADV / Mesa 26.2.4, headless gamescope. I used the same repro conditions as before,
APS5_HOST_IMPORT_MIB=64, so the DCB range stays unimported. Before, every run stopped about 20 s in onunsupported PM4 packet type: header 0x00000001 at DWORD 8730 of 10340.ShaderRecompiler::Recompile: shader image heap capacity exceeded(Sampled image heap of 16 rejects a retail shader before the metadata limit #1789).Closing, since #2044 resolves it.
Tested and written by Claude (AI assistant) on a contributor's machine.