mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-14 13:33:23 +09:00
Two things land together: the plan and contract draft P6 needs before it starts, and the
working notes that until now existed on exactly one machine.
1. P6's plan and contract draft (neither started; not one line of code)
docs/Disaggregated/P6-SPAWN-PLAN.md the package plan
docs/Disaggregated/P6-CONTRACT-DRAFT.md the contract draft (moves to
MG_Remote/CONTRACT-P6.md when c6 lands)
The three findings that mattered, all written into the documents:
a. P6 is SMALLER than its roadmap cell suggests. The transport primitives are already in
the tree and already tested: SocketDoorbell including hangup detection and the two
lost-wakeup fences (Doorbell.cpp:211-360, four cases in FdPassingTest), SCM_RIGHTS fd
passing, ShmSegment::Adopt, and the SurfaceOp / WindowKind schema already defined in
protocol.fbs. inproc uses a real second shm mapping rather than an alias, and
SessionRings.h:22-28 says that was done so today is not the first time it runs. P6 is
assembly plus a control plane plus a process, not "write a socket transport".
b. The evidence for "P6 is only a transport swap" has EXPIRED. It was bought by P5c, whose
audit found 59 direct memory accesses in a tree that already looked clean. P5e changed
the tree again. So the first package, a6, is a read-only audit that writes no code, and
every contract row that depends on it is marked PENDING a6 rather than decided in
advance.
c. P6 does not get a game running on the phone. ANativeWindow* is meaningless in the child,
and protocol.fbs:186-196's own comment says Android transfers the window out of band -
that band is P12. P6 lands pbuffer / surfaceless / offscreen, which is what HeadlessGL,
the split-equivalent lane and trace replay need, and which is exactly what P6's own exit
gate measures. A real window reaching the server is a named refusal. This gets its own
section because "P6 is done" would otherwise read as "the game runs now".
The contract adds rule G: a role may not name a process-local handle. It extends P5c's
rule E (a role may not name the other role's memory) from memory to identity - EGLDisplay /
EGLSurface / EGLContext / ANativeWindow* / fd number / pid / mapped address either cross as
a token this contract defines or they do not cross, and a descriptor crosses only through
SCM_RIGHTS, where the kernel renumbers it.
What P5e left for P6 gets its own section. The latch semantics are already designed
(ARCHITECTURE.md:504) and are copied unchanged. What is new is that after run-ahead the
client no longer waits while the peer process can DIE: the client holds records that were
published and will never be applied, and Android's freezer suspends the child without
closing anything. So slow and dead must be distinguishable WITHOUT a timeout threshold.
That is P6's only new design; everything else is carriage. It shares a predicate with E1's
outstanding re-specification (ID-122), and the two should be designed together.
Two gates carried over from P5e's lessons: G2 requires integration-spawn's test-name set to
match integration-split's name for name, because a differing set means the lane is quietly
running less; and the arm-proof gate requires every entry to record the child pid and
transport=spawn in its OWN private log - ID-124 established that this kind of false green
really happens, and a spawn lane that actually ran monolith would pass every other gate.
S2's shape must be settled before it is written, and the document says why: E1 is the
cautionary case.
2. The working notes move into the repository (docs/Disaggregated/notes/, 415 .md files)
ROADMAP / CURRENT_STAGE_PROGRESS / MEASUREMENTS cite ruling numbers, audit line numbers and
the provenance of gate figures from ~/w7/notes/, a directory that exists on one machine and
is under no version control. A record cited hundreds of times that lives only locally is not
a record, so it moves in whole, with its directory structure intact.
Only .md moves. Scripts, run logs, census and gate result JSON, patches, APKs and nm dumps
stay where they are: they are evidence DATA rather than documents - large, regenerated per
run, and mostly carrying absolute paths and device serials that mean something only on the
machine that produced them. The mapping and this split are written in
docs/Disaggregated/notes/README.md.
Two exceptions are recorded there too: P6's BRIEF has been promoted to a first-class
document and keeps no copy under notes/, so the two cannot drift apart; and recovered/ holds
material salvaged from failed workflow runs, which may duplicate the like-named phase
directories and is kept because a few of those were the only complete version at the time.
CI's citation lint takes docs/Disaggregated/*.md only (non-recursive), and notes/ is
deliberately outside it: those notes' file:line citations were written against their own
code heads, and resolving them against today's HEAD would fail wholesale - which is not an
error, it is their correct state as a historical record. To check one, use the revision it
declares (--rev).
3. Inconsistencies fixed in passing
- README.md's status line still said P5e was in progress while the stage table already said
closed.
- CURRENT_STAGE_PROGRESS's next-step item 1 still listed the exit gate as pending, though
(a) and (b) were run in the previous commit; rewritten to closed plus the one item that
actually remains (E1).
- ROADMAP's P6 row still had the pre-phase plan in three of its cells; it now carries the
package order, the scope boundary and the two new gates.
Checked with check_doc_citations.py: 125 citations across docs/Disaggregated/*.md, 0 problems.
332 lines
25 KiB
Markdown
332 lines
25 KiB
Markdown
---
|
||
|
||
## 2. The non-`pGLContext` paths by which a backend reaches frontend objects
|
||
|
||
`pGLContext` is the only `MG_State::` global the backends touch (§0, "other MG_State globals: 0").
|
||
Every other reach is one of the five shapes below. P1 cannot succeed by rewriting only the 278
|
||
arrow sites: the accessors return **references to live frontend objects**, and the backends then
|
||
call ~200 distinct methods on those objects.
|
||
|
||
### 2.1 The one static cache of a frontend pointer
|
||
|
||
`MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp:142-155` — the only place a raw `GLContext*` is
|
||
stored across calls:
|
||
|
||
```
|
||
142: using FbBindingSlot =
|
||
143: std::remove_reference_t<decltype(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw))>;
|
||
144: static const MG_State::GLState::GLContext* g_fbSlotCacheContext = nullptr;
|
||
145: static Array<FbBindingSlot*, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fbSlotCache = {};
|
||
146: static inline FbBindingSlot& GetFramebufferBindingSlotFast(FramebufferTarget target) {
|
||
147: MG_State::GLState::GLContext* ctx = MG_State::pGLContext.get();
|
||
148: if (ctx != g_fbSlotCacheContext) { ... &ctx->GetFramebufferBindingSlot(...) ... }
|
||
```
|
||
|
||
It caches **interior pointers into the GLContext object** (the by-value binding-slot members),
|
||
keyed on the raw context address. Under P1 this cache has no meaning across a pipe boundary — the
|
||
slots are not addressable from the backend at all. Its five call sites are the hot framebuffer
|
||
path and each is a `GetFramebufferBindingSlot` read that the arrow-site table does **not** list
|
||
(they read through the cache, not through `pGLContext`):
|
||
|
||
| call site | enclosing function | verb path | what it reads |
|
||
|---|---|---|---|
|
||
| `DirectGLES/DirectGLES.cpp:1611` | `SyncNeccessaryTextures` | texture op (draw/dispatch) | draw FBO binding slot |
|
||
| `DirectGLES/DirectGLES.cpp:1905` | `SyncCurrentFBO` | framebuffer sync | slot for `target` |
|
||
| `DirectGLES/DirectGLES.cpp:2743` | `SyncCurrentProgram` | program/uniform sync | draw FBO binding slot |
|
||
| `DirectGLES/DirectGLES.cpp:2858` | `BindCurrentFBO` | framebuffer sync | slot for `target` |
|
||
| `DirectGLES/DirectGLES.cpp:2933` | `ForceBindCurrentFBO` | framebuffer sync | slot for `target` |
|
||
|
||
Each of those five also reads the slot's **version** (`BindingSlot::GetVersion()`) at
|
||
`DirectGLES.cpp:1614`, `1917`, `2745`, `2936` — a value block P1 must carry alongside the handle.
|
||
|
||
There are **no** other `GLContext&`/`GLContext*` parameters or local aliases anywhere in
|
||
`MG_Backend`: `grep -rn 'GLContext\s*[&*]' --include=*.cpp --include=*.h` returns exactly the one
|
||
line 144 above once `pGLContext` matches are excluded.
|
||
|
||
### 2.2 `SharedPtr<MG_State::GLState::…>` in signatures and members (the "handle-ify" surface)
|
||
|
||
177 lines. The ones that matter structurally are the **table-level** ones (they are the verb
|
||
signatures themselves) and the **member-level** ones (backend containers that own or weakly hold
|
||
frontend objects across frames).
|
||
|
||
Table-level (these five `GLFunctionsTable` entries take frontend objects **by handle today**):
|
||
|
||
| declaration | file:line |
|
||
|---|---|
|
||
| `ClearNamedFramebufferfv(const SharedPtr<FramebufferObject>&, …)` | `BackendObject.h:158` |
|
||
| `ClearNamedFramebufferfi(const SharedPtr<FramebufferObject>&, …)` | `BackendObject.h:160` |
|
||
| `ClearNamedFramebufferiv(const SharedPtr<FramebufferObject>&, …)` | `BackendObject.h:162` |
|
||
| `ClearNamedFramebufferuiv(const SharedPtr<FramebufferObject>&, …)` | `BackendObject.h:164` |
|
||
| `BlitNamedFramebuffer(const SharedPtr<FramebufferObject>& read, const SharedPtr<FramebufferObject>& draw, …)` | `BackendObject.h:168-169` |
|
||
| `GetTextureImage(const SharedPtr<ITextureObject>&, …)` | `BackendObject.h:186` |
|
||
| `CopyImageSubData(const CopyImageEndpoint& src, …, const CopyImageEndpoint& dst, …)` where `CopyImageEndpoint` is `{SharedPtr<ITextureObject> Texture; SharedPtr<RenderbufferObject> Renderbuffer;}` | `BackendObject.h:33-38`, `BackendObject.h:176-180` |
|
||
|
||
Their per-backend mirrors: `DirectGLES/DirectGLES.h:60,62,64,66,70,71`;
|
||
`DirectVulkan/DirectVulkan.h:36,38,40,42,76,77,101`;
|
||
`DirectVulkan/Renderer/VulkanRenderer.h:184,186,188,190,195,196,256,1400`.
|
||
|
||
Member-level (backend state that outlives a verb and holds frontend objects):
|
||
|
||
| holder | file:line | what it holds |
|
||
|---|---|---|
|
||
| `UniformManager::m_fallbackTexture2D` | `Renderer/UniformManager.h:305` | `SharedPtr<ITextureObject>` — a synthesised frontend texture the backend itself created |
|
||
| `UniformManager::m_fallbackMultisampleTextures` | `Renderer/UniformManager.h:308` | `UnorderedMap<Uint32, SharedPtr<ITextureObject>>` |
|
||
| `UniformManager::m_unboundStorageImageTextures` | `Renderer/UniformManager.h:317` | `UnorderedMap<Uint64, SharedPtr<ITextureObject>>` |
|
||
| `VkClearManager::m_aliveObjects` | `Renderer/VkClearManager.h:167` | `unordered_map<TextureIdentity, WeakPtr<ITextureObject>>` |
|
||
| `VkTextureManager::m_aliveObjects` | `Renderer/VkTextureManager.h:725` | `unordered_map<TextureIdentity, WeakPtr<ITextureObject>>` |
|
||
| `VkRenderPassManager` attachment records | `Renderer/VkRenderPassManager.h:45,51,330,359` | `WeakPtr<ITextureObject>` / `WeakPtr<RenderbufferObject>` |
|
||
| `VulkanRenderer` blit/clear helper programs | `Renderer/VulkanRenderer.h:378,379,380,388` | `SharedPtr<ProgramObject>` + two `SharedPtr<SamplerObject>` the backend **constructs itself** |
|
||
| DirectGLES raw-depth-fetch sampler | `DirectGLES/DirectGLES.cpp:55` (`static SharedPtr<SamplerObject> g_rawDepthFetchSamplerState`), built at `DirectGLES.cpp:150-160` | same pattern: backend-authored frontend object |
|
||
|
||
The last two rows are the P1 trap: these are frontend objects **created by the backend**, so under
|
||
disaggregation the server has no frontend to create them in. They need either a server-local
|
||
equivalent or a client-side create call.
|
||
|
||
### 2.3 The twin registries and the owner-keyed memos
|
||
|
||
`DirectGLES/Managers.h:271` `template <typename StateObject, typename BackendObject> class
|
||
StateBackendObjectRegistry` — hash map **keyed on the raw frontend object pointer**
|
||
(`using BackendMap = UnorderedMap<StateObject*, Entry>;`, `Managers.h:284`), each `Entry` holding
|
||
`{BackendPtr backend; StateWeakPtr stateRef;}` (`Managers.h:280-283`). Liveness is decided by
|
||
`entry.stateRef.expired()` (`Managers.h:318`, `Managers.h:339`).
|
||
|
||
Six instances, all reached by the draw path:
|
||
|
||
| registry | declared | keyed on |
|
||
|---|---|---|
|
||
| `VertexArrayImpl::g_backendVertexArrayObjects` | `Managers.h:803-804` | `VertexArrayObject*` |
|
||
| `TextureImpl::g_backendTextureObjects` | `Managers.h:1124-1125` | `ITextureObject*` |
|
||
| `FramebufferImpl::g_backendFramebufferObjects` | `Managers.h:1215-1216` | `FramebufferObject*` |
|
||
| `PrgramImpl::g_backendProgramObjects` | `Managers.h:1733-1734` | `ProgramObject*` |
|
||
| `SamplerImpl::g_backendSamplerObjects` | `Managers.h:1833-1834` | `SamplerObject*` |
|
||
| `RenderbufferImpl::g_backendRenderbufferObjects` | `Managers.h:1860-1861` | `RenderbufferObject*` |
|
||
|
||
On top of them, `DirectGLES/DirectGLES.cpp:83-115` `class TwinLookupMemo` — a direct-mapped memo
|
||
whose slot is `{StateObject* key; WeakPtr<StateObject> owner; BackendObject* twin;}`
|
||
(`DirectGLES.cpp:100-104`), indexed by Fibonacci-hashing the **frontend heap address**
|
||
(`DirectGLES.cpp:108-112`), validated by `OwnerEquals` (`DirectGLES.cpp:62-65`, a
|
||
`weak_ptr::owner_before` pair test). Three instances: `g_vaoTwinLookupMemo`
|
||
(`DirectGLES.cpp:122-123`), `g_programTwinLookupMemo` (`DirectGLES.cpp:124-125`),
|
||
`g_fboTwinLookupMemo` (`DirectGLES.cpp:130-131`); looked up at `DirectGLES.cpp:1204`, `2767`,
|
||
`2872` and stored at `1212`, `2775`, `2877`.
|
||
|
||
**Both mechanisms are pointer-identity based and cannot survive a process/thread split.** P1 has to
|
||
replace "frontend heap address + `weak_ptr` owner equality" with the wire handle plus a lifetime
|
||
id. The good news is that the frontend already exposes exactly that: `GetLifetimeId()` (§2.4).
|
||
|
||
### 2.4 Lifetime-id and version reads (the memo keys P1 must carry over the wire)
|
||
|
||
39 `GetLifetimeId()` reads:
|
||
|
||
| object kind | sites |
|
||
|---|---|
|
||
| program | `DirectGLES/DirectGLES.cpp:3333`, `3349`; `DirectVulkan/DirectVulkan.cpp:163`; `Renderer/UniformManager.cpp:482`, `696`, `2272`, `2384`, `2732`; `Renderer/VulkanRenderer.cpp:6044`, `6484`, `6524`, `6577`, `6648`, `6962` |
|
||
| texture | `Renderer/UniformManager.cpp:656`, `1746`, `1792`; `Renderer/VkClearManager.cpp:155`, `197`, `251`, `368`, `459`; `Renderer/VkRenderPassManager.cpp:642`; `Renderer/VkTextureManager.cpp:235`, `733` |
|
||
| sampler | `Renderer/UniformManager.cpp:654`, `1747`, `1793` |
|
||
| VAO | `Renderer/VertexInputStateFactory.cpp:48` (via `attr.Buffer`, actually a buffer); `Renderer/VulkanRenderer.cpp:3552`, `6107`, `6384`, `6965` |
|
||
| FBO | `Renderer/VkRenderPassManager.cpp:844`, `869`; `Renderer/VulkanRenderer.cpp:6112`, `6968` |
|
||
| buffer | `DirectGLES/Managers.cpp:2678`; `Renderer/VertexInputStateFactory.cpp:48` |
|
||
|
||
36 version reads:
|
||
|
||
| version | sites | what it gates |
|
||
|---|---|---|
|
||
| `BindingSlot::GetVersion()` (framebuffer slot) | `DirectGLES/DirectGLES.cpp:1614`, `1917`, `2745`, `2936` | the FBO-sync short-circuit (`g_fboSyncedSlotVersions`) |
|
||
| `BindingSlot::GetVersion()` (VAO index-buffer slot) | `DirectGLES/Managers.cpp:2307` | element-array rebind |
|
||
| `BufferObject::GetChangeSerial()` | `DirectGLES/Managers.cpp:794`, `1078`, `1151`, `1163`, `1203`, `1238`, `1245`, `1283`, `1468`, `1553`, `1580`, `1581`, `2679`; `Renderer/VkBufferManager.cpp:697`; `Renderer/VulkanRenderer.cpp:3849` | every buffer upload/re-upload decision on both backends |
|
||
| `ITextureObject::GetContentVersion()` | `DirectGLES/Managers.cpp:3649`, `3776`, `3826`, `4731`; `Managers.h:1012`; `Renderer/VkTextureManager.cpp:1646`, `1755`; `Renderer/VulkanRenderer.cpp:6293`, `7016` | texture upload short-circuit + the draw-time content-sum memo |
|
||
| `SamplerObject::GetVersion()` | `DirectGLES/Managers.cpp:4756`, `8534`; `Managers.h:1016`; `Renderer/UniformManager.cpp:655`; `Renderer/VkSamplerManager.cpp:401` | sampler twin re-sync |
|
||
| `ITextureObject::GetShapeVersion()` | `Renderer/VkTextureManager.cpp:1647`, `1756` | image recreate decision |
|
||
| `ITextureObject::GetTextureParamsVersion()` | 14 sites, e.g. `DirectGLES/Managers.cpp:3647`, `3777`, `3847` | texture-parameter re-push |
|
||
| `ProgramObject::GetBackendStateVersion()` / `GetLinkVersion()` / `GetImageUnitVersion()` / `GetUBOContentVersion()` / `GetBlockBindingVersion()` | `DirectGLES/DirectGLES.cpp:3335`, `3350`, `3540`, `2798`, `2799`, `3715`, `3401`; `Renderer/UniformManager.cpp:2273`; `DirectVulkan/DirectVulkan.cpp:165` | program twin re-sync + UBO re-upload |
|
||
| `VertexArrayObject::GetConfigVersion()` | `DirectGLES/DirectGLES.cpp:1258`, `2962`; `DirectGLES/Managers.cpp:2306` | VAO re-emit |
|
||
| `FramebufferObject::GetObjectVersion()` | `DirectGLES/DirectGLES.cpp:1615`, `1918`, `2937` | FBO twin re-sync |
|
||
|
||
### 2.5 `BufferBackendOps` — the reverse (frontend → backend) immediate-op table
|
||
|
||
Declared `MobileGL/MG_State/GLState/BufferState/BufferObject.h:76-123`; seven function pointers:
|
||
`Respecify`, `SubData`, `ResidentSubData`, `FlushMappedRange`, `OnDestroy`, `AcquirePersistentMap`,
|
||
`ReadbackFromGpu`. Every one takes `BufferObject&` — a **frontend object by reference**, and the
|
||
callee reads the frontend shadow through it.
|
||
|
||
| backend | table | registered | unregistered |
|
||
|---|---|---|---|
|
||
| DirectGLES | `g_glesBufferBackendOps` (`DirectGLES/Managers.cpp:1351-1361`, all seven filled, each wrapped in a `…Tracked` shim that bumps the buffer-mutation epoch) | `Managers.cpp:1374-1379` `RegisterBufferBackendOps()`, called from `DirectGLES/BackendObject_DirectGLES.cpp:849` and `DirectGLES/DirectGLES.cpp:10063` | `Managers.cpp:1381-1392`, also from `OnBackendContextDestroyed` (`Managers.cpp:1397`) |
|
||
| DirectVulkan | `g_vulkanBufferBackendOps` (`Renderer/VkBufferManager.cpp:106-113`, **six** filled — `ResidentSubData` is left null) | `Renderer/VkBufferManager.cpp:132` inside `VkBufferManager::Initialize` | `Renderer/VkBufferManager.cpp:139-141` inside `Shutdown` |
|
||
|
||
The epoch the GLES shims maintain (`Managers.cpp:1362-1372`, `CurrentBufferMutationEpoch` /
|
||
`BumpBufferMutationEpoch`) is itself a frontend-write-visibility signal the draw path memos read.
|
||
|
||
### 2.6 Backend → frontend **writes** (not pulls, but the same coupling, and P1 must not miss them)
|
||
|
||
These are calls the backend makes that mutate a frontend object. They are the mirror image of the
|
||
pull sites and a pipe with only a pull direction will silently drop them:
|
||
|
||
| method on frontend object | sites |
|
||
|---|---|
|
||
| `BufferObject::MarkGpuWritten()` | `DirectGLES/DirectGLES.cpp:510`, `1828`; `Renderer/UniformManager.cpp:1074`, `1230` |
|
||
| `BufferObject::SetBackendResource()` | `DirectGLES/Managers.cpp:1015`, `1482`; `Renderer/VkBufferManager.cpp:297` (+1) |
|
||
| `BufferObject::EnsureGpuResidentStorage()` | `Renderer/UniformManager.cpp:1072`, `1228`; `Renderer/VulkanRenderer.cpp:11326` |
|
||
| `BufferObject::WritebackFromBackend()` | `DirectGLES/Managers.cpp:1279` |
|
||
| `ITextureObject::AllocateStorage()` / `MarkStorageDirty()` / `TruncateMipmapLevels()` / `SetInternalFormat()` / `SetSamples()` / `SetFixedSampleLocations()` | `DirectGLES/DirectGLES.cpp:6326`, `6327`; `Renderer/UniformManager.cpp:1484`, `1489`, `1490`, `1496`, `1497`, `1498`, `1625`, `1628`, `1635` |
|
||
| `ProgramObject::SetDrawID()` / `SetBaseVertex()` / `SetBaseInstance()` / `SetViewportPassMask()` | `DirectGLES/DirectGLES.cpp:3683`, `3689`, `3677`, `3813` |
|
||
| `ProgramObject::AttachShader()` / `Link()` / `MarkUBOContentDirty()` / `SetBackendHashMemo()` | `Renderer/VulkanRenderer.cpp:4233`, `4234`, `4235`, `4313`, `4315`, `4665`, `8564`; `Renderer/ProgramFactory.cpp:3448` |
|
||
| `VertexArrayObject::SetBackendHashMemo()` / `SetBackendStateMemo()` / `SetBackendAuxMemo()` | `Renderer/VertexInputStateFactory.cpp:60`, `78`, `83` |
|
||
| `SamplerObject::SetWrapS/T/R`, `SetMinFilter`, `SetMagFilter`, `SetMipmapMode`, `SetLodRange` | `Renderer/VulkanRenderer.cpp:4270-4276` |
|
||
| `GLContext::RecordError()` | 6 sites, §3 accessor `RecordError` |
|
||
| `GLContext::InvalidateCompileEnv()` | 2 sites, §3 accessor `InvalidateCompileEnv` |
|
||
|
||
### 2.7 Frontend-object method surface reached through the pulled references
|
||
|
||
Counted by receiver-name heuristic over `MG_Backend/**` (excludes comment lines). This is the size
|
||
of the "handle + value block" problem P1 inherits once the arrow sites are replaced:
|
||
|
||
| frontend class | distinct methods called | call sites | top methods |
|
||
|---|---|---|---|
|
||
| `BufferObject` | 16 | 169 | `GetSize` 50, `MappedData` 42, `SyncPersistentMappedRange` 20, `GetChangeSerial` 15, `GetBackendResource` 11, `SyncGpuWrites` 6 |
|
||
| `ITextureObject` | 38 | 411 | `GetExternalIndex` 147, `GetTarget` 48, `GetFormat` 47, `GetTextureParamsVersion` 14, `GetStorageType` 11, `IsTextureView` 11, `GetLifetimeId` 10 |
|
||
| `ProgramObject` | 56 | 275 | `GetExternalIndex` 69, `GetLifetimeId` 14, `GetLinkStatus` 14, `GetUBOSize` 14, `GetUniformLocation` 12, `GetUniformSamplerOrImageUnitIndex` 10 |
|
||
| `FramebufferObject` | 9 | 115 | `GetAttachment` 39, `GetExternalIndex` 19, `IsDefaultFramebuffer` 18, `GetDrawBuffers` 16, `GetReadBuffer` 8, `GetObjectVersion` 7 |
|
||
| `SamplerObject` | 26 | 52 | `GetVersion` 5, `GetAllSamplerParameters` 3, then one-per-parameter getters (`GetWrapS/T/R`, `GetMinFilter`, …) |
|
||
| `VertexArrayObject` | 12 | 40 | `GetIndexBufferBindingSlot` 11, `GetAttribute` 6, `GetConfigVersion` 6, `GetBackendHashMemo` 4, `GetLifetimeId` 4 |
|
||
|
||
Total ≈ **1062** method calls on frontend objects across 157 distinct methods. `MGPResourceDesc`,
|
||
`MGPProgramDesc`, `MGPSamplerDesc`, `MGPFramebufferState` and `MGPVertexElements` in
|
||
`MG_Pipe/PipeFields.def` are the intended replacements; the field lists there are visibly narrower
|
||
than this surface, which is the second thing P1 has to reconcile.
|
||
|
||
---
|
||
|
||
## 3. (a) The `PipeInputs` accessor surface
|
||
|
||
Return types are from `MobileGL/MG_State/GLState/Core.h` (the `GLContext` class body starts at
|
||
`Core.h:61`). "sites" is the count from §0.2; full site lists are in §1.
|
||
|
||
### 3.1 Accessors that return a **handle to a frontend object** (must become an `MGPipeHandle`)
|
||
|
||
| accessor | signature (Core.h) | sites | verb paths served |
|
||
|---|---|---|---|
|
||
| `GetBufferBindingSlot` | `BindingSlot<BufferObject>& GetBufferBindingSlot(BufferTarget)` — `Core.h:78` | 29 | draw (indirect/parameter), dispatch (indirect), readback (PixelPack), buffer op (`SyncBoundBuffer`) |
|
||
| `GetBufferBindingPoint` | `BindingSlotRange1D<BufferObject>& GetBufferBindingPoint(BufferTarget, Uint)` — `Core.h:79` | 9 | buffer op (SSBO/UBO/atomic), xfb, program/uniform sync (descriptor) |
|
||
| `GetFramebufferBindingSlot` | `BindingSlot<FramebufferObject>& GetFramebufferBindingSlot(FramebufferTarget)` — `Core.h:473` | 19 (+5 via the fast cache, §2.1) | framebuffer sync, clear, blit, readback, draw, pipeline build |
|
||
| `GetTextureUnitObject` | `TextureUnit& GetTextureUnitObject(Int)` — `Core.h:120` | 19 | texture op, program/uniform sync (descriptor) |
|
||
| `GetImageTextureBinding` | `ImageTextureBinding& GetImageTextureBinding(Int)` (+ const overload) — `Core.h:121-122` | 7 | texture op (image unit), program/uniform sync (storage image/texel) |
|
||
| `GetBoundVertexArray` | `const SharedPtr<VertexArrayObject>& GetBoundVertexArray()` — `Core.h:102` | 12 | vertex sync, draw |
|
||
| `GetProgramForDraw` | `const SharedPtr<ProgramObject>& GetProgramForDraw()` — `Core.h:174` | 7 | draw, program/uniform sync |
|
||
| `GetProgramForDispatch` | `const SharedPtr<ProgramObject>& GetProgramForDispatch()` — `Core.h:179` | 3 | dispatch |
|
||
| `GetProgramObject` | `const SharedPtr<ProgramObject>& GetProgramObject(Uint)` — `Core.h:159` | 3 | program/uniform sync (`ShaderStorageBlockBinding`, `GetBackendProgramId`) |
|
||
| `GetTextureObject` | `const SharedPtr<ITextureObject>& GetTextureObject(Uint)` — `Core.h:110` | 1 | texture op (`VkTextureManager::SyncTextureAndGetDescriptor` liveness check) |
|
||
| `GetTransformFeedbackProgram` | `const SharedPtr<ProgramObject>& GetTransformFeedbackProgram() const` — `Core.h:329` | 3 | xfb |
|
||
| `GetCurrentVertexAttribute` | `const CurrentVertexAttributeValue& GetCurrentVertexAttribute(Uint)` — `Core.h:106` | 2 | vertex sync |
|
||
|
||
**The four in bold below return a reference to a mutable slot, not a value**, so a `PipeInputs`
|
||
accessor cannot simply copy them: `GetBufferBindingSlot`, `GetBufferBindingPoint`,
|
||
`GetFramebufferBindingSlot`, `GetTextureUnitObject`. Every call site immediately follows with
|
||
`.GetBoundObject()`, `.GetRange()`, `.GetVersion()` or `.GetSamplerObject()`; those are the value
|
||
blocks P1 must materialise. `PipeFields.def` already models them as `MGPVertexBuffer`,
|
||
`MGPIndexBuffer`, `MGPIndirectBuffers`, `MGPBufferRange`, `MGPShaderBuffers`, `MGPBoundView`,
|
||
`MGPSamplerViews`, `MGPImageView`, `MGPFramebufferState`.
|
||
|
||
### 3.2 Accessors that return a **plain value** (a `PipeInputs` field can hold them verbatim)
|
||
|
||
| accessor | return type (Core.h line) | sites |
|
||
|---|---|---|
|
||
| `GetActiveTextureUnit` | `Int` (146) | 8 |
|
||
| `GetBlendColor` | `const FloatVec4&` (278) | 1 |
|
||
| `GetBlendEquationIndexed` | `void(Uint, BlendEquation&, BlendEquation&)` — **out-params** (255) | 1 |
|
||
| `GetBlendFuncIndexed` | `void(Uint, BlendFactor&×4)` — **out-params** (250) | 1 |
|
||
| `GetBufferBindingPointCount` | `constexpr SizeT(BufferTarget)` (80) | 3 |
|
||
| `GetTouchedBufferBindingPointCount` | `SizeT(BufferTarget)` (86) | 2 |
|
||
| `GetClampReadColor` | `GLenum` (235) | 1 |
|
||
| `GetClearColor` | `const FloatVec4&` (272) | 1 |
|
||
| `GetClearDepth` | `Float` (274) | 1 |
|
||
| `GetClearStencil` | `Uint32` (276) | 1 |
|
||
| `GetColorMaskIndexed` | `BoolVec4(Uint)` (270) | 6 |
|
||
| `GetCullFaceMode` | `CullFaceMode` (294) | 1 |
|
||
| `GetDepthFunc` | `DepthTestFunc` (259) | 1 |
|
||
| `GetDepthMask` | `Bool` (261) | 5 |
|
||
| `GetDepthRangeIndexed` | `const FloatVec2&(Uint)` (282) | 1 |
|
||
| `GetLineWidth` | `Float` (211) | 1 |
|
||
| `GetLogicOp` | `LogicOperation` (257) | 1 |
|
||
| `GetMaxTouchedTextureUnit` | `Int` (126) | 1 |
|
||
| `GetMinSampleShadingValue` | `Float` (289) | 1 |
|
||
| `GetPatchDefaultInnerLevel` | `const FloatVec2&` (219) | 3 |
|
||
| `GetPatchDefaultOuterLevel` | `const FloatVec4&` (217) | 3 |
|
||
| `GetPatchVertices` | `Uint` (215) | 3 |
|
||
| `GetPixelStoreParameters` | `PixelStoreParameters(Bool isUnpack)` (292) | 6 (all with `isUnpack=false`) |
|
||
| `GetPolygonModeFront` | `GLenum` (237) | 1 |
|
||
| `GetPolygonOffsetFactor` | `Float` (222) | 1 |
|
||
| `GetPolygonOffsetUnits` | `Float` (223) | 1 |
|
||
| `GetPrimitiveRestartIndex` | `Uint32` (240) | 3 |
|
||
| `GetProvokingVertexMode` | `ProvokingVertexMode` (298) | 1 |
|
||
| `GetRenderStateParameters` | `const RenderStateParameters&` (205) | 11 |
|
||
| `GetScissorBox` | `const IntVec4&` (300) | 3 |
|
||
| `GetStencilState` | `const StencilFaceState&(StencilFace)` (266) | 8 |
|
||
| `GetViewport` | `IntVec4` (207) | 1 |
|
||
| `GetViewportIndexed` | `const FloatVec4&(Uint)` (209) | 1 |
|
||
| `IsCapabilityEnabled` | `Bool(CapabilityInput)` (242) | 29 |
|
||
| `IsCapabilityEnabledIndexed` | `Bool(CapabilityInput, Uint)` (244) | 1 |
|
||
| `IsTransformFeedbackActive` | `Bool` (325) | 5 |
|
||
| `IsTransformFeedbackPaused` | `Bool` (326) | 2 |
|
||
| `HasOpenTransformFeedbackSpan` | `Bool(Uint64 lifetimeId)` (450) — **NOT in PipeFilled.inc** | 1 |
|
||
| `ValidateProgramName` | `Bool(Uint) const` (157) — pseudo-call `kClientResolved` | 3 |
|
||
|
||
The 14 distinct `CapabilityInput` values the 30 capability reads ask for (P1's `CapabilityBits`
|
||
block, which `PipeFields.def` already names in `MGP_FIELDS_ResidualValueBlock`): `FramebufferSrgb`,
|
||
`RasterizerDiscard`, `PrimitiveRestart`, `PrimitiveRestartFixedIndex`, `Multisample`, `SampleMask`,
|
||
`CullFace`, `DepthTest`, `PolygonOffsetFill`, `ColorLogicOp`, `StencilTest`, `SampleShading`,
|
||
`ScissorTest`, `Blend` (only via `IsCapabilityEnabledIndexed`).
|
||
|
||
### 3.3 Accessors that return a **version / generation counter** (memo keys)
|
||
|
||
| accessor | return type (Core.h line) | sites | memo it keys |
|
||
|---|---|---|---|
|
||
| `GetPipelineStateVersion` | `Uint` (204) | 3 | Vulkan pipeline cache + fast-path snapshot |
|
||
| `GetRenderStateParametersVersion` | `Uint` (202) | 2 | GLES render-state shadow, Vulkan dynamic-state tail |
|
||
| `GetTextureBindGeneration` | `Uint64` (130) | 5 | per-draw sampled-texture set |
|
||
| `GetSamplingResolutionGeneration` | `Uint64` (136) | 9 | mipmap-completeness re-resolve |
|
||
| `GetTextureContextId` | `Uint64` (143) | 6 | disambiguates a recreated context at the same address |
|
||
| `GetTransformFeedbackGeneration` | `Uint64` (334) | 1 | xfb counter-buffer rebind |
|
||
| `GetTransformFeedbackCapturedVertices` | `Uint64` (357) | 1 | GLES scatter-capture record count |
|
||
| `GetTransformFeedbackPausedPrimitiveCounter` | `Uint64` (349) | 2 | xfb primitives-written query |
|
||
| `GetBoundTransformFeedbackLifetimeId` | `Uint64` (441) — **NOT in PipeFilled.inc** | 1 | Vulkan xfb counter-slot ownership |
|
||
|
||
### 3.4 Non-read accessors (reverse channel / client-resolved)
|
||
|
||
| accessor | signature | sites | Coverage.def classification |
|
||
|---|---|---|---|
|
||
| `RecordError` | `void(ErrorCode, UniquePtr<ErrorInfo>)` — `Core.h:66` | 6 | `kReverseChannel` |
|
||
| `InvalidateCompileEnv` | `void()` — `Core.h:506` | 2 | `kClientResolved` |
|
||
| `ValidateProgramName` | `Bool(Uint) const` — `Core.h:157` | 3 | `kClientResolved` |
|
||
|
||
### 3.5 Reconciliation with `PipeFilled.inc`'s 61 fields
|
||
|
||
| | |
|
||
|---|---|
|
||
| fields in `PipeFilled.inc` (`generated/PipeFilled.inc:28-90`, asserted at `:92`) | **61** |
|
||
| distinct accessors read by the backends on this tree | **61** (+`.get()`) |
|
||
| in `PipeFilled.inc` but **never read by a backend** | 1 — `GetBoundTransformFeedbackName` (`PipeFilled.inc:32`) |
|
||
| read by a backend but **absent from `PipeFilled.inc`** | 2 — `GetBoundTransformFeedbackLifetimeId`, `HasOpenTransformFeedbackSpan` |
|
||
| corrected field count for P1 | **62** |
|
||
|
||
Additional structural gaps that PipeInputs-as-generated does not model, all of which have real
|
||
sites:
|
||
|
||
1. **`.get()` on the context itself** (`DirectGLES.cpp:147`) has no accessor to map to; the whole
|
||
`GetFramebufferBindingSlotFast` cache (`DirectGLES.cpp:142-155`, 5 call sites) must be deleted
|
||
or rebuilt on handles.
|
||
2. **Slot version reads** (`BindingSlot::GetVersion()`, 5 sites, §2.4) ride on
|
||
`GetFramebufferBindingSlot`/`GetBoundVertexArray` and are not separate fields today;
|
||
`MGP_FIELDS_MGPBindRenderState` has a `Version` and `MGP_FIELDS_MGPFramebufferState` has a
|
||
`ContentHash`, but no field carries a framebuffer **binding-slot** version.
|
||
3. **`GetBufferBindingSlot` is polymorphic over `BufferTarget`** — `Coverage.def:37-42` already
|
||
flags this ("its rows split across set_vertex_buffers, set_index_buffer, set_indirect_buffers
|
||
and set_shader_buffers once the inventory carries the target argument (P1)"). The five targets
|
||
actually asked for are `DrawIndirect` (13 sites), `Parameter` (4), `PixelPack` (7),
|
||
`DispatchIndirect` (1), plus a runtime `target` parameter at `DirectGLES.cpp:518`
|
||
(`SyncBoundBuffer`, called with `DrawIndirect` @`:664` and `DispatchIndirect` @`:691`).
|
||
4. **`GetBufferBindingPoint` is likewise polymorphic**: `TransformFeedback` (3), `ShaderStorage`
|
||
(1), `AtomicCounter` (1), `Uniform` (2), plus a runtime `target`/`bufferTarget` at
|
||
`DirectGLES.cpp:370` and `UniformManager.cpp:1194`.
|
||
|