20 KiB
S2 — textures and samplers under run-ahead (P5e scouting report)
Head 2fde7034. Paths under MobileGL/. "The record arm" = MOBILEGL_PIPE_PUSH +
TextureResourceSubsystemEnabled() / SamplerSubsystemEnabled().
Headline. The SAMPLER half is already run-ahead-clean on its record arm, and
SamplerImpl::ResolveSamplerCsoTwin (Managers.cpp:13457-13486) is the exact shape the TEXTURE
half needs: handle in, record first, GetOrCreateByHandle, serial-gated sync, no frontend touch,
no allocator probe. The texture half is one step behind — its three sync bodies already read the
record and the staged store, but every one re-enters through
g_backendTextureObjects.HandleOf(frontendObject) and the per-draw work list borrows frontend
binding slots. No new record and no new wire field on the draw path; one signature, four
line-level substitutions, one ruling.
1. The live reads at 2fde7034
live = frontend dereference; id = GetLifetimeId() + client allocator probe; mint =
Acquire, a WRITE into it; rec = reads nothing frontend; hit = runs on every steady draw.
1.1 The per-draw keys — already server-owned, nothing to do
CaptureDrawTextureSyncKeys DirectGLES.cpp:2188-2211 reads GetTextureContextId :2189,
GetMaxTouchedTextureUnit :2203, GetSamplingResolutionGeneration :2204 — rec, hit: the
two generations are APPLIER_DERIVED (FieldOwnership.def:126-131), answered from
MGPipeApplierTextureShutterSerial()/ContextSerial() under a server stamp
(PipeInputs.h:490-527); the mark is RECORD_SUPPLIED via set_context_values (Coverage.def:260,
MGPipeTypes.h:1694). CurrentUnitBindingsEpoch's record arm :2044-2071 ->
UnitBindingsEpochFromRecords :2003-2042 reads the two counts and the two serials — rec, hit.
1.2 SyncNeccessaryTextures, unit half (:2321-2391)
| site | read | kind |
|---|---|---|
:2346 PairingsIntact(g_unitTextureSyncList) |
per entry entry.slot->get(), a const SharedPtr<ITextureObject>* borrowed INTO the frontend unit's slot (:2127-2131, borrowed at :2381) |
live, hit |
:2356 IsDrawSyncClean(entry.slot->get(),...) -> Managers.h:1641-1658 |
GetTextureParamsVersion, GetContentVersion, GetSamplerObject()->GetVersion(), GetStorageType(); then :2358-2361 hands *entry.slot to the three syncs |
live, hit |
:2374-2382 memo MISS |
GetTextureUnitObject(index)/unit, every GetAllBindingSlots() slot's GetBoundObject(), IsUndefinedDefaultTexture, SyncTextureObjectToBackend(obj) |
live, id, mint |
1.3 SyncTextureObjectToBackend (:1723-1806) and the three sync bodies
| site | read | kind |
|---|---|---|
:1734 scope, :1736 Find(obj), :1737 GetOrCreate(obj), and the re-resolve :1781,:1797 |
HandleOf -> GetLifetimeId + MGPipeSlots().FindByLifetimeId (SlotTables.h:430-450); miss mints (:244-250) |
id, mint |
:1757-1758 RequireImageBindableStorage(obj) -> Managers.cpp:5816-5975 |
HandleOf :5884, GetTarget :5888, GetUploadTargets/GetMipmapTexelSize/GetMipmapByteSize/MarkStorageDirty :5931-5940 — the last is ALREADY Fatal{RoleViolation,"texture-legacy-arm"} from the apply thread (MipmapStorage.cpp:39-51) |
id, live |
:1760 -> SyncTextureParamsToBackend Managers.cpp:8803+ |
prologue ResolvePushedTextureParams :8712-8743 = scope :8717 + HandleOf :8719; body :8876-8877 GetTarget() on both arms; :8841,:8874 GetExternalIndex (logs) |
id, live |
:1761 -> SyncBuiltinSamplerToBackend :8554+ |
prologue ResolvePushedBuiltinSampler :8472-8551 = scope :8481 + HandleOf :8483; body :8620-8621 GetTarget() on both arms |
id, live |
:1762 -> SyncMipmapsToBackend :7049+ |
:7060 IsTextureView() BEFORE the record is resolved; :7087 scope + :7089 HandleOf; :8223-8231 the Buffer arm's GetBufferBindingSlot().GetBoundObject(); GetExternalIndex in ~14 log lines. Everything else on the staged arm is the record + ServerStagedTexture() (:6902-6985) |
id, live (4 sites) |
1.4 BindCurrentTextures / ResolveAndBindUnitTextures
:5277-5284memo key:currentProgram->GetLifetimeId/GetBackendStateVersion/GetLinkStatus— live, hit (S4's rows; this memo is the consumer).:5290'smemcmpvsg_boundTexturesCacheis server memory — rec.:4861-4948ResolveAndBindUnitTextures(memo MISS) — live, id:GetTextureUnitObject(unit):4860, two passes overGetAllBindingSlots():4876,GetBoundObject,IsUndefinedDefaultTexture:4884,GetExternalIndex:4886,GetTarget:4890, the alias arbitersampledTargetForUnit:4845-4857(GetMaxUniformLocation/GetUniformSamplerOrImageUnitIndex/GetUniformType),GetSamplerObject:4914-4916,SamplesAsIncompleteTexture:4918, scope:4926+Find(obj):4928, unbind walk:4945-4947(GetTargetper slot).
1.5 The samplers
BindCurrentUnitSamplersgate:5051-5059(keys +g_boundSamplersCache) and record arm:5106-5151(st.SamplerStateStart/Count/BoundSamplerStates[],UnbindSampler,ResolveSamplerCsoTwin) — rec, hit; already clean.- decline arm
:5152-5169:GetTextureUnitObject(unit).GetSamplerObject():5157->ResolveUnitSamplerBackend:4988-5026= scope:4993+HandleOf:4999— live, id. - sampler pass in
BindCurrentProgramWithResources:5562-5596— live:GetTextureUnitObject(unit):5562,GetSamplerObject:5563,GetBindingSlot(Texture2D).GetBoundObject():5564-5565,samplerObject->GetLodBias():5573,boundTexture->GetSamplerObject()->GetLodBias():5580,texture2D->GetSamplerObject():5591,texture2D->GetFormat():5595. :5609-5620CSO-twin arm — rec.:5624-5632else arm:ResolveUnitSamplerBackend(id) theng_backendSamplerObjects.GetOrCreate(samplerObject):5626— mint, the only one left here.- Off the steady draw path (waited ops under P5e-2):
SyncTextureObjectToBackendfromCopyImageSubData:8294-8295,GenerateMipmap:9575,:9578, the copy-endpoint resolve:9735, the texture ops:10046+, readbacks:11695,:11866;Find(texture.get())at:8512,:8839,:9305,:9419,:11886.
2. What the records and applier state ALREADY carry
| need | carrier | citation |
|---|---|---|
which texture each unit samples, PROGRAM-RESOLVED, one per unit, no alias contest, with IsUndefinedDefaultTexture + SamplesAsIncompleteTexture already applied (entry -> null) |
st.BoundSamplerViews[unit]={View,Texture,Unit}, SamplerViewStart/Count/Serial |
PipeApply.h:673-676, MGPipeTypes.h:856-862, emitter SamplerEmit.h:733-789, the two drops :760-765 |
| the frontend TARGET to bind, aliasing format, view restrictions, samples | SamplerViewCsos[View].View.{Target,InternalFormat,MinLevel..NumLayers,Samples,FixedSampleLocations} |
MGPipeTypes.h:524-535, SamplerEmit.h:899-916 |
which sampler CSO each unit carries, and its 15 values (lodBias, filters, mipmapMode, compareMode, border) with a server-owned Serial |
st.BoundSamplerStates[] + SamplerCsos[h].Params |
PipeApply.h:335-341, :678-681, MGPipeValueTypes.h:462-486 |
a texture's params, built-in sampler CSO, the two resync bits; and its storage shape, target, format, levels, immutability, ImageBindableHint, ViewOf, texbuffer backing |
TextureResources[h].Params+ParamsSerial, .Desc |
PipeApply.h:265-281, MGPipeTypes.h:295-356, :548-566 |
| "did storage/texels move", and the twin's own "what did I last sync" | .Serial + PendingUploads + ServerStagedTexture() defined/GPU-dirty; m_syncedResourceSerial, m_syncedParamsSerial, m_syncedBuiltinSamplerSerial+m_syncedBuiltinSampler |
PipeApply.h:240-318, Managers.cpp:6902-6985, Managers.h:1785-1812 |
| texture death by handle on the apply thread, no allocator; the handle-keyed adopt/recycle rule | object_death -> ReleaseTwinsForWireObjectDeath -> ReleaseByHandle; GetOrCreate(MGPipeHandle)/FindByHandle |
Managers.cpp:328-337, SlotTables.h:278-341, :420-425, :488-506 |
The brief's two questions: yes, object_death covers textures since P5c ct
(Managers.cpp:331-332); and the default texture (name 0) DOES have a record —
TextureEmit.h:806-833 self-heals a create+respecify when its first set_texture_params is
refused, written for exactly that case.
3. The gap
- G-S2-1 (a signature, not a field). No
SyncTextureToBackendByHandle(MGPipeHandle); every entry takesconst SharedPtr<ITextureObject>&and re-derives the handle withHandleOf. Needed: one handle-taking entry point + handle-taking prologues (ResolvePushedTextureParams(MGPipeHandle),ResolvePushedBuiltinSampler(MGPipeHandle)). - G-S2-2 (four live reads inside the handle arm, all with an existing carrier).
Managers.cpp:8876-8877and:8620-8621GetTarget()->Desc.Target(viaStagedTextureTargetForPipeTarget,:6905);:7060IsTextureView()->Desc.ViewOf(MGPipeTypes.h:356), record resolved first;:8223-8231->Desc.BufferForTexBuffer(already read at:8240). Plus G-S2-4: ~20 log lines printGetExternalIndex();Desc.GlNameForDiag(MGPipeTypes.h:349) is the declared diagnostics carrier. Mechanical. - G-S2-3 (no handle-arm clean gate).
IsDrawSyncClean(const ITextureObject*,...)(Managers.h:1641-1658) has no record-keyed sibling, so the memo-HIT path is frontend-bound every draw. Needs no new state — see §4. - G-S2-5 (RULING).
RequireImageBindableStorage's re-dirty (Managers.cpp:5930-5962) reaches back into the client's storage model and already aborts under split;ImageBindableHintis the prevention and P9 owns the pull, so P5e should delete the frontend argument and leave the split arm a NAMED refusal at the entry, not build half a terminator. - G-S2-6 (narrowing, needs the integrator's word). Driving the unit list off
BoundSamplerViewssyncs only the program-resolved texture per unit, where today the walk syncs every slot of every touched unit. The union of (views + image units + the FBO attachment lists + the waited texture ops) still covers every texture anything reads, so this narrows WORK, not coverage — but it is a behaviour delta against the monolith arm and must be named under G1.
4. The retirement design
Twin key. g_backendTextureObjects stays one table; every draw-path resolution becomes
GetOrCreateByHandle(view.Texture) / FindByHandle(view.Texture). HandleOf, Find(StateObject*)
and GetOrCreate(const StatePtr&) become unreachable from the apply thread, so the existing
MGPipeRefuseAllocatorFromApplyThread guards (SlotTables.h:255-266, :436-441) stop being
scope-exempted here, and this family's MGPipeFrontendKeyedRegistryScope sites —
DirectGLES.cpp:1734, :4926, :4993, Managers.cpp:5882, :7087, :8481, :8717 — are
deleted, not moved. Sync shape (the ResolveSamplerCsoTwin template,
Managers.cpp:13457-13486):
BackendTextureObject* SyncTextureToBackendByHandle(MGPipeHandle res, Bool imageBindable) {
const auto* rec = PipeTextureRecordForHandle(res); // record FIRST; null -> named decline
auto* slot = rec ? g_backendTextureObjects.GetOrCreateByHandle(res) : nullptr; // adopt/recycle by Gen
if (!slot) return nullptr; // backward Gen -> named decline
if (!*slot) *slot = MakeShared<BackendTextureObject>();
(*slot)->SyncTextureParamsToBackend(res, *rec); (*slot)->SyncBuiltinSamplerToBackend(res, *rec);
(*slot)->SyncMipmapsToBackend(res, *rec);
if ((*slot)->NeedsParameterResync()) { /* params; builtin */ }
return slot->get();
}
The by-value twin copy + second Find (:1745-1755, :1781-1805) go with the map arm: a slot
entry is an array element, so the nested-glTextureView relocation hazard is answered by
re-indexing, not by a refcount.
"Is it clean" from server-owned serials. IsDrawSyncCleanByRecord(res, rec) is EXACTLY the
conjunction of the three handle-arm prologues' own early-outs — the relation
Managers.h:1626-1640 already states for the frontend version:
m_isInitialized && m_syncedResourceSerial != 0 && m_syncedResourceSerial == rec->Serial // :7133-7139
&& rec->PendingUploads.empty()
&& m_syncedParamsSerial == rec->ParamsSerial && rec->Params.ForceResync == 0 && !m_forceTextureParamsResync
&& m_syncedBuiltinSampler == rec->Params.BuiltinSampler // :8735-8741
&& m_syncedBuiltinSamplerSerial == PipeSamplerCsoRecordForHandle(rec->Params.BuiltinSampler)->Serial
&& rec->Params.SamplerResync == 0 && !m_forceSamplerResync // :8539-8548
&& (TextureStorageType)rec->Desc.StorageKind == TextureStorageType::Mipmap
No new member, no new field. The contextId/samplingGeneration arguments disappear: the
applier's ContextSerial and TextureShutterSerial (PipeApply.h:697-717) are what the keys
already carry.
The unit work list and the bind. UnitTextureSyncEntry (:2127-2132) becomes
{MGPipeHandle Res, BackendTextureObject* backend}; the memo key becomes (ContextSerial, SamplerViewsSerial, SamplerViewStart, SamplerViewCount, g_backendContextGeneration) —
SamplerViewsSerial moves iff the resolved set moved, which the emitter's content-hash suppressor
(SamplerEmit.h:692-697) makes sound — and PairingsIntact is deleted: no borrowed slot is
left to go stale. The rebuild walks st.BoundSamplerViews[Start..Start+Count) calling
SyncTextureToBackendByHandle. ResolveAndBindUnitTextures collapses to one pass over the same
window: FindByHandle(entry.Texture), target from SamplerViewCsos[entry.View].View.Target, bind;
then unbind every backend target the window did not claim, driven by the fixed TextureTarget enum
plus g_boundTexturesCache (server memory) instead of GetAllBindingSlots(). The alias arbiter
sampledTargetForUnit :4845-4857 DIES — the client already arbitrated (SamplerEmit.h:751-757).
The sampler pass :5562-5596. The unit's effective sampler becomes st.BoundSamplerStates[unit]
if non-null, else TextureResources[view.Texture].Params.BuiltinSampler; its values come from the
CSO record (lodBias, minFilter, magFilter, mipmapMode, compareMode all present,
MGPipeValueTypes.h:462-486), and texture2D->GetFormat() becomes
SamplerViewCsos[view.View].View.InternalFormat. The raw-depth-fetch substitution itself STAYS on
the server (docs/Disaggregated/ARCHITECTURE.md §5.5 assigns it there by name). :5624-5632's
GetOrCreate(samplerObject) is deleted; the CSO-twin arm :5609-5620 is the only arm left.
Finally the two decline arms — BindCurrentUnitSamplers :5152-5169 and
CurrentUnitBindingsEpoch :2073-2098 — become named refusals under a live wire instead of
falls-back to GetTextureUnitObject, and with them gone
CaptureUnitBindings/UnitBindingsUnchanged/g_observedUnitBindings (:1867-1957) are
monolith/legacy-only text.
5. Monolith / G1
Every edit sits inside #if MOBILEGL_PIPE_PUSH and, for the run-ahead arms, behind
MG_Config::Transport != MG_Config::TransportMode::Monolith — the idiom already at
DirectGLES.cpp:3226, :4725, :6534. The pull build keeps :1936-1957, :2073-2098,
:2340-2391, :4861-4948, :5152-5169, :5624-5632 token for token, the D-P rule this family
already follows (Managers.cpp:8613-8618, :8791-8800 spell the #if/#else arm split and why a
shared prologue is a G1 failure). Three obligations: (1) BackendTextureObject's pull-build layout
must not move — every member the new gate needs is already inside #if MOBILEGL_PIPE_PUSH
(Managers.h:1785-1812), so the admitted-resize set stays empty; (2) UnitTextureSyncEntry's
re-shape is push-only, two structs under one name selected by #if, which
g_fboTextureSyncListRecordKeyed (:2157-2171) already does; (3) the push build under
Transport=monolith must KEEP the frontend walk — no server to answer from — so the run-ahead arm
is a THIRD arm, like MGB_STAGED_TEXTURE_LIVE (:6902-6903). MOBILEGL_PIPE_VERIFY forces the
lockstep, so the comparator keeps seeing both.
6. Red-once
- Widen the pinned guard list.
MG_Test/Wire/RemoteClientTest.cpp:1938-1941deliberately EXEMPTS the shape reads — "GetMipmapTexelSize / GetMipmapByteSize / GetMipmapLevelCount / GetUploadTargets / GetTarget / IsComplete ... the per-draw binding walk reads them every draw". P5e adds those six plusGetSamplerObject/GetTextureParamsVersion/GetContentVersiontoRefuseLegacyTextureArmFromApplyThread(MipmapStorage.cpp:39-51) and itsMGL_TEXTURE_GUARD_TESTrows:1942-1978. Revert the handle arm -> each aborts by name. Fatal{UnmigratedPipeInput,"GetTextureUnitObject@DrawVbo"}underMOBILEGL_IPC_STRICT_ERRORS=1(PipeInputs.cpp:71-77,:191-224), where the row is BARRIER_PULLED and the read today only counts asrsp: revertResolveAndBindUnitTextures' window arm -> strict lane red at the named field+verb. 3. And an integration assertion overPipeSlotPeek(MG_IntegrationTest/Harness/PipeSlotPeek.h) thatMGPipeFrontendKeyedRegistryScopeentries for this family are 0 over a frame; the mint is alreadyFatal{RoleViolation,"MGPipeSlots"}(SlotTables.h:255-266).DirectGLESTextureSync.UnitMemoRefusesToDriveATwinFromAnotherTexture(MG_Test/SanityTest.cpp:3075-3144) is thePairingsIntactpin: keep it for monolith/pull, and give it a handle-arm sibling whose hazard is ABA — recycle the slot to{slot, gen+1}while the old entry is in the work list, assert the twin is re-minted (SlotTables.h:316-340's forward/backward rule answers it).TextureParamsWithoutASamplerViewScenariostays green (the D10/G9 pin), i.e. the view window must not become the ONLY path intoSyncTextureParamsToBackend; andSampledSetStalenessScenario— a texture that becomes complete with no rebind must re-enter the sampled set, which under the new key isSamplerViewsSerialmoving; revertTracker.h:605-610'stextureParamsmix -> red.
7. Risks and what I could not settle
- R1 — the window is not pinned (A8). Everything in §4 reads
[SamplerViewStart, +SamplerViewCount).SamplerEmit.h:735-737emitsStart=0, Count=maxTouched+1, butPipeApply.h:666-669says only "the last set as received" and does not require coverage; under run-ahead a too-narrow window silently drops a sync, and a decline is what run-ahead cannot take. P5e needs a contract sentence: the window MUST cover[0, MaxTouchedTextureUnit], narrower isFatal{ProtocolCorruption,"SetSamplerViews.Count"}— not a server-side re-derivation. - R2 — the program key (conflict with S4).
BindCurrentTextures' memo:5277-5284reads the frontendProgramObject; my design deletes the alias arbiter but not that key. Ruling needed: key it onst.DrawProgram+ProgramBindingSerial(S4's rows), or onSamplerViewsSerialalone — which already mixes the program-resolved opaque units (Tracker.h:605-610)? I lean to the latter (one fewer cross-family key) but could not rule out that a relink with unchanged resolution must still re-bind. - R3 —
RequireImageBindableStorageunder run-ahead. §3 G-S2-5. It aborts today, but three frames deep at aMarkStorageDirtyguard rather than as a named refusal at the entry; run-ahead turns a decline into a hard stop. How oftenImageBindableHintfails to prevent it is unmeasured. - R4 — the twin's mutable flags.
m_forceTextureParamsResync/m_forceSamplerResyncare server-set and read-not-cleared from the wire (D-E2) — server memory, safe — but the new gate reads BOTH them and the wire bits, so "neither side clears the other's" becomes load-bearing for a gate, not just for a push. One contract sentence. - R5 — buffer textures.
Managers.cpp:8223-8231still hands the frontendBufferObjecttoEnsureBufferResourceForHandle(D-A4'sfrontendargument).Desc.BufferForTexBuffernames the buffer; whetherfrontendmay benullptrunder split is S1/S5's call — the E note says the transport arm reads nothing from the object (Managers.cpp:3199+), which suggests yes. - R6 — unmeasured. This family's share of the apply thread at
2fde7034is unknown; the E note's 3.0%HandleOf<ITextureObject>is at85362a85, pre-round-3. Re-measure before sizing.