The twelve Server* forwarders' one-slot mailbox carried a raw function
pointer plus a void* to a stack-local Args struct (P5c audit row G4);
neither means anything across a process boundary. The slot now carries
one SurfaceControlFrame by value: kind is the op, scalars are the
arguments, and ok/eglMajor/eglMinor come back in the same value. The
blocking handshake, the shadow, the doorbell order and the C2 exit
block are unchanged.
- protocol.fbs: append-only - SurfaceOpKind gains SetSwapInterval=8,
ReleaseResources=9, SetWindowHandle=10; WindowKind gains MetalLayer=6;
SurfaceOp gains readSurface and context (the MakeCurrent four-tuple).
protocol_generated.h regenerated with the pinned flatc.
- Server/SurfaceControlFrame.{h,cpp}: the frame (trivially copyable by
static_assert) and the op enum - ten wire kinds plus four inproc-only
kinds (InitCapabilities is answered by CapsSnapshot on the wire,
SwapBuffers/InitWindowSurface have no production caller - f0-egl F8,
kept for the inproc test lane).
- Protocol/SurfaceOpCodec.{h,cpp}: the frame <-> SurfaceOp/SurfaceReply
codec, the explicit WindowBackend <-> WindowKind table, and the wire
entry ServerApplyWireSurfaceOp whose refusals are named:
Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} and
Fatal{ProtocolCorruption, "SurfaceOp"}.
- ServerLoop: RunOnApplyThread(fn, void*) -> RunSurfaceControlFrame(frame)
plus the dispatch ApplySurfaceControlFrame; the old per-forwarder Args
bodies moved into the dispatch unchanged (C7/ID-54, N-3, R-12 included).
RunProbeOnApplyThreadForTesting is the test seam through the same
channel (the hook lives in a member, never in the slot).
- Tests: RemoteClientTest/ServerLoopTest migrated to the probe seam; new
SurfaceControlFrameTest suite (mapping tables, wire round-trips, the
two named refusals) plus ServerLoopTest's AVoidForwarderCrossesAsOne-
DispatchedFrame (the frame channel's red-once handle) and
ServerLoopEglTest's TheInitializeDisplayReplyArrivesThroughTheFrame.
- ID-132's finding, diagnosed: with the flip thrown the 70 red lane entries are ONE race
and two debts. The race is the client's residual fill writing gPipeInputs while the apply
thread is inside an earlier UNBARRIERED record. Measured interleaving, verbatim:
`[MobileGLIntegra/ERROR] RA2-PROBE fill verb=DrawElements wireop=81 waitclass=-1
applierInside=1` then `[mgl-srv-apply/ERROR] RA2-PROBE unfresh field=IsCapabilityEnabled
verb=DrawElements serverStamped=0 ownership=RECORD-SUPPLIED` then
`[mgl-srv-apply/FATAL] MGPipe: Fatal{UnmigratedPipeInput, "IsCapabilityEnabled@DrawElements"}`.
The GL thread's MGPipeServerClearVerbBoundary withdrew the applier's own stamp mid-record.
- The marker's verb is the CLIENT's verb, which is what identifies the writer without a
probe at all: the applier can only ever put a verb of MGP_VERB_OP_LIST into the block, and
40 of the 54 unbracketed aborts named a verb that is not in it.
- A/B control on the same binary and the same records: MOBILEGL_IPC_RUN_AHEAD=0 is 8/8 green
where the default arm is 2/6 red, so the defect is in the wait rule and not in a record.
- RefusePipeInputsTouchWhileApplierOwnsIt could not fire on any of it: its run-ahead arm read
`if (isBarrieredFill) return;`, and that sentence is a claim about the FUTURE ("this thread
is about to park behind the record"). The order at the validate point is fill, then emit,
then park. So the arm now tests the fact - `isBarrieredFill && !ApplyThreadIsInsideApplier()`
- and the fill sites MAKE it true first with §2.5's forced wait (QuiesceApplierBeforeFill).
- TWICE PER VERB, because the block is written in two phases that straddle publication: the
serial bump / stamp withdrawal / verb rename run before the fourteen emitters and the
63-field walk of step 4 runs after them, and the records those emitters published are
records whose apply reads the block.
- ClientVerbIsBarriered: MGP_VERB_OP_LIST joins the whole draw family through one row
(DrawVbo -> DrawArrays), so inverting it verb-first answered kOpCount for DrawElements and
eighteen other draw verbs, and the "unknown verb answers barriered" default filled for each
while the record it published was a kWaitNone draw_vbo it never parked behind. The family's
row is applied to the family; every other unknown verb keeps the conservative answer, which
the wait above now makes honest. Measured alone it is worth 112 -> 133 isplit; with the wait
in place the lane is 161 either way, so this half is the PERFORMANCE half - without it every
indexed draw takes a forced drain and run-ahead is lockstep by another name on the draw path.
- Red-once A: put the guard's arm back to `if (isBarrieredFill) return;` and
RemoteGuards.ClientBarrieredFillWhileTheApplierIsInsideUnderRunAheadIsFatalByName exits 0
instead of aborting (`Value of: DiedOfAbort(child) Actual: false`), while its green control
keeps passing. Red-once B: delete the kDraw fallback and
RemoteRunAhead.AnIndexedDrawVerbIsUnbarrieredAndTouchesPipeInputsNotAtAll dies of signal 6
with `Fatal{RoleViolation, "gPipeInputs"} - the GL thread touched gPipeInputs
(MGPipeValidateForVerb) on a RUN-AHEAD session in a barriered fill that did not first wait
for the applier to catch up`.
- Both new cases run in the ordinary unit lane with run-ahead armed by the fixture, which is
what ID-132(1) asks for: the strict lane runs under lockstep and cannot see this class.
- Gate on the flipped head: unit 2256/2256, isplit 161/181 (was 112), gpu 1339/1359 (was 1290),
gens rc=0, flavours rc=0. The remaining 20 are two named debts, not this race: 18 are
`GetBufferBindingSlot@DrawArrays [UNBARRIERED]` on the two indirect multi-draw tiers, whose
retiring phase is P8, and 2 are ID-82's own refusal of client vertex arrays.
- The arm is stated and not inferred (ID-81): WaitForApplyToCatchUp's first test is
`m_runAheadArmed && m_started`, which reduces to Transport != Monolith. Under lockstep,
under monolith and under MOBILEGL_IPC_RUN_AHEAD=0 every line added here is a call that
returns, so those arms are unchanged.
- Fifteen cases, and every one of them arms run-ahead through the production latch
(publish kCapRunAheadApply into the caps mirror before Start) rather than through a
test-only setter - ID-102's lesson: a red-once whose probe is a branch beside the
production path is a red-once that proves the branch.
- RemoteRunAhead: the knob alone does not arm it; VERB_BARRIER=0 disarms it even with
the cap bit; a credit-1 client pays at every present after the first and a credit-3
client at none of its first three; an unbarriered verb fills nothing and is allowed;
a 3 MB event burst behind an unwaited sequence flow-controls instead of aborting and
arrives LOSSLESS; only the texture half of resource_subdata wants its reply.
- RemoteGuards: a gPipeInputs touch outside a barriered fill under run-ahead is
Fatal{RoleViolation, "gPipeInputs"} by name and the barriered fill is allowed; a
residual pull under an UNBARRIERED record is Fatal WITHOUT the strict knob while the
same pull under a barriered one still only counts (ruling 4, in executable form); a
deferred destroy from an unbarriered apply is the finding and from a barriered one is
still served (ruling 13).
- SessionTest: a full SEG_EVENT wakes a client parked on appliedSeq AND one parked on
the present credit, without touching either watermark; the drain clears the latch and
RINGS, once, and is silent when the latch was already down.
- The event burst's drainer starts 50 ms late on purpose. A drainer that starts at once
can keep a 256 KiB ring from ever being full, and a red-once that depends on the
scheduler losing a race is not a red-once.
- ClientVerbIsBarriered now takes a nullable context: the validate point asks the
question before it knows whether there is anything to fill, so the answer is about the
RECORD. A null context has no open XFB span, and treating it as "barriered" instead
made the fill red-once unreachable in any fixture without a live GLContext.
- kMGPipeP5eClientWaitRuleLanded flips WITH kMGPipeP5eRunAheadReady, in the integration
commit - an amendment to ID-103's wording ("the same commit that lands the wait rule").
The argument ID-103 makes is about whether the CLIENT is parked: RunAheadArmed() needs
the server's caps bit, so between ra landing and the bit being published the client
still blocks after every record, and a `false` stamp there would withdraw §4.4's
exemptions from probes that wait still makes safe. Two spellings, one switch.
- MGPShaderBuffers::WritableMask is Uint32[3] and the POD grows 32 -> 40 (ID-104). One
Uint32 could describe only the first 32 of an 84-point window, so a storage buffer bound
at point 32 or above read as unwritable with nothing able to see it; the ruling widens the
field rather than narrowing the window, which would change what an application may bind.
MGPipeShaderBufferMaskHas / ...Set are the one spelling of the mask, so the emitter, the
applier and the four backend sites cannot disagree about which word a point lives in.
- MGPipeApplySetShaderBuffers has its body: per-class window, Start/Count validated against
the capacity, the mask copied by word, ONE ShaderBuffersSerial advanced on every applied
record and ADVANCED - never zeroed - by both resets, which also clear the three windows.
- Route rows for opcode 38 on both arms (Mono_ and Wire_) and the server sink in the wire
codec's decode switch, which is where a kCtxState row's sink lives - PipeApplier.cpp's
ServerVerbSink has no method for it and the brief's "OnSetShaderBuffers" names no code.
The codec also bounds Class and Start+Count where Count becomes a byte length.
- New MG_Impl/Pipe/ShaderBufferEmit.h: one record per class, window = the touched
high-water mark clamped to 84, zero early-out before any hash, a BASE binding travelling
as kMGPipeWholeBuffer so the server re-resolves the extent at use, three suppressor slots
(ruling 11), and the writable mask that replaces the backend's GPU-write walk.
- BufferState gains a per-target bind-point generation (push-only, G1) and dirty bits
15/16/17 shutter on it instead of the buffer CONTENT aggregate. That aggregate answered
the wrong question in both directions: any glBufferSubData fired all three, and
glBindBufferBase fired none of them, because it mutates a binding point through a returned
reference. Same defect class as P4a's glBindSampler hole.
- Init.cpp publishes consumer bit 13 for DirectGLES and not for Magma (ID-106), in the same
commit as the wired constant, and PipeFill's liveness gate asks R-8 for bit 13 in
particular plus bit 13-requires-bit-7.
- `Touch` joins the dirty-surface scanner's mutator prefixes; TouchBufferBindingPoint gets
its row and SetNamedTransformFeedbackBinding's is rewritten now that bit 17 reads a
generation a binding moves. Negative control 6 is re-pointed: the pair it used to refute
is a live answer, and a control that has quietly become positive is what the self-test is
for.
- RemoteClientTest's MGL_TEXTURE_GUARD_TEST block gains the seven MipmapStorage shape
reads P5c deliberately exempted, with the sentence that exempted them ("the per-draw
binding walk reads them every draw") replaced by the reason it is no longer true.
- SanityTest gains ARecycledTextureSlotReMintsTheTwinOnTheHandleArm beside
UnitMemoRefusesToDriveATwinFromAnotherTexture: the borrowed-slot list needs a pointer
compare because its key is derived state; the by-handle list needs none, because a
recycled slot arrives as {s, g+1} and the forward-Gen reset gives the successor a fresh
twin while the predecessor's handle stops resolving. G2/G14 skip twin included.
- MGPipeShaderCsoRecord gains Archive, a SharedPtr<const ProgramArchive> the create
ADOPTS under a transport. Before this the applier stored the descriptor and the two
companion pointers named the frontend's own LinkArtifacts/SpirvArtifacts, which
ProgramObject::Link() replaces in place - so every reflection read on the apply thread
was torn or stale by construction the moment the client runs ahead (gap G-A).
- MGPipeApplyCreateShaderState takes the archive as a fourth argument, undefaulted for
PipeRoute.h's stated reason: a caller that has one and forgets it would compile, and
the failure would be silent. Null on the monolith arm, which keeps its frontend arms
token for token (ruling 1 / ID-81).
- ProgramArtifactsCodec gains ProgramArchive and the FRAME. SpirvArtifacts carries no
stage list and MGPProgramDesc::StageMask cannot stand in for one: it is a bit set, and
GL lets two shader objects share a stage, so a list rebuilt from it can be shorter than
generatedSpirv - which the backend pairs with it by one running index.
- Codec completeness, as CONTRACT-P5E §5.5 asks: the skipped member is named
(LinkArtifacts::program, the live glslang TProgram) and proven unread by either backend
twin; the libc++ caveat at the size echo is read as what it is - a cross-toolchain
check, exact for an in-process split - and both verdicts are written where they were
asked for.
- set_program_bindings (opcode 80) lands end to end: EmitProgramBindings on the client,
the FIFTH ROUTE ESCAPE (three tails in three index spaces plus a parallel name array -
no generated row can express it, so op 80's gMGPipeContext slot stays null as
PipeCatalogueTest pins), the wire emitter, the decoder arm that resolves and
NUL-checks every name span, and the applier body that copies the names into the record
(rule C) and advances BindingsSerial.
- The record is emitted AFTER create_shader_state, not before as the draft note said: a
re-issued create clears all three tails because they index into the archive it just
replaced, so bindings published ahead of it are wiped by the create that follows.
- MGPProgramDesc::LinkStatus is filled, and ID-88 is VERIFIED rather than assumed:
ProgramObject::Link()'s prologue bumps m_linkVersion and assigns m_artifacts = {} -
"the complete not-linked state" - before the body runs, and every ProgramLinkTask
failure arm leaves linkStatus false. A failed relink therefore reports unlinked, the
re-issue carries LinkStatus = 0, and nothing needs an object_death.
- ID-87's measurement gets its instrument: the archive's byte count is added to
PipeStats' CsoBlobBytes where the bytes actually exist, one sample per link.
- c0e and id were implemented in parallel and each declared the two shared seams,
so the merge produced five duplicate definitions the text could not collide:
MGPipeBarriered, MGPipeApplierCurrentRecordIsBarriered and the three by-handle
twin resolvers. Resolved per ID-99 and ID-103.
- MGPipeBarriered keeps c0e's real body (the static wait-class column, the XFB
escalation and the client-array escalation); id's constant-true placeholder goes.
- The current-record flag keeps c0e's thread_local storage and setter and loses
id's MGPipeApplierState member: it describes which record THIS THREAD is inside,
not per-context state the applier owns, and the allocator guard that reads it
would otherwise have to find the right context's applier first. id's test hook
posts through the setter.
- The three resolvers keep id's real bodies beside each registry; c0e's asserting
stubs for them go. The seam stubs no package has bodied yet stay.
- ApplyOne stamps `true` while kMGPipeP5eClientWaitRuleLanded is false. The
predicate says what the client WILL do; until ra changes EmitAndWaitTails the
client still blocks after every record, so a `false` stamp would withdraw the
§4.4 exemption from a probe the client's own wait still makes safe. The
predicate is computed on every record regardless, so it is exercised for the
whole phase instead of first running on the day it starts deciding.
- Gates on the merged tree: unit 2206/2206, integration-split 111/111, both
generators --check/--self-test green, include closure 0 problems, dirty surface
rc 0, doc citations one pre-existing CONTRACT-P5.md line (c0e retired another).
- New DirectGLESSlotTable.ACompositeHandleDoesNotGrowTheOrdinaryTable (with its
G2/G14 skip twin) pins the band: a composite handle grows m_band by four
entries and the ORDINARY table by none, the lookup finds it, an ordinary slot
beside it disturbs neither space, ForEachLive reports the composite at the slot
THE CLIENT MINTED rather than at its band index, and §4.3's recycle answer
holds inside the band. It asserts on the two spaces' CAPACITIES, not on
liveness: the table holds one live entry either way, so LiveCount could not see
the growth this case exists to catch.
- RemoteGuards.AnAllocatorProbeInsideAnExemptionScopeOnTheApplyThreadIsAllowed is
DELETED. It asserted that a named scope exempts unconditionally, which is the
rule CONTRACT-P5E §4.4 retires. Its replacement,
...FromTheApplyThreadIsFatalEvenAtTheOldDebtSites, opens the same scope with
the applier's CurrentRecordBarriered forced false and expects HandleOf to
abort. The hook is the production field ApplyOne stamps, not a test-only branch
beside it; the texture and the table are built on the CLIENT thread, because a
TextureObject2D's constructor mints and building it on the apply thread would
abort at the mint with the same diagnostic - a case passing for the wrong
reason.
- The green half of the deleted case needs no replacement: every Espryt
integration-split entry runs the frontend-keyed scope's surviving sites on the
apply thread behind barriered records, so a guard that stopped exempting would
take the whole lane down.
- Unit 2203 -> 2204 (the new name only adds); integration-split unchanged at 111.
- The round-2 report guessed the apply thread's ~48% mutex time was the backend
pendingMutex; the symbolized call graph at 56a77348 says it is the idle poll in
ServerLoop::ApplyThreadMain taking m_controlMutex twice per iteration
(ControlIsPending in the ready lambda, PumpControlRequest), with DrainRing at
29% of the thread. Every package below is cut from that graph, not from self%.
- T (transport): the control mailbox gets an atomic shadow bit m_controlPosted so
the idle poll takes no lock (handshake, C2 critical section and the
NOT_INITIALIZED answer unchanged); Doorbell::Wait spins on a once-calibrated
iteration budget and reads no clock in steady state (the park phase alone
computes deadlines); client/server wait and park counts ride the stats line
as wait[srv= srvpark= cli= clipark=] and the wire ledger as cliwait=/clipark=.
- D (role guards): OnApplyThread() is inline - one relaxed atomic key against the
caller's thread pointer (aarch64 __builtin_thread_pointer, else
std::this_thread::get_id) - and m_applyThreadId, the function-static guard and
the pthread_equal call are gone; the three scope depth counters stop being
thread_local (their only reader is the apply-thread guard, so they count only
when OnApplyThread() is true; a GL-thread scope no longer exempts the apply
thread, pinned by a new case); the guard chains are ordered cheapest-first.
- B (persistent maps): the shadow allocator rounds the SIZE to a page multiple, so
TrackWriteMap aligns OUTWARD when the shadow is page-granular - a sub-page map
is one protected page, edges and their per-draw XXH3 disappear; the inward arm
stays as the fallback. Review found a hole the edge hashes had hidden since
round 1: a map that faulted but was not consumed by the walk that advanced the
epoch was never pushed. The walk now drains every marked member before the
binding walk, registration bumps the epoch so a fresh map ships once on the
draw path, and a non-4 KB kernel page refuses the mprotect arm by name.
- C (client per verb): an image-unit high-water mark (push build only, fed by
glBindImageTexture) replaces the 192-unit sweep per draw; the residual fill's
7-term supplied conjunction is a memo keyed on its four inputs; the server's
63-field stamp per verb is a constexpr per-class mask table.
- generate_mipmap no longer skips its wait under MOBILEGL_IPC_BATCH_WAITS: its sink
is the backend GenerateMipmap, which reads MGB_CTX->GetActiveTextureUnit(), and
a batched publish let the next validate move the serial under it -
Fatal{UnmigratedPipeInput, "GetActiveTextureUnit@ReadPixels"} on the gate tree.
The rule is written at the site: a record may skip its wait only if its apply
reads nothing the residual fill writes. The texture-death scenario gets the
framebuffer case's fence before it reads the object_death tally.
- Redmi 2f7cbe2e, FCL + Minecraft 26.3-rc-3, VD12, CPU pinned: inproc 64.3 fps
(client 15.0 ms/frame, apply 12.4) -> 103-114 fps (client 8.7-9.2 ms/frame on a
mid core, apply 7.0-7.4); monolith 115 (120 Hz vsync-capped runs) / 206 (one
uncapped run, 5.0 ms/frame on the big core). Gates: unit 2203/2203,
integration-split 111/111 (the two formerly flaky cases 6/6 on repeat),
generators, include closure, dirty surface and doc citations green.
VD12 Minecraft on the Redmi (FCL, DirectGLES): the split's in-world frame rate
13 -> 60-66 fps against monolith's 110-114, and persistent-map wire bytes
25 MB/frame -> ~0.23 MB/frame. The residual split-specific costs are named at
the end, not hidden: the R-1 serialization (WaitForApplied ~12%, P11's
versioned-inputs design) and a split-only mutex complex still under
investigation.
Persistent-map push, three arms picked per buffer at map time:
- mprotect dirty tracker: the mapped range's page-aligned interior is
protected PROT_READ; the app's next write faults, the SIGSEGV handler marks
the page in the slot's bitmap and unprotects, and the push ships one 64 KB
block per block containing a faulted page. Ownership is three tests -
SEGV_ACCERR only (a userspace raise and an unmapped page chain verbatim),
inside a live tracked range, and not a refault on a still-set bit (a write
we unprotected succeeds, so that is a non-write access and it crashes
honestly). The alignment is INWARD: an outward-aligned edge page holds
foreign bytes, and a signal-blocking worker thread faulting on one is a
process kill - that was the ctest-only SmallRing segfault. Edge spans
(at most two per map) are hashed per push and ship their containing block
only on change (unconditional edges measured 112 MB/frame). Re-arm is one
mprotect per contiguous run. The first push ships everything once.
- hash-suppress arm (MOBILEGL_IPC_PERSISTENT_HASH_SUPPRESS, default 1):
per-block XXH3 against the last push's hashes; untracked buffers only.
- PushDrawConsumers: only the buffers the upcoming draw/dispatch can read
(VAO attribs/element, indexed binding points, indirect/parameter slots,
TexBuffer backing - the last also retires the per-frame-cadence cloud
twitch), plus the fault-epoch skip: an unmoved epoch proves no interior
byte changed, tracked edges are hashed in place, and any hash-arm member
vetoes. The membership map caches the tracker slot pointer (a 64-slot
rescan per buffer per draw was measured and removed).
MOBILEGL_IPC_BATCH_WAITS (default 1; MOBILEGL_PIPE_VERIFY forces 0): a
value-class record (kCtxState/kCtxCso/kCtxObject with no reply slot) is
published without waiting for its own apply; the barrier is taken at the next
pull-reading verb, the only place BARRIER-PULLED fields are read, so the R-1
fence posts are untouched and only the round trips between them change. Two
consequences are pinned rather than argued: kCtxObject verbs the tests read
synchronously get explicit fences (GenerateMipmapFieldsCross,
FramebufferDeathCrosses), and a frontend object whose last SharedPtr drops on
the apply thread - impossible before batching - now enqueues its
(kind, lifetimeId) on the deferred destroy queue and the GL thread replays
the DestroyAndFree helper at the verb hooks (CopyImageLayeredScenario aborted
Fatal{RoleViolation, MGPipeSlots} without it).
Doorbell::Wait's spin reads the clock once per 64 yields: on the workload
device a steady_clock read is a real syscall (no vDSO) and the per-iteration
reads were ~20% of the whole process at ~1000 waits/frame.
Gates: gen_pipe --check/--self-test green; unit 2188/2188;
integration-split 110/111, the one red being GenerateMipmapDepthPixels whose
DirectGLES and DirectVulkan MONOLITH siblings flake identically with every
code path of this package provably inert (PushIsArmed false, no ClientSession,
deferral's monolith early-out).
Layer 1 arms the texture object surfaces at MipmapStorage (the one point every concrete
texture class and view forwards through): AllocateStorage, TruncateMipmapLevels,
UpdateMipmapSubData, MapMipmapData, MarkStorageDirty(Region), IsStorageDirty,
GetStorageDirtyRegion(s) take Fatal{RoleViolation, texture-legacy-arm} from the apply
thread with an active transport. What the lane then exposed is fixed, not exempted: the
completeness gate answers from the staged store's defined-ness, the two logging reads go
through the descriptor macro, and the remint replay loop keeps its named
Fatal{UnmigratedEmulation} ahead of the guard. Magma's texture sync read path is the one
named exemption (MGPipeTextureLegacyArmScope, P7). Layer 2 wires InBarrierWait as the
client half of the gPipeInputs single-writer rule (RefusePipeInputsTouchWhileApplierOwnsIt:
applier owns it AND this thread not in a barrier wait => Fatal{RoleViolation,
gPipeInputs}; the barrier-off negative control is deferred to its own Fatal), and stale
reads of RECORD-SUPPLIED / APPLIER-DERIVED fields under a server stamp now report
RoleViolation with the field named. The RemoteGuards suite pins all nine texture surfaces
plus HasDefinedContent by name, and a negative control for the fill window. CI gains the
integration-split-strict job: unit lane as the hard gate under MOBILEGL_IPC_STRICT_ERRORS=1,
the scenario lane expected-red with the marker asserted (rewritable to plain green once P7
retires the object-class rows). rsp is per-frame in the stats line and measured on the
locally available traces (bsl in-world 948.5/frame avg over 123 frames, complementary
1591.9, iterationrp 286 until its named remint-pull Fatal); value-class pulls are zero by
rv's pinned list, what remains is the object-class fifteen.
Evidence: unit 2187/2187 (also under STRICT_ERRORS=1); integration-split 111/111; red-once
per layer (the texture guard and the gPipeInputs guard each turned their named death test
red and restored green). CONTRACT-P5C sections 5.4, 6.
set_context_values (opcode 79, MGPContextValues 96B, kCtxState) carries the active texture
unit, the max touched unit, the fifteen touched-binding-point counts and the five value-class
XFB rows, emitted at validate under the subsystem bit and hash-suppressed as a whole; the
emit gate and the residual-skip gate read the same answer so they can never disagree.
MGPipeApplySetContextValues writes gPipeInputs (the PackState shape). MGPAttribValue grows
24->56B to carry the frontend's converted three views, and MGPipeApplySetVertexAttribDefaults
finally writes them (the 'ignores MGPAttribValue::ValueClass' warning dies), so
GetCurrentVertexAttribute leaves the refusal set. The three texture shutters answer the
applier's own serials under a server-stamped verb (APPLIER_DERIVED), storage answer kept
verbatim elsewhere. FieldOwnership.def: nine value-class rows become derived RECORD_SUPPLIED,
the three shutters APPLIER_DERIVED, the nine object-class rows keep their phases;
FieldOwnershipTest pins that the remaining pulls are exactly the object-class list.
gen_pipe.py gains SCAN_EXEMPT_ACCESSORS with per-name reasons and retiring phases (it repairs
the --check gate the hd merge's two G6 probes had left red).
Evidence: unit 2175/2175; integration-split 111/111; both generators' --check and
--self-test green; rsp on the Xfb capture scenario 36 -> 22; red-once - gating the emission
off turns seven named scenarios red, restoring returns 111/111. CONTRACT-P5C section 5.3.
A split-only verb-handle workspace in MGPipeApplierState (written by the sinks before
dispatch, read by the backend within the same verb) carries the record's handles to the
backend entries whose shared function-table signatures cannot grow them without moving
G1. OnBlit resolves ReadFbo/DrawFbo through the FBO twin table by handle (the named arm
of BlitFramebuffer, a loud refusal when the backend does not consume it) and the split
path's ScopedBlitBindings rebinding is deleted; CopyTex resolves MGPCopyFromFramebuffer::Dst;
the mip descriptor check resolves MGPMipPlan::Res; the indirect families resolve their
buffer handles from the records and read the bytes from the server staged shadow with
RequireStagedCoverage. EnsureBufferResourceForHandle answers HasDefinedContent from
Desc.HasDefinedContent OR the staged coverage set (the streaming idiom defines content
through subdata with no new descriptor). The minting GetOrCreate(StatePtr), HandleOf and
the death switch's lifetime-id arms take Fatal{RoleViolation, MGPipeSlots} from the apply
thread, as do the three MGPipeSlots() entry points; the frontend BufferObject legacy
accessors take Fatal{RoleViolation, buffer-legacy-arm}; Magma's four caps reads go to the
server's own backend and the client-mirror fallback arm is a named refusal.
Evidence: unit 2156/2156 (incl. 9 new RemoteF1 + 10 new RemoteGuards death tests pinning
the four Fatal names); monolith smoke green; six production mutations each turned their
named test red and were restored; c1f_redcheck M5 RED then RESTORED GREEN. The mip
descriptor check overlaps tx and is coordinated at the merge (tx's sync rekey wins the
sync body, this handle resolution wins the identity check). CONTRACT-P5C section 3.