8.6 KiB
P15 wait-class audit
Commands are recorded and replayed asynchronously by default. A record makes the client wait only
because its row in MG_Pipe/PipeCalls.def has a wait class other than kWaitNone. Since e00db8e6
the client's per-verb barrier answer is exactly the wait class of the record its emitter publishes
(MG_Pipe/VerbRecordOps.h). This file lists every row that still waits, says why, and flags the
candidates for async. No row is converted until the user has seen this table. Each conversion
needs a red-once test.
The rule for keeping a wait:
- true reply: the client acts on an answer it cannot derive itself; or
- present pacing.
Anything else (a kWaitApplied row whose apply still reads a BARRIER_PULLED or residual field, or a
reply the client could derive) is a candidate.
Measured hit rates (FCL, inproc, cpuhunt, MOBILEGL_IPC_WAIT_STATS=1)
Waits per frame in steady state, 600-frame windows:
| wait | vanilla 1.21.5 (Magma, Espryt) | BSL (Magma) |
|---|---|---|
| present credit | 1.00 | 1.00 |
record ResourceCreate (blocking create) |
0.00-0.01 | 2-3 |
record ResourceCopyRegion (glCopyImageSubData) |
0 | 3 |
record GenerateMipmap |
0 | 3 |
record CopyFramebufferToTexture (glCopyTexSubImage2D) |
0 | 1 |
| barriered verbs quiescing the applier at validate | 0 | 8 (CopyImageSubData 3, GenerateMipmap 3, CopyTexSubImage2D 1, ClientWaitSync 1) |
follow-on quiesce MGPipeNoteFrontendMutation |
0 | 14-16 |
follow-on quiesce MGPipeValidateForVerb/residual |
0 | 8 |
| ring space | 0 | 0 |
- Vanilla meets the target: the present credit is the only per-frame wait.
- On BSL, every barriered verb costs about three waits, not one: the record's own applied wait, the
quiesce before the fill, and then a re-quiesce at each frontend mutation until the next
unbarriered verb (
g_lastFillWasBarriered). Making the threekWaitAppliedverbs async removes all of these at once.
Final verdicts (user decision 2026-10-08)
The principle, in the user's words: everything that is fire-and-forget is async and non-blocking; only what must block (client-memory readback, glFinish, ClientWaitSync, ...) blocks. A reply the client can derive or pre-allocate (handles the client mints) is not a reason to block.
| row | was | now | why |
|---|---|---|---|
Present |
kWaitPresent | keep | present pacing (the credit, now derived from MOBILEGL_FRAMES_IN_FLIGHT) |
GetCaps |
kWaitReply | keep | the caps snapshot the app reads (startup only) |
MapPersistent |
kWaitReply | keep | the map pointer the app writes through |
FenceStatus, FenceWait |
kWaitReply | keep | glGetSync / glClientWaitSync answers |
QueryAvailable, QueryResult, QueryTimestamp |
kWaitReply | keep | query results; an XFB primitives-written count blocks at the query read, never at End |
ResourceReadback |
kWaitReply | keep | bytes for client memory |
ReadPixels, GetTextureImage |
kWaitReply | keep for client memory; async per call with a pack buffer bound | the PBO path publishes the kWaitNone *ToBuffer record; the validate point now answers per call (PackReadbackGoesToBuffer), and the rare reply-form fallback re-validates barriered |
SharedImage |
kWaitReply | keep | the server allocates the dma-buf: its fd, modifier and stride cannot be derived (anland only; a per-op relaxation for attach/present/release is a follow-up) |
ResourceCreate |
kWaitReply | async on the shared-pool arm (inproc); the stream arm keeps its deferred create window | the handle is the client's own and the accept is derivable (the server declines only protocol corruption or a family with no consumer, which the client never emits for) |
ResourceRespecify, SetTextureParams, ResourceSubData |
kWaitReply | already async per call once the object is published | with the create async the object is published at create, so the first-use wait is gone too |
GenerateMipmap, ResourceCopyRegion, CopyFramebufferToTexture |
kWaitApplied | kWaitNone | applies resolve everything from the record's handles (P13 W4b/W4c); the dual-block census is empty |
SetStorageBlockBinding |
kWaitApplied | kWaitNone | resolved through the record's program CSO (P5f) |
Begin/End/Pause/ResumeStreamOutput, BindStreamOutput |
kWaitApplied | kWaitNone | the span lives server-side (P5f); a draw inside an open span is still barriered by the XFB clause, and the primitive count is answered at the query read |
ApplierReset |
kWaitApplied | keep | once per make-current, and its apply probes the client allocator, which is only legal inside a parked scope; not on any per-frame path |
And the cascade: with two PipeInputs blocks (MOBILEGL_IPC_ROLE_SPLIT_STATE, on by default since
P15 - every split lane and the on-device gate already ran with it, and its census is empty) a
barriered verb's fill writes the client's own block, so QuiesceApplierBeforeFill and the guard
skip the applier quiesce. One barriered verb now costs one wait: its record's.
Rows that waited (the audit as reviewed)
| row | class | why it waits today | FCL hits/frame | verdict |
|---|---|---|---|---|
Present |
kWaitPresent | present credit, taken before the encode | 1 | keep (pacing) |
GetCaps |
kWaitReply | the caps snapshot | 0 (startup) | keep (true reply) |
ResourceCreate |
kWaitReply | accept/decline feeds the client's publish latch; a create window defers it for some objects | BSL 2-3 | candidate: find which creates take the blocking path every frame on BSL and why the window refuses them |
ResourceRespecify |
kWaitReply | texture half until the object's create is accepted; already fire-and-forget once published (MGPipeResourceRespecifyWantsItsReply) |
0 | keep (relaxed per call already) |
MapPersistent |
kWaitReply | the client needs the mapping | 0 (startup) | keep (true reply) |
FenceStatus, FenceWait |
kWaitReply | GPU state the app asked for | BSL 1 (ClientWaitSync) | keep (true reply) |
QueryAvailable, QueryResult, QueryTimestamp |
kWaitReply | GPU results | 0 | keep (true reply) |
SetTextureParams |
kWaitReply | waits only before the texture's create is accepted; fire-and-forget after | 0 | keep (relaxed per call already) |
ResourceSubData |
kWaitReply | buffer half and wire texture half never wait (MGPipeSubDataWantsItsReply) |
0 | keep (relaxed per call already) |
ResourceReadback, GetTextureImage, ReadPixels |
kWaitReply | the bytes | 0 | keep (true reply); note: the verb table names the reply op for ReadPixels / GetTexImage, so a pack-buffer read (which publishes the kWaitNone *ToBuffer record) is still barriered at validate. A per-call answer would make PBO reads async |
SharedImage |
kWaitReply | AHB/dma-buf handle exchange (anland) | 0 | keep (true reply) |
GenerateMipmap |
kWaitApplied | its apply read GetActiveTextureUnit and the unit's binding slot from the residual block (PipeCalls.def / ClientSession.cpp); P13 W4b gave the record arm the texture by handle (VerbMipRes) |
BSL 3 | candidate: check whether any backend path still reads the residual unit; if not, kWaitNone |
ResourceCopyRegion |
kWaitApplied | its apply read GetTextureObject, a BARRIER_PULLED row whose retiring phase was P7 (ID-118); P13 W4b resolves both endpoints by handle |
BSL 3 | candidate: same check |
CopyFramebufferToTexture |
kWaitApplied | the read framebuffer and the destination were pulled; P13 W4b resolves the destination by handle (VerbCopyTexDst) and P13 W4c names framebuffers by handle |
BSL 1 | candidate: same check |
SetStorageBlockBinding |
kWaitApplied | probes a registry | 0 | keep for now (not on any FCL path) |
BeginStreamOutput, EndStreamOutput, PauseStreamOutput, ResumeStreamOutput, BindStreamOutput |
kWaitApplied | XFB span accounting and the capture-binding shadow | 0 | keep for now (not on any FCL path) |
ApplierReset |
kWaitApplied | orders a make-current reset against the verbs after it | 0 (make-current) | keep |
Count note: the X rows in PipeCalls.def parse as 59 kWaitNone, 15 kWaitReply, 10 kWaitApplied and 1 kWaitPresent (85 rows).
Proposed order (after the user's go-ahead)
GenerateMipmap,ResourceCopyRegion,CopyFramebufferToTextureto kWaitNone, each with a red-once test (a poison or verify build that aborts on a residual read under an unbarriered apply). BSL then loses 7 record waits, 7 validate quiesces and the 14-16 follow-on quiesces per frame.ResourceCreateon BSL: find the 2-3 blocking creates per frame and why the create window declines them.- Pack-buffer
ReadPixels/GetTexImage: a per-call barrier answer instead of the verb-level reply op.