# P5e audit — apply-thread reads of client-owned memory on the DirectGLES inproc draw path Repo head `2fde7034` (`feat/disaggregated`). Read-only static audit; no build, no run. Legend — paths: `D` = `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `M` = `MobileGL/MG_Backend/DirectGLES/Managers.cpp`, `ST` = `MobileGL/MG_Backend/DirectGLES/SlotTables.h`, `PA` = `MobileGL/MG_Remote/Server/PipeApplier.cpp`, `AP` = `MobileGL/MG_Pipe/PipeApply.cpp/.h`, `MT` = `MobileGL/MG_Pipe/MGPipeTypes.h`, `PI` = `MobileGL/MG_Backend/MGPipe/PipeInputs.h`, `MD` = `MobileGL/MG_Backend/DirectGLES/MultiDraw.cpp`. Kinds: `LIVE` = dereference of a client object/field; `PROBE` = `GetLifetimeId()` + `MGPipeSlots().FindByLifetimeId`; `MINT` = `MGPipeSlots().Acquire(...)`; `ROW-O` = `gPipeInputs` BARRIER_PULLED row holding a raw pointer/SharedPtr into the live `GLContext`; `ROW-V` = BARRIER_PULLED copied-value row. Arm: `rec` = runs when `MG_Config::Transport != Monolith` with all `MOBILEGL_PIPE_PUSH` bits; `leg` = legacy/monolith arm only; `both` = no transport branch. Freq: `draw` = every draw/dispatch; `memo` = only on memo miss / clean-probe open; `rare` = config change / error / special-feature path. "Dup" = pushed record or `MGPipeApplierState` field (`AP:PipeApply.h`) already carrying the same information; `none` = nothing pushed covers it. Sink map (all sinks are in `PA`; bodies read no `MG_State` — every client read is one level down in the callee): `OnDrawVbo` PA:430 → the 19 draw entries (`D:6705`-`D:7295`), all of which call `PrepareForDraw` (D:4762) first; `OnClear` PA:168 → `Clear` D:6180 / `ClearBufferfi` D:10045 / `ClearBufferfv` D:10091 / `ClearBufferiv` D:10104 / `ClearBufferuiv` D:10120; `OnBlit` PA:232 → `BlitFramebuffer` D:8322; `OnLaunchGrid` PA:670 → `DispatchCompute` D:9610 / `DispatchComputeIndirect` D:9618 → `PrepareForCompute` D:6118; `OnGenerateMipmap` PA:915 → `GenerateMipmap` D:9559; `OnCopyFramebufferToTexture` PA:931 → `CopyTexImage2D` D:9256 / `CopyTexSubImage2D` D:9373. `AP` (`PipeApply.cpp`, 38 `MGPipeApply*` entries) contains **zero** client-owned reads — no `pGLContext`, no `MGPipeSlots()` probe, no `MGB_CTX->`. ## VAO / vertex | # | site | function | what is read | kind | freq | arm | dup | |---|---|---|---|---|---|---|---| | 1 | D:4770 | `PrepareForDraw` | `MGB_CTX->GetBoundVertexArray()` — SharedPtr to client VAO | ROW-O | draw | both | `BoundVertexElements` (AP:594) | | 2 | D:1617 → ST:436-446 | `VertexArrayImpl::ResolveVaoTwin` | `Find(vao.get())` → `HandleOf` → `MGPipeSlots().FindByLifetimeId` | PROBE | draw (memo hit at ST:444 skips) | rec (slot arm) | `BoundVertexElements` | | 3 | D:1618 → ST:247-248 | `ResolveVaoTwin` | `GetOrCreate(vao)` → `MGPipeSlots().Acquire` | MINT | rare | rec | none (first-touch only) | | 4 | D:1626, D:1629-1634 | `ResolveVaoTwin` | legacy memo/registry keyed on frontend heap address + `stateRef` weak_ptr | LIVE | draw | leg | none | | 5 | D:4775 | `PrepareForDraw` | `currentVAO->GetConfigVersion()` | LIVE | draw | both | none (server has `VertexBuffersSerial`/`ContentSerial` only) | | 6 | D:1653 → M:4881 | `SyncCurrentVAO` | client VAO handed to `SyncToBackend` | LIVE handoff | draw | both; handle arm diverts to `SyncToBackendFromApplier` M:5160 (zero client reads) | `VertexElementsCso` record (AP:487) | | 7 | M:4919-5104 | `BackendVertexArrayObject::SyncToBackend` | `GetConfigVersion`, index slot, `GetAllAttributeVersions`/`GetAllAttributes`, per-attrib `Enabled/Divisor/IsBgra/Offset/Stride/Type/...` | LIVE | draw | leg only | `MGPVertexAttribWire`/`MGPVertexBindingPointWire` (MT:389-400) | | 8 | M:5479-5544 | `SyncFloat64AttributeAsFloat32` | `attrib.*`, `bufferObject->MappedData()` — client shadow bytes; `NarrowDoubleStreamToFloat32` reads them | LIVE | rare | leg | none | | 9 | D:1669 | `SyncCurrentVertexAttributeValues` | `GetBoundVertexArray()` | ROW-O | draw | both | `BoundVertexElements` | | 10 | D:1683, D:1688, D:1700 | `SyncCurrentVertexAttributeValues` | `vao->GetConfigVersion()`, `vao->GetAttribute(location).Enabled`; `GetCurrentVertexAttribute(location)` | LIVE + ROW-V | draw / memo | both | `SetVertexAttribDefaults` (AP:1600) for :1700; none for :1683/:1688 | | 11 | D:6449, D:6451, D:6459 | `BoundElementArrayBuffer` | `GetBoundVertexArray()`, VAO index slot `GetBoundObject()`, `EnsureBufferResource` | ROW-O + LIVE + PROBE | per indexed draw | both | `IndexBuffer` (AP:620) | | 12 | D:6724, D:6731 | `DrawArrays` | `GetBoundVertexArray()` + registry `Find(currentVAO.get())` → the one true per-draw `MGPipeSlots().FindByLifetimeId` from the apply thread | ROW-O + PROBE | every DrawArrays | both | `BoundVertexElements` | | 13 | D:6768, D:6777 | `MultiDrawArrays` | same pair, per sub-draw | ROW-O + PROBE | per sub-draw | both | `BoundVertexElements` | | 14 | M:5372-5449 | `SyncClientSideAttributesForDrawArrays` | `GetAllAttributes()`, `attrib.Offset` reinterpreted as client-memory pointer, client array bytes uploaded | LIVE | VAOs with client-side arrays | both | none (client-memory vertex data) | | 15 | MD:96 | `BoundIndexBuffer` | `MGB_CTX->GetBoundVertexArray()` | ROW-O | per multi-draw element call | both | `BoundVertexElements` | ## Buffers | # | site | function | what is read | kind | freq | arm | dup | |---|---|---|---|---|---|---|---| | 16 | D:897 | `SyncNeccessaryBuffers` | `currentVAOObject->GetIndexBufferBindingSlot().GetBoundObject()` | LIVE | every indexed draw | both | `IndexBuffer` (AP:620) | | 17 | D:907 → M:2922-2941 | `SyncNeccessaryBuffers` | `HandleOfBuffer(possibleIBO.get())` → `MGPipeSlots().FindByLifetimeId` | PROBE | every indexed draw | rec | `IndexBuffer.Res` | | 18 | D:911 → M:3109-3151 | `IsBufferDrawCleanByHandle` | server-only under split; `frontend->IsMapped()` at M:3147 is monolith-only | LIVE (monolith) | memo | both | resource record `Serial`/`HasLiveHostWrites` | | 19 | D:915, D:932 | `SyncNeccessaryBuffers` | `EnsureBufferResource(possibleIBO)` | PROBE | rare | rec | `IndexBuffer.Res` | | 20 | D:926, D:928, D:934 | `SyncNeccessaryBuffers` | `memo->iboFrontend == possibleIBO.get()` — client address compare | LIVE | every indexed draw | leg | `IndexBuffer.Res` | | 21 | D:842 | `SyncNeccessaryBuffers` | `currentVAOObject->GetAttribute(entry.attribIndex).Buffer` | LIVE | rare (clean-probe fail) | both | `VertexBuffers` window (AP:598-600) | | 22 | D:857-861 | `SyncNeccessaryBuffers` | `GetAllAttributes()`, per-attrib `Enabled`/`Buffer` | LIVE | memo | leg-memo path | `VertexElementsCso` + `VertexBuffers` | | 23 | D:864, D:874 | `SyncNeccessaryBuffers` | stored raw client `BufferObject*`, `EnsureBufferResource(bufferObject)` | LIVE + PROBE | memo | both | buffer handle in `VertexBuffers` | | 24 | D:736, D:746-753, D:763, D:766-770 | `SyncVaoAttributeBuffersByHandle` | memo-miss full walk: `GetAllAttributes`, `attrib.Buffer`, stores client pointer + `HandleOfBuffer` | LIVE + PROBE | memo | rec | `VertexBuffers`/`VertexElementsCso` | | 25 | M:3158 | `IsBufferDrawClean` | `HandleOfBuffer(frontend)` | PROBE | every probe | rec | resource record | | 26 | M:3163, M:3175, M:3179, M:3180 | `IsBufferDrawClean` | `frontend->GetBackendResource/IsMapped/GetSize/GetChangeSerial` | LIVE | every probe | leg | resource record | | 27 | M:3444 | `EnsureBufferResource` | `HandleOfBuffer(bufferObject.get())` | PROBE | per bound VBO/IBO/SSBO/UBO/atomic/XFB/PBO per draw | rec | the handle in each pushing record | | 28 | M:3452 | `EnsureBufferResource` | `bufferObject->GetExternalIndex()` | LIVE | error arm only | both | none | | 29 | M:3221, M:3340, M:3393 | `EnsureBufferResourceForHandle` | `bufferObject->MappedData()/SyncPersistentMappedRange()/HasDefinedContent()` — **monolith-only**; under an active transport this function is client-free | LIVE | — | leg | `ServerStaged()` shadow + resource record | | 30 | M:2953 | `MarkBufferGpuWritten` | `HandleOfBuffer(bufferObject.get())` before the reverse announcement | PROBE | per SSBO per draw | rec | none (reverse direction) | | 31 | M:2950 | `MarkBufferGpuWritten` | `bufferObject->MarkGpuWritten()` — client **write** | LIVE | per SSBO per draw | leg | none | | 32 | M:3083 | `GetBufferResource` | `bufferObject->GetBackendResource()` (called per UBO per draw from D:5472) | LIVE | draw | both | none | | 33 | D:962, D:5987, D:6077, D:6511, D:6930-6931, D:7082-7083 | indirect/restart helpers | client `GetBufferBindingSlot(DrawIndirect/Parameter)` + `MappedData()` — all behind `Transport == Monolith`; record arms (D:373, D:951-956, D:6534-6536, D:6889-6927, D:7043-7079) read applier handles + server staged shadows only | ROW-O + LIVE | indirect draws | leg | `VerbIndirectBuffer`/`VerbIndirectParameterBuffer` (AP:742-743) | | 34 | M:5364-5468 | see #14 | client-side attribute bytes | LIVE | rare | both | none | ## Textures / samplers | # | site | function | what is read | kind | freq | arm | dup | |---|---|---|---|---|---|---|---| | 35 | D:1899-1913 | `UnitBindingsUnchanged` | `GetTextureUnitObject(unit)` (raw `TextureUnit*` into client array), `GetAllBindingSlots()`, `GetBoundObject()->GetLifetimeId()`, sampler `GetLifetimeId()` | ROW-O + LIVE | memo | rec walk arm | `BoundSamplerViews`/`BoundSamplerStates` windows (AP:673-681) | | 36 | D:1871-1885 | `CaptureUnitBindings` | same tables, captures lifetime ids | ROW-O + LIVE | memo | rec | same | | 37 | D:1934-1940, D:1947-1953 | legacy twins of the above | same + `WeakPtr` owner checks | LIVE | memo | leg | same | | 38 | D:2346 → D:2136, D:2356, D:2359-2361 | `SyncNeccessaryTextures` served path | `entry.slot->get()` — borrowed pointer **into a client binding slot**; `*entry.slot` ITextureObject handed to twin sync | LIVE | **every draw, per list entry** | both | `BoundSamplerViews` | | 39 | D:2374-2376 | `SyncNeccessaryTextures` rebuild | `GetTextureUnitObject(index)`, slots, `GetBoundObject()` | ROW-O | memo | both | `BoundSamplerViews` | | 40 | D:2379-2381 | `SyncNeccessaryTextures` | `IsUndefinedDefaultTexture(...)`, probe + `SyncTextureObjectToBackend` mint | LIVE + PROBE/MINT | memo | both | texture resource handle | | 41 | D:2405-2406, D:2408-2409 | `SyncNeccessaryTextures` draw-FBO half | `GetFramebufferBindingSlotChecked(Draw).GetBoundObject()`, slot `GetVersion()`, `currentFBO->GetObjectVersion()` | ROW-O + LIVE | **every draw** | both | `BoundFramebuffer[Draw]` + `FramebufferRecord` (AP:646,524) | | 42 | D:2438-2447 | `SyncNeccessaryTextures` | replay of memoised `*entry.slot` through `IsDrawSyncClean` | LIVE | memo | both | `BoundSamplerViews` | | 43 | D:2450-2459 | `SyncNeccessaryTextures` | `currentFBO->GetAllAttachmentObjects()`, `attachment.GetTexture()` + mint | LIVE + MINT | memo | both | `MGPFramebufferState` (MT:750) | | 44 | D:2246-2247, D:2252, D:2258-2259 | `SyncReadFramebufferTextureAttachments` | read-FBO slot `GetBoundObject()`, pointer compare, versions | ROW-O + LIVE | every draw (PUSH) | both | `BoundFramebuffer[Read]` | | 45 | D:2285, D:2290-2296 | same | `PairingsIntact` + replay to `SyncTextureParamsToBackend`/`SyncBuiltinSamplerToBackend`/`SyncMipmapsToBackend` | LIVE | memo | both | sampler-view/params records | | 46 | D:2303-2308 | same | `readFBO->GetAllAttachmentObjects()`, `GetTexture()` + mint | LIVE + MINT | memo | both | `MGPFramebufferState` | | 47 | D:1736, D:1738 | `SyncTextureObjectToBackend` | `g_backendTextureObjects.Find(textureObject.get())` / `GetOrCreate(textureObject)` | PROBE + MINT | per texture sync | both | texture resource handle | | 48 | D:1781, D:1790, D:1797-1799 | `SyncTextureObjectToBackend` | re-resolution tail Find/GetOrCreate | PROBE + MINT | per call | both | same | | 49 | D:1758, D:1760-1762, D:1771-1772 | `SyncTextureObjectToBackend` | client SharedPtr handed into `BackendTextureObject::Sync*` (deep reads at M:5816/7049/8554/8802) | LIVE | per sync | both | pushed params/mipmap records | | 50 | D:4861, D:4876-4877 | `ResolveAndBindUnitTextures` | `GetTextureUnitObject(unit)`, `GetAllBindingSlots()`, `GetBoundObject()` | ROW-O | memo | both | `BoundSamplerViews` | | 51 | D:4883, D:4885, D:4888 | same | `IsUndefinedDefaultTexture`, `GetExternalIndex()`, `GetTarget()` | LIVE | memo | both | none | | 52 | D:4914-4918 | same | `textureUnit.GetSamplerObject()` ×2, `textureObject->GetSamplerObject()`, `SamplesAsIncompleteTexture` | LIVE | memo | both | `BoundSamplerStates` | | 53 | D:4928, D:4945-4946 | same | `Find(textureObject.get())`; clear pass reads `bindingSlot.GetTarget()` | PROBE + LIVE | memo | both | view handle | | 54 | D:5157, D:5159-5160 | `BindCurrentUnitSamplers` legacy arm | `GetTextureUnitObject(unit).GetSamplerObject()` | ROW-O + LIVE | memo | leg | `BoundSamplerStates` | | 55 | D:4999, D:5003, D:5015, D:5018 | `ResolveUnitSamplerBackend` | `HandleOf(samplerObject.get())` probe, `OwnerEquals` weak memo, `Find` | PROBE + LIVE | per non-CSO sampler per draw | rec | `BoundSamplerStates` | | 56 | D:5626 | `BindCurrentProgramWithResources` | `g_backendSamplerObjects.GetOrCreate(samplerObject)` | MINT | rare | leg-in-rec fallback | `BoundSamplerStates` | | 57 | D:5633-5637, D:5639 | same | `SyncToBackend(samplerObject, ...)` — client SamplerObject | LIVE | memo | both | `SamplerCsos` record (AP:501) | | 58 | D:5573, D:5577-5580, D:5591-5595 | same | `GetLodBias()`, binding-slot reads, `texture2D->GetSamplerObject()/GetFormat()` | LIVE | memo | both | none (LOD-bias/depth-fetch fixups) | | 59 | D:9564-9566, D:9568-9570 | `GenerateMipmap` | `GetTextureUnitObject(unitIndex)` (client array), slot `GetBoundObject()`, `texture->GetFormat()` | ROW-O + LIVE | every mipmap | both (rec arm too) | `MGPMipPlan.Res` (MT:1382) covers identity only | | 60 | D:9162, D:9217 | `GenerateDepth/ColorTexture2DMipmap` | `HandleOf(texture.get())` | PROBE | rare | rec | `VerbMipRes` (AP:739) | | 61 | D:9484-9548 | `GenerateThreeChannelFloatMipmapOnCpu` | `GetFormat`, `AsMipmapTexture`, `GetMipmapLevelCount/TexelSize`, `MapMipmapData` — client shadow bytes read+written, `MarkStorageDirty` | LIVE | rare | both | none | | 62 | D:9580, D:9584-9585, D:9591, D:9603 | `GenerateMipmap` | `texture->GetFormat()/GetTarget()` | LIVE | every mipmap | both | resource record `Desc` | | 63 | D:8839, D:8861-8897 | `ScopedDetachedTextureFramebufferAttachments` | `Find(texture.get())` probe; **unmemoised walk of every live twin's client FBO**: `GetAllAttachmentObjects`, `GetTexture`, `GetTextureUploadTarget`, `IsLayered`, `GetTextureLevel` | PROBE + LIVE | rare (per mipmap) | both | none | | 64 | D:8472 | `UpdateTextureBindingAtTarget` | `GetTextureUnitObject(unit)` | ROW-O | every copy-tex | both | `MGPCopyFromFramebuffer.Dst` (MT:1639) covers the handle | | 65 | D:9302-9312, D:9416+ | `CopyTexImage2D`/`CopyTexSubImage2D` legacy arm | unit binding slot `GetBoundObject()`, `Find(textureObject.get())`, `GetFormat()` | ROW-O + LIVE + PROBE | every copy-tex | leg | `VerbCopyTexDst` (AP:737) | | 66 | M:5884, M:7089, M:8483, M:8719 | texture `HandleOf` probes | `g_backendTextureObjects.HandleOf` → allocator probe | PROBE | memo/feature paths | rec | resource handle | | 67 | M:9261, M:9318 | `SyncAttachmentObject` | `GetOrCreate(stateTextureObject)` / `GetOrCreate(renderbufferObject)` | MINT | FBO attach walk | leg | attachment record | ## Framebuffers / attachments | # | site | function | what is read | kind | freq | arm | dup | |---|---|---|---|---|---|---|---| | 68 | D:3019, D:3021, D:3032 | `FramebufferRecordMatchesBinding` (from `SyncCurrentFBO` rec arm D:3112-3113) | `GetFramebufferBindingSlotChecked(target).GetBoundObject()` — **client slot, every record-arm draw, per target**; `pDefaultFramebufferInfo->defaultFBO`; `HandleOf(bound.get())` at D:3032 is monolith-gated | ROW-O + LIVE | draw | rec | `BoundFramebuffer` + `FramebufferSerial` (AP:646,652) | | 69 | D:3178-3179, D:3186 | `SyncCurrentFBO` rec arm | `slot.GetBoundObject()` on serial miss; `currentFBO` handoff | ROW-O + LIVE | memo | rec | `FramebufferRecords` (AP:524) | | 70 | D:3227-3228 | `SyncCurrentFBO` rec arm | `NoteStateForHandle(record.Fbo, currentFBO)` — **stores the client FBO into the twin table** | LIVE | per handle sync | rec | — | | 71 | D:3269 → M:10044 | `SyncCurrentFBO` rec arm | `backendObj->SyncToBackend(currentFBO, target)` walks the frontend FBO | LIVE | memo | rec | `MGPFramebufferState` | | 72 | D:3305-3306, D:3317-3319, D:3336, D:3349, D:3358, D:3360, D:3367-3369, D:3373 | `SyncCurrentFBO` legacy arm | slot + `GetBoundObject()`, versions, `defaultFBO`, `Find`/`GetOrCreate` mint, `SyncReadBufferToBackend`, `SyncToBackend` | ROW-O + LIVE + PROBE/MINT | draw | leg | same records | | 73 | D:4568, D:4580-4581, D:4592, D:4600, D:4602 | `BindCurrentFBO` legacy arm (record arm D:4528-4565 is client-free: applier record + `FindByHandle`) | slot, `GetBoundObject()`, `defaultFBO`, `Find`, weak-owner memo | ROW-O + LIVE + PROBE | draw | leg | `BoundFramebufferRecord` | | 74 | D:4720-4721, D:4734-4736 | `ForceBindCurrentFBO` | `GetFramebufferBindingSlotChecked(target).GetBoundObject()` + versions + `fbo.get()` — **unconditional on both arms**; memoises the frontend pointer at D:4736 | ROW-O + LIVE | every call (clear/blit/DSA) | both | `BoundFramebuffer` | | 75 | D:4630, D:4646-4647, D:4655 | `SyncAndBindFramebufferObject` | `defaultFBO` compare, `Find`/`GetOrCreate` mint, `SyncToBackend` | LIVE + PROBE/MINT | rare | both | `MGPFramebufferState` | | 76 | D:4700, D:4705 | `SyncAndBindFramebufferByHandle` | `StateForHandle(fbo)` returns the **recorded client FBO** (server-side weak note from #70); `SyncToBackend(stateObj, target)` walks it | LIVE | rare (named blit) | rec | handle itself is server | | 77 | D:8357-8359 | `BlitFramebuffer` named arm | `FramebufferImpl::pDefaultFramebufferInfo->defaultFBO` | LIVE | rare | rec | none | | 78 | D:8407-8410 | `BlitFramebuffer` bound arm | `GetFramebufferBindingSlot(Read/Draw).GetBoundObject()` ×2 — **both client FBOs, every bound blit** | ROW-O + LIVE | every bound blit | both | none (`MGPBlit` null-handle form carries no FBO identity) | | 79 | D:8210-8302 | `BlitLayeredDestinationAspects` | `GetDrawBuffers()`, `GetReadBuffer()`, `GetAttachment(...)`, `IsLayered/GetTextureLevel/GetTextureLayer`, `GetTexture()`, `GetFormat()/GetSamples()`, `GetTarget()` + `SyncTextureObjectToBackend` probe/mint | LIVE + PROBE/MINT | every blit | both | none (attachment detail not in `MGPBlit`) | | 80 | D:6284, D:6288-6291 | `Clear` | `GetFramebufferBindingSlot(Draw).GetBoundObject()` + `GetExternalIndex/GetObjectVersion/GetDrawBuffers()` — **debug-log only** | ROW-O + LIVE | dbg | both | — | | 81 | M:10054, M:10119, M:10121, M:10216, M:10283-10284 | `BackendFramebufferObject::SyncToBackend` | `stateFBOObject->GetExternalIndex/GetDrawBuffers/GetAttachment/GetAllAttachmentObjects/GetAllFramebufferAttachmentVersions` | LIVE | per FBO sync | both (entered from #71/#75/#76) | `MGPFramebufferState` | | 82 | M:9938, M:9872, M:9886 | `SyncReadBufferToBackend` | `GetReadBuffer()`, `GetExternalIndex()` | LIVE | per FBO sync | both | `MGPFramebufferState` | | 83 | M:9862, M:10101 | same / `SyncToBackend` | `HandleOf(stateFBOObject)` — monolith branch only | PROBE | per sync | leg | FBO handle | | 84 | D:4386-4399 | `SyncCurrentProgram` (FBO half) | `GetFramebufferBindingSlotChecked(Draw)`, `slot.GetBoundObject()/GetVersion()`, `drawFBO->GetObjectVersion()`, `drawFBO->GetDrawBuffers()` | ROW-O + LIVE | draw (memo-guarded at D:4390) | both | `BoundFramebufferRecord(Draw)` + `MGPFramebufferState.DrawBuffers` | ## Images | # | site | function | what is read | kind | freq | arm | dup | |---|---|---|---|---|---|---|---| | 85 | D:2650-2653 | `SyncImageTextureBinding` | `GetImageTextureBinding(unit)` (client array), `binding.Texture`, `IsWritableImageBufferTexture` (`Texture->GetStorageType()`) | ROW-O + LIVE | memo/rare | both | `BoundShaderImages` window (AP:683-686) | | 86 | D:2660, D:2671 | same | `SyncTextureObjectToBackend(imageBinding.Texture, true)` probe/mint; `imageBinding.Texture.get()` identity via `HandleOf` D:2634 | PROBE/MINT + LIVE | memo | both | `MGPImageView` | | 87 | D:2672-2675 | same | legacy arm reads `imageBinding.Layered/Layer/Level/Format` (rec arm reads the `MGPImageView` record) | LIVE | memo | leg | `MGPImageView` | | 88 | D:2682, D:2723-2724, D:2736, D:2744 | same | `GetTarget()`, `GetFormat()`, `imageBinding.Access` | LIVE | memo | both | `MGPImageView` | | 89 | D:2767, D:2773-2774 | `MarkWritableImageBufferTexturesGpuWritten` | `GetImageTextureBinding(unit)`, downcast to `TextureObjectBuffer*`, `GetBufferBindingSlot().GetBoundObject()` | ROW-O + LIVE | every draw when writable image-buffer units exist | both | `BoundShaderImages` | | 90 | M:10849 | `BoundImageUnitFormat` (via `ImageUnitFormatsStillMatch`, D:4472) | `MGB_CTX->GetImageTextureBinding(unit).Format` | ROW-O | programs with format-less image units | both | `MGPImageView.Format` | ## Programs / shader state | # | site | function | what is read | kind | freq | arm | dup | |---|---|---|---|---|---|---|---| | 91 | D:4781 | `PrepareForDraw` | `MGB_CTX->GetProgramForDraw()` — SharedPtr to client ProgramObject | ROW-O | draw | both | `DrawProgram` (AP:691) | | 92 | D:6126 | `PrepareForCompute` | `MGB_CTX->GetProgramForDispatch()` | ROW-O | every dispatch | both | `DispatchProgram` (AP:692) | | 93 | D:4339 | `SyncCurrentProgram` | `currentProgram->GetLinkStatus()/GetSpirvStatus()` | LIVE | draw | both | `ShaderCsos` record (AP:503) | | 94 | D:4415-4416 | `SyncCurrentProgram` | `g_backendProgramObjects.Find(currentProgram.get())` → `HandleOf` probe; `GetOrCreate(currentProgram)` mint | PROBE + MINT | draw | rec | `DrawProgram` handle | | 95 | D:4425-4429 | `SyncCurrentProgram` | `g_programTwinLookupMemo.Lookup(currentProgram)`; address-keyed `Find`/`GetOrCreate` mint | LIVE + MINT | draw | leg | `DrawProgram` | | 96 | D:4457-4459 | `SyncCurrentProgram` | `currentProgram->GetLinkVersion()/GetImageUnitVersion()` | LIVE | draw | both | `ShaderCso` serials | | 97 | D:4463-4464 → M:10821 | `SyncCurrentProgram` | `ComputeShaderStorageBlockBindingSignature(*currentProgram)` → `ProgramObject::GetShaderStorageBlockBindingOverrides()` | LIVE | draw (early-out on empty map) | both | none (overrides not pushed) | | 98 | D:4507 | `SyncCurrentProgram` | `twin->SyncToBackend(currentProgram)` — full client ProgramObject handoff | LIVE | memo | both | `CreateShaderState` record | | 99 | D:4242, D:4274-4313 | `ResolveGlobalConstantsRecord` | `HandleOf(program)` probe; `program->GetUBOSize()` compares | PROBE + LIVE | every draw with a default UBO | rec | `SetGlobalConstants` record (AP:3010) | | 100 | D:5257, D:5279-5282 | `BindCurrentTextures` | `programKey = currentProgram.get()`; `GetLifetimeId()`, `GetBackendStateVersion()`, `GetLinkStatus()` | LIVE | draw | both | `DrawProgram` | | 101 | D:5320, D:5333, D:5337 | `BindCurrentProgramWithResources` | `GetLinkStatus()/GetSpirvStatus()`, `currentProgram.get()`, `Find` probe | LIVE + PROBE | draw | both | `DrawProgram` | | 102 | D:5349, D:5372-5373 | same | `GetUBOSize()`, `GetUBOContentVersion()` | LIVE | every draw with UBO | both | `ShaderCso.Desc.GlobalUboSize` + `GlobalConstantsSerial` | | 103 | D:5380, D:5382, D:5425-5430 | same | `currentProgram->MapUBO()` — client shadow bytes (legacy fallback of the ring path); `GetUBOSize()` for upload | LIVE | ring-miss only | both | `GlobalConstants` bytes | | 104 | D:5463 | same | `currentProgram->GetUniformBlockBinding(i)` | LIVE | per UBO per draw | both | `ShaderCso.Desc` | | 105 | D:5522, D:5546 | same | `GetBackendStateVersion()`, `GetUniformSamplerOrImageUnitIndex(location)` | LIVE | memo | both | `ShaderCso` records | | 106 | D:1672, D:1701 | `SyncCurrentVertexAttributeValues` | `program->GetActiveAttributeLocationMask()`, `program->GetAttribType(location)` | LIVE | draw | both | none | | 107 | D:5679, D:5683-5692 | `GetCurrentBackendProgram` | `GetProgramForDraw()` + stash-miss `Find(currentProgram.get())` probe | ROW-O + PROBE | per indirect sub-draw | both | `DrawProgram` | | 108 | D:5733-5740 | `CurrentProgramMayNeedPerSubDrawBuiltins` | `currentProgram->GetLinkVersion()` | LIVE | per sub-draw query | both | `ShaderCso` serial | | 109 | D:6157, D:10023, D:6152, D:10022 | `GetBackendProgramId` / `ShaderStorageBlockBinding` | `GetProgramObject(...)`, `ValidateProgramName(...)` — frontend name-table probe | LIVE | API-driven, not per draw | both | none | | 110 | M:12954 | `BackendProgramObjectImpl::SyncToBackend` | `HandleOf(stateProgramObject)` | PROBE | per program sync/relink | rec | `ShaderCso` handle | ## Binding-point tables | # | site | function | what is read | kind | freq | arm | dup | |---|---|---|---|---|---|---|---| | 111 | D:510-511 | `SyncBufferBindingPoints` | `GetBufferBindingPoint(target, i)` — raw pointer into the live GLContext array; `point.GetBoundObject()` SharedPtr to client BufferObject | ROW-O + LIVE | per touched SSBO per draw; per UBO+SSBO per dispatch | **both — no arm switch** | `MGPShaderBuffers` record (MT:910) | | 112 | D:517, D:524, D:526, D:529-530 | same | `EnsureBufferResource(obj)` probe; `point.GetRange()`; `obj->GetSize()` | PROBE + LIVE | per bound point | both | buffer handle + range in record | | 113 | D:602, D:605 | `MarkShaderStorageBuffersGpuWritten` | touched count; `GetBufferBindingPoint(ShaderStorage,i).GetBoundObject()` | ROW-V + ROW-O/LIVE | **every draw** | both | `MGPShaderBuffers` | | 114 | D:569-570, D:575, D:583, D:585, D:588-589 | `SyncTransformFeedbackBindingPoints` | TF binding point, `GetBoundObject()`, ensure probe, `GetRange()`, `GetSize()` | ROW-O + LIVE + PROBE | rare (capture span open) | both | none | | 115 | D:621, D:628, D:630, D:636, D:642-648, D:658-660 | `SyncAtomicCounterBuffers` | count; `GetBufferBindingPoint(AtomicCounter, ...)`, `GetBoundObject()`, ensure, `GetRange()`, `GetSize()`, `MarkBufferGpuWritten` | ROW-V/O + LIVE + PROBE | rare (programs with atomic counters) | both | none | | 116 | D:669, D:675 | `SyncBoundBuffer` | `GetBufferBindingSlot(target).GetBoundObject()`, ensure probe | ROW-O + LIVE + PROBE | monolith indirect paths | leg | `VerbIndirectBuffer`/`VerbDispatchIndirectBuffer` | | 117 | D:5464-5466 | `BindCurrentProgramWithResources` | `GetBufferBindingPoint(Uniform, binding)`, `GetBoundObject()`, `GetRange()` | ROW-O + LIVE | per UBO per draw | both | none (UBO bindings not pushed as records) | | 118 | D:5472-5479 | same | `GetBufferResource(bufferObj.get())`, `IsBufferDrawClean`, `EnsureBufferResource(bufferObj)` | LIVE + PROBE | per UBO (epoch-stamped) | both | buffer handle | ## XFB | # | site | function | what is read | kind | freq | arm | dup | |---|---|---|---|---|---|---|---| | 119 | D:1387 | `StartPendingTransformFeedback` | `MGB_CTX->GetTransformFeedbackProgram()` | ROW-O | span start | both | none (`TransformFeedback*` context values are pushed, the program row is not) | | 120 | D:1405, D:1407-1415, D:1417 | same | `program->GetTransformFeedbackBufferCount()`, TF binding points, `GetBoundObject()`, ensure probe, `GetRange()`, `GetSize()`; pushes the client `SharedPtr` into `xfb.targets` | LIVE + ROW-O + PROBE | span start | both | none | | 121 | D:1427, D:1434, D:1439, D:1463, D:1497 | same | `NeedsScatteredTransformFeedbackCapture()`, `GetTransformFeedbackStride/PackedStride/BufferMode()` | LIVE | span start | both | none | | 122 | D:1138, D:1295 | `ReadbackCapturedRanges` / `ScatterCapturedRecords` | `HandleOfBuffer(target.buffer.get())` | PROBE | span end | rec | buffer handle (carried in `xfb.targets`, but stored as SharedPtr) | | 123 | D:1174, D:1191, D:1310, D:1342 | same | `IsBackendPersistentMapped()`, `WritebackFromBackend(...)`, `MappedData()` | LIVE | span end | leg | none | | 124 | D:1247, D:1282, D:1312 | `ScatterCapturedRecords` | `program->GetTransformFeedbackPackedStride/Stride/Varyings()` | LIVE | span end | both | none | ## Misc | # | site | function | what is read | kind | freq | arm | dup | |---|---|---|---|---|---|---|---| | 125 | PA:742-743 | `ServerVerbSink::OnResourceCopyRegion` | `gPipeInputs.GetTextureObject(SrcGlName/DstGlName)` — BARRIER_PULLED sticky-forward frontend name-table probe returning `SharedPtr` (`FieldOwnership.def:151`) | ROW-O | per `CopyImageSubData` | both | `MGPCopyRegion.Src/Dst` handles (MT:1302); the GL names are the declared P5b debt | | 126 | PA:591 | `OnDrawVbo` | `MGPipeHostBytes(*userIndices)` — SEG_STAGE bytes under split; client pointer under monolith | ROW-V (split: server bytes) | `kDrawHasUserIndices` arm only | both | record tail | | 127 | D:11761, D:11765, D:12192, D:12196, D:10232, D:11214, D:11451 | readback helpers (`ReadPixels`, `GetTexImage`, `StoreReadbackRowsToClient`, ...) | pack-PBO `GetBufferBindingSlot` + ensure | ROW-O + PROBE | readback paths | both | `MGPReadbackInfo` (MT:1388) | | 128 | MD:909, MD:930 | `MultiDrawImpl::DrawElementsBatch` | `BoundIndexBuffer()` (client EBO), `BoundIndexBufferId()` | LIVE | per multi-draw | both | `IndexBuffer` (AP:620) | ## (a) Scope construction sites `MGPipeFrontendKeyedRegistryScope` (exempts `MGPipeSlots()` from the apply-thread `Fatal{RoleViolation}`, `MobileGL/MG_Impl/Pipe/SlotAllocator.cpp:29`). Production sites: D:1609 (`ResolveVaoTwin`, VAO twin resolve, every draw) · D:1734 (`SyncTextureObjectToBackend`) · D:2588 (`ResolveShaderImageRecord`, image-seam identity) · D:3303 (`SyncCurrentFBO`, legacy half) · D:4236 (`ResolveGlobalConstantsRecord`) · D:4329 (`SyncCurrentProgram`, whole function) · D:4585 (`BindCurrentFBO`) · D:4926 (`ResolveAndBindUnitTextures`) · D:4993 (`ResolveUnitSamplerBackend`) · D:5328 (`BindCurrentTextures`) · D:5689 (`GetCurrentBackendProgram`) · D:6166 (`GetBackendProgramId`) · D:6729 (`DrawArrays`) · D:6775 (`MultiDrawArrays`) · D:8837 (`ScopedDetachedTextureFramebufferAttachments`) · D:9161 / D:9216 (mipmap depth/color) · D:9695 (`SyncRenderbufferObjectToBackend`) · D:10029 (`ShaderStorageBlockBinding`) · D:11884 (`GetTexImage`) · M:256 (`OnFrontendStateObjectDestroyed`, NoSession arm only) · M:4544 (`HandleOfSamplerViewForTexture`) · M:5882 (`RequireImageBindableStorage`) · M:7087 (`SyncMipmapsToBackend`) · M:8481 (`ResolvePushedBuiltinSampler`) · M:8717 (`ResolvePushedTextureParams`) · M:9256 / M:9310 (`SyncAttachmentObject`, legacy arm) · M:9709 (`SyncAttachmentSurface` RBO cross-check) · M:10382 / M:10403 (debug-only verification block in `BackendFramebufferObject::SyncToBackend`) · M:12949 (`BackendProgramObjectImpl::SyncToBackend`) · M:13273 (`BackendSamplerObject::SyncToBackend`) · M:13570 (`BackendRenderbufferObject::SyncToBackend`). Tests: `MobileGL/MG_Test/Wire/RemoteClientTest.cpp:1883, :1903`. `MGPipeReverseAnnouncementScope`: `MobileGL/MG_Impl/Pipe/ResourceTracker.h:723` (`MGPipeAnnounceBufferGpuWritten`) · M:2938 (`BufferImpl::HandleOfBuffer`) · test at `RemoteClientTest.cpp:1885`. Definitions: `MobileGL/MG_Impl/Pipe/SlotAllocator.h:199-233`. ## (b) Frontend objects/pointers held across records by the apply thread | site | storage | what | |---|---|---| | D:67 | `g_rawDepthFetchSamplerState` | SharedPtr to a frontend `SamplerObject` created and retained by the backend for process life | | D:1027, D:1054 (in `g_xfbObjects` D:1064) | `XfbObjectState::targets[].buffer`, `scatterProgram` | SharedPtrs to frontend `BufferObject`/`ProgramObject` pinned across a capture span | | D:117-121; instances D:138, D:140, D:146 | `TwinLookupMemo` ×3 (VAO/program/FBO) | raw frontend key + `WeakPtr` owner (legacy arm) | | D:1847/1849, D:1926/1928 (in `g_observedUnitBindings` D:1959) | `UnitBindingsSnapshot::legacySlotObjects`/`legacySamplerObject` | WeakPtrs to frontend textures/samplers | | D:2127-2131 (in `g_unitTextureSyncList` D:2140) | `UnitTextureSyncEntry` | borrowed pointer into a frontend binding slot + raw `ITextureObject*` | | D:2151-2152, D:2235-2236 | `g_fboTextureSyncList`, `g_readFboTextureSyncList` + raw `FramebufferObject*` keys | draw/read FBO attachment sync lists | | D:4097-4098 | `g_currentDrawFrontendProgram`/`g_currentDrawBackendProgram` | per-draw program/twin stash | | D:4107 | `g_broadcastMemoFbo` | raw `const FramebufferObject*` memo key | | D:4978-4985 | `g_unitSamplerLookupMemos` | per-unit `WeakPtr` frontend sampler + handle key | | D:5220-5235 | `g_resolvedTextureBindingMemos` | frontend `ProgramObject` as `const void*` key + bound-texture copies | | D:5588-5592 | `rawDepthSamplerObject` | pointer into frontend `TextureUnit` storage (transient per sampler pass) | | `Managers.h:1223/1254/1278` | `BackendVertexArrayObject::ResolvedDrawBuffers::Entry::frontend`, `iboFrontend` | raw client `BufferObject*` per-VAO memo | | `Managers.h:1378` | `BackendVertexArrayObject::m_syncedIndexBufferObject` | raw `BufferObject*` | | `Managers.h:2054` / M:10453 | `g_fboSyncedObjects[2]` | raw `FramebufferObject*` per target (compare-only) | | `Managers.h:320-324`; registries at 1423/1818/1943/2540/2678/2819 | legacy `StateBackendObjectRegistry::m_entries` | raw frontend address key + `StateWeakPtr` | | ST:156-167, ST:362-378 | `BackendSlotTable::Entry::stateRef`; `NoteStateForHandle`/`StateForHandle` | per-handle frontend `WeakPtr`/SharedPtr note | | PI:807, PI:812-814 | `m_boundVertexArray`, `m_programForDispatch`, `m_programForDraw`, `m_transformFeedbackProgram` | SharedPtrs to frontend objects inside `gPipeInputs` (BARRIER_PULLED rows) | | PI:808-811, PI:815 | `m_bufferBindingSlot[15]`, `m_bufferBindingPointBase[15]`, `m_framebufferBindingSlot[2]`, `m_imageTextureBindingBase`, `m_textureUnitBase` | raw pointers into frontend tables inside `gPipeInputs` | ## (c) `MGB_CTX->` accessors used from `MG_Backend/DirectGLES` whose `FieldOwnership.def` row is BARRIER_PULLED Under `MOBILEGL_PIPE_PUSH`, `MGB_CTX` = `&gPipeInputs` (`MobileGL/MG_Pipe/PipeInputsSwitch.h:17-27`); BARRIER_PULLED object rows dereference the live GLContext through the pointer bases at PI:807-815. | accessor | call sites | |---|---| | `GetFramebufferBindingSlot` / `GetFramebufferBindingSlotChecked` | D:182, D:197, D:6284, D:8408-8409; M:9397, M:9775 | | `GetBufferBindingSlot` | D:401, D:669, D:962, D:6852, D:6930-6931, D:7009, D:7082-7083, D:7263, D:7312, D:10232, D:11214, D:11451, D:11761, D:12192; `Utils.cpp:2353`; MD:88 | | `GetBufferBindingPoint` | D:510, D:569, D:605, D:628, D:1407, D:5464 | | `GetBufferBindingPointCount` | D:621 | | `GetBoundVertexArray` | D:1669, D:4770, D:6449, D:6724, D:6768; MD:96 | | `GetTextureUnitObject` | D:1871, D:1899, D:1934, D:1947, D:2374, D:4861, D:5157, D:5562, D:8472, D:9302, D:9416, D:9564, D:11874 | | `GetImageTextureBinding` | D:2650, D:2767; M:10849 | | `GetProgramForDraw` | D:4781, D:5308, D:5679, D:5734 | | `GetProgramForDispatch` | D:6126 | | `GetTransformFeedbackProgram` | D:1387 | | `GetProgramObject` / `ValidateProgramName` | D:6157, D:10023 / D:6152, D:10022 | | `RecordError` | D:8804; M:13668 | (`GetTouchedBufferBindingPointCount`, `GetActiveTextureUnit`, `GetCurrentVertexAttribute`, `GetPrimitiveRestartIndex`, `GetRenderStateParameters*`, `GetViewport`, `IsCapabilityEnabled`, `IsTransformFeedbackActive/Paused`, `GetPatch*`, `GetTransformFeedbackCapturedVertices`, `GetMaxTouchedTextureUnit` are RECORD-SUPPLIED; `GetTextureContextId`/`GetTextureBindGeneration`/`GetSamplingResolutionGeneration`/`GetPixelStoreParameters(false)` are APPLIER_DERIVED — server-owned, not listed.) ## What would have to change for the apply thread to read only server-owned memory **VAO/vertex.** The bound-VAO row (`GetBoundVertexArray`) must become a handle read of `BoundVertexElements` plus the `VertexElementsCso` record; `GetConfigVersion()` compares must be replaced by the applier's own `VertexBuffersSerial`/`IndexBufferSerial`/`ContentSerial`. The `DrawArrays`/`MultiDrawArrays` entry-path probe (#12/#13) and `SyncClientSideAttributesForDrawArrays` (#14) have no pushed equivalent for client-side arrays — those draws would need the client-array bytes staged with the record or the path refused under split. The legacy memo/registry arm (#4, #7, #8) dies with the legacy arm. **Buffers.** Every `HandleOfBuffer` probe on the hot path (#17, #25, #27, #30) is redundant with handles already present in `IndexBuffer`, the `VertexBuffers` window, and the shader-buffer records; the sync lists must key on handles and drop the stored raw frontend pointers. `GetBufferResource`/`GetBackendResource` (#32) needs a handle-keyed twin lookup. The reverse announcement `MarkBufferGpuWritten` (#30/#31) must consume the record-side handle instead of probing the client allocator per SSBO per draw. **Textures/samplers.** The memoised sync lists must stop borrowing pointers into client binding slots (#38); the unit windows already exist as `BoundSamplerViews`/`BoundSamplerStates` records, so the per-draw key check, the rebuild walk (#35-#40), and `ResolveAndBindUnitTextures` (#50-#53) can all run on handle arrays. Frontend-keyed `Find`/`GetOrCreate` (#47-#48, #55, #66) becomes `FindByHandle`/`GetOrCreateByHandle`. `GenerateMipmap`'s unit/slot resolution (#59) must take the texture from `VerbMipRes` only; the CPU-mip shadow path (#61) and the unkeyed all-twin detach sweep (#63) need staged shadow bytes and a handle-keyed affected-FBO list respectively. **Framebuffers/attachments.** The record arm of `SyncCurrentFBO` must stop validating the pushed record against the live binding slot (#68) — the record's own `Gen`/serial is the truth; the slot read at #69, the `NoteStateForHandle` SharedPtr stash (#70), and the `SyncToBackend(currentFBO)` walk (#71, #81, #82) must be replaced by applying `MGPFramebufferState` directly. `ForceBindCurrentFBO` (#74) needs a handle-keyed force-bind. The bound-form blit (#78, #79) must carry FBO handles and attachment descriptors in `MGPBlit` (or a companion record), since today nothing pushed identifies the two framebuffers or their attachments. **Images.** `GetImageTextureBinding` rows (#85-#90) are already duplicated by the `BoundShaderImages` window (`MGPImageView` records); the apply path needs to read the window + serial and drop the live binding read, the `binding.Texture` dereference, and the `HandleOf` identity check, including the format-less-image `BoundImageUnitFormat` lookup. **Programs/shader state.** The program rows (#91, #92) become `DrawProgram`/`DispatchProgram` handle reads; twin identity resolves by handle, eliminating #94/#95. Version checks (#96, #102, #108) map to `ShaderCso` serials; the per-draw `GetShaderStorageBlockBindingOverrides` signature (#97) and the attribute mask/type reads (#106) need to be baked into the `ShaderCso` record at create/relink time. `MapUBO` fallback bytes (#103) already have the `GlobalConstants` record as the pushed arm. The name-table probes (#109) are API-driven and can stay latency-bound or be handle-translated at emit. **Binding-point tables.** This family has no transport branch at all: `SyncBufferBindingPoints`, `MarkShaderStorageBuffersGpuWritten`, `SyncAtomicCounterBuffers`, and the per-UBO loop read the live GLContext arrays on every draw/dispatch (#111-#118). The SSBO side is partly covered by `MGPShaderBuffers`; UBO and atomic-counter bindings need equivalent pushed windows (handle + range per index) so the loops iterate applier state, and `EnsureBufferResource` becomes handle-keyed throughout. **XFB.** Capture setup (#119-#121) reads the TF program row and TF binding points live; the record arm needs the TF program handle and the captured-target list pushed at `BindStreamOutput`/begin, with `xfb.targets` storing handles instead of client SharedPtrs. The span-end readback probes (#122) then resolve by handle; the legacy writeback arm (#123) is monolith-only and unaffected. **Misc.** `OnResourceCopyRegion`'s sticky-forward name-table probe (#125) must resolve `MGPCopyRegion.Src/Dst` handles instead of the GL names. Readback pack-PBO reads (#127) need the pack buffer handle in `MGPReadbackInfo`-class records. The multi-draw EBO reads (#128) fold into the `IndexBuffer` handle. The two deliberate leaks (`gPipeInputs`, PI:818-835; `MGPipeSlots()`, `SlotAllocator.cpp:370-381`) and every frontend-pointer memo in list (b) must be retired or converted to handle keys before the R-1 barrier can be removed.