- spans (d1a7c5f1) removed GetPixelStoreParameters from the emitted list because the call
carries the pack half of a two-element field, so the generated MGPipeFieldEmitter no longer
has a SetPixelPackState enumerator; the tracker's subsystem map and its static_assert, written
against the contract, still named it and the push and verify builds did not compile on the
integrated tree
- the NEW_PIXEL_PACK dirty bit still names the pixel-pack subsystem, which is what the
emission gate consults; the field itself stays in the residual fill loop by design
- the residual value block armed on NEW_PIPELINE_STATE, so glEnable(GL_CLIP_DISTANCE0)
never re-armed it: SetCapability's ClipDistance0..7 arms are deliberately not
BumpVersions() and those eight are 8 of the 35 CapabilityInputs the block carries, so
the D9/G10 trip wire was disarmed for them for an unbounded window - and invisibly, a
block that is never emitted cannot diverge. It now arms on either render-state counter,
the same answer DirtySurface.def derives for SetCapability, and the comment that
asserted the opposite ("every SET_CAPABILITY arm calls BumpVersions") is corrected.
- the arming moved outside the residual subsystem gate: whether the capability set may
have moved is a fact about the frontend, not about which subsystems this build pushes.
- set_vertex_attrib_defaults published NOTHING across a context change. Tracker::Reset()
sets the staging mirror to the GL defaults and a fresh GLContext holds the same, so the
per-attribute diff was empty on the one walk that must publish a COMPLETE state, while
MGPipeApplierReset() leaves gPipeInputs.m_currentVertexAttribute holding the previous
context's values - which cancelled, two lines later, the InvalidateAll() written for
exactly that case. It now sends all 32 when the tracker is freshly primed, the arm
EmitRenderState already had.
- the fresh-context reset of the CSO cache and the applier moved out of EmitRenderState,
which runs only when bit 0 of MOBILEGL_PIPE_PUSH is set: the per-subsystem A/B D14
invites gave a fresh context a never-reset applier while every suppressor slot was
invalidated.
- MGPipeVertexAttribDefaultsLastHeader() is the observable for both properties of that
call that cannot be read back without a poisoned read of m_currentVertexAttribute.
- the repair case now asserts the invariant (the call named exactly what moved, at most
one repair) instead of repairs == before + 1, which pinned today's applier and would
have gone red the day package A honours MGPAttribValue::ValueClass.
- a static_assert that no MGPipeDirty bit owns kMGPipeSubsystemResidualValues, which is
what makes the residual block's direct subsystem test the one safe exception to
MGPipeSubsystemForDirty, and a note that the NEW_PIPELINE_STATE/NEW_RENDER_STATE
MOBILEGL_ASSERT is a debug/verify alarm over behaviour that is safe in every build.
- set_vertex_attrib_defaults hard-coded MGPAttribValue::ValueClass to 0 for every attribute
and always sent the FLOAT view's four words. A CurrentVertexAttributeValue is one value in
three views and GLContext converts numerically between them, so those bytes cannot
reproduce the frontend value: glVertexAttrib4f(loc, 1.5f, ..) leaves 1 in intValue and
0x3FC00000 in floatValue, and every glVertexAttribI4i/ui default was wrong too
- GLContext now records which view each glVertexAttrib* write filled directly
(GetCurrentVertexAttributeClass, push-only) and the payload carries that class and THAT
class's own words. It is kept beside the value rather than inside it because
CurrentVertexAttributeValue is mirrored into PipeInputs and compared there by a memcmp
whose size assertion lives in a file this package does not own
- MGPipeFillAttribValue is the flattening, in one named place, so TrackerAttribPayload can
pin it: the old defect turns three of its four cases red
- the applier (package A's) still memcpys the four words into all three views regardless of
ValueClass, so the emitter now CHECKS: it compares the mirror the applier wrote against the
frontend's value and, when they differ, copies the field itself and says so once. That
closes the window the old code left wrong - a glVertexAttrib* write followed by a verb
whose class does not read the field, where the residual fill does not run for it - and it
stops repairing by itself the day A's applier honours the class
- MGPipeVertexAttribDefaultRepairCount() makes that repair observable to a test without
reading storage the fill table forbids that verb to read
- the emission gate now goes through MGPipeSubsystemForDirty, the one bit-to-subsystem map,
instead of a second copy of it written out by hand at the validate point; five
static_asserts tie that map to the field-emitter map it has to agree with
- the staging mirror is advanced only by the branch that sent dynamic bytes, with the
invariant it used to rely on (BumpVersions moves both counters, RenderState.h) asserted
here rather than assumed of another package's file
- TrackerShippedEmitter drives MGPipeValidateForVerb itself and reads the real singletons
back, so the blend-toggle and viewport shapes are pinned on the shipped emitter and not
only on the unit tests' local re-implementation; the fixtures reset the applier, the cache
and the tracker together, which is the only consistent state of the three
- comments: the derivation probe is a one-field sample, the residual block's trip wire is
half a tautology until package A's c1 lands, and the widened counter cannot see a change of
exactly 65536 - all three recorded where the code is, not only in a review
- Counters::Binds was declared, documented as one of the three numbers P13's capacity retune
reads, and incremented nowhere: the retune would have read a permanent zero, and the unit
tests counted binds in a local of their own
- counted in Acquire, which has exactly one caller and is followed by a bind_render_state
every time, so the count cannot drift from the emitter forgetting to tick it
- TrackerWalk drives the tracker and the cache DIRECTLY rather than through
MGPipeValidateForVerb: the validate point reaches the library's one process-wide tracker, and
a unit test that asserts on a shared singleton fails the moment ctest runs the suite in
parallel. The three lines of emission logic it reproduces are the same three lines.
- BlendToggleReusesTwoCsos is the Blaze3D shape the whole "push at validate, not in the
setter" decision was made for: 32 enable/draw/disable/draw pairs mint exactly TWO CSOs, bind
64 times and hit 62. A per-setter design would show up here as 64 mints.
- ViewportDoesNotMintACso is the regression RenderState.h records: 16 glViewports mint nothing,
never move the pipeline version, and each sends exactly chunk D0 - not the other seven.
- WrapAroundRePushesButNeverMisses drives m_version past 65535 and asserts every one of 70000
changes fired. The alternating value deliberately never touches the default: a setter that
early-outs would otherwise make the first iteration a false miss and hide a real one.
- AggregateGenerationCatchesABoundTextureMoving is the first test of the direction the P1
verify comparator cannot see - it compares object-class fields by identity only, so a bound
texture whose content moved looks unchanged to it. The bit fires and then settles, so it is a
shutter and not a stuck flag.
- ANaNPatchLevelEqualsItselfAndDoesNotFireForever: a NaN outer level is a legal
glPatchParameterfv value, float equality says it differs from itself and a byte compare says
it does not. That is why the shutter is a memcmp.
- ThePixelPackShutterIsAByteCompareOfThePackHalfOnly asserts an UNPACK write does not move the
pack shutter, which is the half that deliberately has no carrier.
- HashCollisionDoesNotAliasTwoStates needed a seam and got one: MGPipeCsoCache::s_hashForTest,
null in every real build, one never-taken branch on a path that runs only when the pipeline
version moved. Without it the memcmp confirm is unreachable code that nothing can prove is
doing anything, and what it stops - two different render states on one CSO - is silent wrong
pixels with no gate that can see it.
- ContentAddressingOffMintsEveryTime pins that bit 63 really changes mint/reuse behaviour, so
the negative control cannot rot into a dead switch.
- The set-hash suppressor is exercised on all seven slots even though P2 wires one, including
the reserved-zero contract: a computed hash of 0 is remapped to 1 so it is never confused
with "never emitted".
- set_residual_value_state emits the 35 capability bits, read from the FRONTEND's own
IsCapabilityEnabled rather than from the assembled mirror. That direction is the whole
design: the applier then compares the carried answer against the assembled one, so the block
is an independent oracle instead of a tautology - which is the failure the P1 entry compare
had and P2 is paying to remove.
- It goes out AFTER the residual fill, not with the other emissions: the mirror the trip wire
compares against is written either by the applier's derivation or by that fill, so before it
the block would be compared against the previous verb's answer.
- It is HELD, not dropped, when the verb's class does not carry IsCapabilityEnabled. kQuery and
kXfbSpan do not read it, so at those verbs the mirror is stale by construction; a capability
that moved between two queries would silently disarm the wire if the emission were skipped
instead of deferred.
- ByteClass::ResidualValueBlock has been a placeholder that "stays at 0 until P2" since P0.
This makes it non-zero, which is half of G10.
- The wire is not theoretical: the first version of this commit fired it for real -
Fatal{PipeResidualDiverged, "Dither"} carried=1 assembled=0, on every verb whose class does
not read the capability mirror - and that is what the holding latch above is for. GL_DITHER
defaults to enabled, so the very first mismatch the block could have found is the one it
found.
- integration-verify 818 green, integration-gpu 878 green under the default bitmask and again
under MOBILEGL_PIPE_PUSH=0, unit 1499 green.
- set_pixel_pack_state on NEW_PIXEL_PACK, set_patch_state on NEW_PATCH_STATE,
set_vertex_attrib_defaults on NEW_VERTEX_ATTRIB_DEFAULTS, each gated on its own runtime
subsystem bit so the bitmask stays a per-subsystem A/B.
- MG_Impl/Pipe/SetHashSuppressor.h: seven slots, one per kVarTail set_*, with
SetVertexAttribDefaults the one P2 wires. 0 is reserved for "never emitted" and a computed 0
is remapped to 1, so the first emission always goes out. The other six are the carrier for
the ~175 lines of backend debounce that move in P3b/P4b; landing the mechanism now means the
shape is pinned by a test rather than by a plan.
- The var-tail carries only the attributes that differ from the tracker's mirror, underneath
the set-hash suppression of the whole resolved set - the two suppressors answer different
questions and both are cheap.
Two rows of Coverage.def's emitted list CANNOT yet retire their pull, and each says why in the
code rather than being silently absent:
- GetPixelStoreParameters is BOTH halves of the pixel store and set_pixel_pack_state
deliberately carries only PACK, so the unpack half has no carrier at all. The field keeps
being pulled and the verify comparator keeps proving it.
- GetCurrentVertexAttribute's three views are not bit-identical - GLContext CONVERTS between
them - while MGPipeApplySetVertexAttribDefaults memcpys one Data[4] into all three and
ignores MGPAttribValue::ValueClass, which the wire type carries precisely so it does not
have to. Until that applier reads ValueClass the carrier cannot reproduce the frontend
value. The call is still emitted, so the wire shape, the payload bytes and the suppressor
are all real; the residual fill runs after emission, so the mirror ends up correct either
way.
Both are contract-side defects in files this package does not own; they are reported to the
integrator with the exact fix rather than worked around here.
- MG_Impl/Pipe/CsoCache.h: 64 entries, LRU, hash -> probe -> MEMCMP -> handle. The memcmp is
not optional: a bare 64-bit hash equality would let a collision alias two different render
states onto one CSO, which is silent wrong pixels with no gate that can see it, and Mesa's
cso_cache memcmps for exactly that reason. It runs only when the pipeline version moved, so
never in the steady state. Eviction emits delete_render_state and frees the client slot.
- kMGPipeBehaviourNoCsoContentAddressing (bit 63) turns off the PROBE and the handle reuse,
not the records: every pipeline-version change then mints, binds and evicts, which is the
whole-block content addressing the design is measured against.
- The validate point's step 3: bind_render_state when the pipeline version moved (12 bytes, no
hashing, no blob when the cache hits), set_dynamic_state when m_version moved, carrying only
the dynamic chunks that differ from the tracker's staging mirror. An EMPTY chunk mask still
sends the 32-byte header, because the version is what Magma's dynamic tail gates on and it
moved.
- The residual fill now skips a field a P2 call supplies, driven by the generated
kMGPipeFieldEmittedBy[] and gated per subsystem on the runtime MOBILEGL_PIPE_PUSH bitmask, so
the bitmask is a true per-subsystem A/B. THE STAMP IS UNCHANGED: a stamp says "this verb
published this field", which is as true of an emitted field as of a copied one, and
withholding it would abort every backend read of the fields the migration just took over.
- PipeStats::RecordDrawPayloadBytes has been implemented, unit-tested and called by nothing
since P0. This is its first emitter.
- A field that reaches PipeInputs only through MGPipeDeriveRenderStateFields is skipped only
when that derivation is really there. It is package A's and is a declared stub on the
p2/contract tag this branch starts from, so rather than hard-code which branch this is, the
filler probes once: a sentinel in a scratch block, the mirror cleared, the derivation run,
the answer latched. It stays useful after A lands - if the derivation is ever deleted the
filler degrades to PULLING those fields rather than rendering a default.
- integration-verify, 818 entries, green: the comparator re-reads every field from the live
context at every backend read, so "the assembled block equals the live context" is now
proven rather than asserted, and the entry compare has stopped being a tautology.