- SyncTextureObjectToBackend re-resolves its slot after the nested glTextureView sync. The
assert that it is still there is right - the caller holds the frontend object, so nothing
can reclaim its slot - but the function returns a REFERENCE, and in a release build the
assert is gone and the deref is not. It now falls back to GetOrCreate and puts the twin
the caller is about to use back.
- ResolveVaoTwin documents why its raw twin pointer survives the whole draw on both arms
instead of pointing at TwinLookupMemo, which the handle arm does not compile.
- Five cases against BackendSlotTable directly, through a stand-in state object that carries
only GetLifetimeId(), so none of them needs a GLContext, a driver or ES entry points.
- The load-bearing one is the ABA: an object dies, the sweep returns its slot, the next
object takes the same slot with a moved generation, and the predecessor handle answers
null instead of the successor twin. That is the property the address key could only paper
over with a weak_ptr.
- Gen moves on reuse and only on reuse; Find never mutates the table (which is what lets
SyncTextureObjectToBackend drop its second Find); a whole table saves, resets with = {} and
restores, which is the shape ScopedDirectGLESTextureBindings needs; and two tables of one
kind agree on a single object handle, because the identity comes from the client allocator
rather than from either table.
- ResolveVaoTwin, SyncCurrentProgram and BindCurrentFBO stop consulting the three
TwinLookupMemos on the handle arm. The memo existed to replace the registry hash probe
with an array index, and the slot table Find already IS that array index; its safety
argument - owner-equality of a weak snapshot against a recycled heap address - is
answered by the generation instead of re-derived per lookup. OwnerEquals, the memo
template and its three instances are now compiled only under MOBILEGL_PIPE_LEGACY_MEMOS.
- UnitSamplerLookupMemo compares {slot, gen} instead of owner-equality, and keeps its "a
miss is never cached" contract verbatim: the sampler twin is created later in the same
draw by the program pass.
- UnitBindingsSnapshot holds lifetime ids rather than weak_ptrs under push. It cannot hold
handles: a bound-but-never-synced texture has no twin and so no handle, and two of those
would read as equal. A lifetime id exists before the twin does and is never handed out
twice, which is the property the weak_ptr was there for.
- SyncTextureObjectToBackend keeps its by-value copy only to hold the twin alive across the
nested glTextureView sync; the second Find-or-create and the put-the-twin-back repair are
gone on the handle arm, because nothing there erases a live entry.
- The one direct-iteration site walks ForEachLive, which hands over a strong reference to
the framebuffer instead of the map key - the raw frontend address it had to null- and
expiry-check by hand before dereferencing.
- GetFramebufferBindingSlotFast becomes GetFramebufferBindingSlotChecked and, under push,
reads MGB_CTX->GetFramebufferBindingSlot(target) every time. This closes the P1 accessor
bypass: the cached raw pointer ran the checked accessor once per context change and then
handed out the pointee forever, so the per-verb poison stamp and the verify read-hook
were skipped at all five call sites.
- Every one of these is a push-build arm; the pull build compiles the pre-P2 text and its
symbol report stays 0 added / 0 removed / 0 renamed with no new resize.
- SlotTables.h: BackendSlotTable<StateObject, BackendObject, kKind>, indexed by
MGPipeHandle::Slot and validated by Gen, with the handle minted by the client
MGPipeSlotAllocator off the frontend object GetLifetimeId(). A lookup is one bounds
check plus one array index, and unlike the registry Find it never mutates the table,
so a returned BackendPtr* is not invalidated by the next call on it.
- StateBackendObjectRegistry keeps its name, its signature and all ~40 call sites, and
becomes the two-arm facade ARCHITECTURE.md 9.6 asks for: the pre-handle map under
MOBILEGL_PIPE_LEGACY_MEMOS, the slot table under MOBILEGL_PIPE_PUSH, chosen once per
process by EsprytSlotTablesEnabled() off kMGPipeSubsystemEsprytSlots. A clear bit with
MOBILEGL_PIPE_LEGACY_MEMOS=0 leaves no arm at all and is Fatal{PipeLegacyMemosDisabled}.
- The kind is a template parameter only in the push build (MGB_TWIN_KIND_ARG): a third
template argument would rename every instantiation and G1 wants the pull build byte
identical. Pull-build symbol report is 0 added / 0 removed / 0 renamed and adds no
resize beyond the three RenderState symbols the contract commit already moved.
- ForEachLive replaces begin()/end() under push and hands the callee a strong reference
to the state object instead of the map key, which was the raw frontend address.
- Nothing switches over yet: the tables are built and reachable, and the twins still go
through whichever arm the bit selects.