- ADrawAfterTheEarlyReclaimFollowsTheNewStoreNotTheMemoizedHandle rides the same split/spawn
fixture as the three gauge cases and the tcp arm does not carry it (magma-m2.md §9.3); the
integration tree keys this list by exact tail, so the fourth name goes in beside the three
Rule J's red-once for M2. The server role's VkBufferManager publishes four PipeStats gauges
(push builds only, so the pull image is untouched): wire buffer records alive, and the run
maxima of live wire VkBuffers and of parked bytes, plus the watermark's forced-sync count -
maxima because the defect lived inside one frame and a swap-time sample reads after the frame
boundary's own sweep. Two cases, each inside a single frame: 1024 undrawn respecifies of one
4 KiB buffer (live peak <= 4 x wire buffers), and 48 respecify-and-draw rounds of a 1 MiB store
under the lane's MOBILEGL_IPC_WIRE_DEFERRED_MB=8 (parked peak <= budget + one store, every strip
its own colour). Red at base (ec46a550's defer path, counted): 1025 vs 4 live, 49,287,168 vs
9,437,184 bytes and 49 vs 11 live, on both arms; green after: 1 live, 9,437,184 bytes / 9 live
with 5 forced syncs. The knob's negative control (=0) reds the second case with the same
49,287,168 bytes. Inproc and spawn only: the tcp server is the lane-wide fixture supervisor,
whose environment and log the entry cannot reach - named in spawn_lane_parity.py as
MAGMA_NOT_ON_TCP (the ID-P7-14 shape), and PipeStatsTest pins the four short names.
- BlitWireFramebuffers runs colour first. A scaled COLOR|DEPTH blit out of a multisample framebuffer scaled and wrote its colour and then recorded INVALID_OPERATION on its depth, and an X-mirrored one mirrored its colour and left its depth alone - partial effects GL 18.3.1 does not allow. The shape decline (scale, X mirror, cross-format Y mirror) moves into DeclineWireDepthStencilResolveShape, which BlitWireFramebuffers asks for every depth/stencil aspect that would take the multisample resolve arm before any aspect runs; ResolveWireDepthStencil asks it too for any other caller. Default read/draw framebuffers are left to the lambda (a default read is never multisampled; a default draw behind one is the region Fatal, and resolving it early would move its write index).
- The X decline and the cross-format Y decline get their own MGLOG_E_ONCE sites; one site used to cover both, so after the first X decline a cross-format Y decline logged nothing.
- An empty rectangle on one side only (64x48 onto 0x48) is a size mismatch and now takes the scale decline (INVALID_OPERATION) instead of returning silently; empty on both sides stays a silent no-op.
- DirectVulkan.{Split,Spawn}.MsFlip1. forces MGITEST_MAGMA_FORCE_SHADER_DEPTH_RESOLVE=1, so the shader arm min-corner rectangle under a flip runs on this host; split+spawn only and named in MAGMA_SERVER_ENV_KNOB_NO_TCP.
- spawn_lane_parity.py: every server-env-knob tail must match exactly one split key, so a dead or over-broad tail fails the gate instead of riding silently.
- The flipped-resolve case gains a COLOR|DEPTH scale leg (error, colour untouched, depth untouched) and an empty-destination leg (error, depth untouched).
- Red-once, each on split + spawn (+ tcp MsFlip.) and restored: pre-pass disabled -> all five MsFlip/MsFlip1 entries fail "the declined COLOR|DEPTH scale must not have written its colour either" (read {255,0,0,255}, want {0,0,255,255}); round-1 silent empty-side return -> all five fail the empty-destination leg (error 0, want 1282); shader arm handed sx0/sy0 instead of the min corner -> MsFlip1 fails on both arms ("768 of 768 depth values differ from 0.75", "928 of 3072 stencil values are wrong; first at (20, 4): got 255, want 99") while MsFlip. stays green; a dead ".MsFlip2." tail -> parity rc 1 "matches 0 split entrie(s)".
- The split once-sites have no lane red-once: the cross-format Y decline needs a device whose renderbuffer and texture D24S8 map to different VkFormats, which lavapipe is not.
The B3 rework's parity hunk and B2's round-1 hunk described the same asymmetry
under two names; the merge kept B2's compare_arms(no_tcp=) and added .StaleSerial.
to MAGMA_SERVER_ENV_KNOB_NO_TCP. One line of the package tree's other mechanism
(`not_on_arm = not_on_arm or {}`) survived outside the conflict hunks and would
have been a NameError on the first run; it goes, and the note's two mentions of
MAGMA_NOT_OVER_TCP now name the list that exists.
MGITEST_MAGMA_FORCE_STALE_BUFFER_SERIAL is read by the server. The tcp
arm's server is the lane fixture (scripts/ci/tcp_server_fixture.py),
started once from its own environment, so an entry-level ENVIRONMENT never
reaches it: DirectVulkan.Tcp.StaleSerial.* ran the unforced case under a
name that claimed the knob and could never red (review ID-P7-34, item 1).
The entry is now registered only when the arm is Split or Spawn, and
spawn_lane_parity.py grows a second named asymmetry beside
MAGMA_INPROC_ONLY: MAGMA_NOT_OVER_TCP, key prefixes the reference arm has
and the tcp arm does not by name. It comes off the reference for the tcp
arm alone, so the same entry missing from spawn is still an error. B2 adds
its own knob entries to the same tuple in its tree.
integration-magma-tcp 71 -> 70 entries (68 cases + the two fixture
entries); split 90, spawn 69 unchanged. spawn_lane_parity.py build-split
rc 0; tcp_lane_tools_test.py 6/6.
Integrator merge onto pipe: B2's round-1 mechanism (`mglItestServerEnvKnobArm` in
CMake, `compare_arms(no_tcp=)` in the parity script) already covers this shape, so
`.StaleSerial.` joins MAGMA_SERVER_ENV_KNOB_NO_TCP instead of the separate
MAGMA_NOT_OVER_TCP the package tree introduced.
ID-P7-31 review, should-fix 1.
Every MGITEST_MAGMA_FORCE_* knob is read BY THE SERVER (the getenv readers in
WireFramebuffer.inc run on the apply thread), and a ctest ENVIRONMENT property only
reaches the process ctest starts - the client. Inproc runs the server in that process
and ServerSpawn.cpp copies ::environ into the spawned child, so both arms see the knob;
the tcp arm's server is the lane-wide TcpServer.Start fixture, started once from
scripts/ci/tcp_server_fixture.py's own os.environ, and nothing a per-case property says
reaches it. The tcp copy of each knob=1 entry ran the DEFAULT arm under a name claiming
otherwise.
Measured, not deduced: a temporary fatal in each of the three readers killed all five
knob=1 entries on split and on spawn and left all five green on tcp.
So ShaderMip1, ShaderMip2, DepthMip, DefaultBlitShape1 and MsResolve1 are registered on
split+spawn only (the first four are package B's pre-existing vacuity of the same
shape). The knob=0 twins DefaultBlitShape0 / MsResolve0 keep their tcp entries: zero is
the default and means the same thing on every arm.
spawn_lane_parity.py names the exception, following ID-P7-14's MAGMA_INPROC_ONLY:
MAGMA_SERVER_ENV_KNOB_NO_TCP, applied through a new compare_arms(no_tcp=) that drops the
keys from the tcp comparison ONLY - folding them into inproc_only would stop requiring
them on spawn, which is where they do most of their work.
Tier 2 replays the split-arm registrations - ~100 scenarios, the set Espryt's disaggregation work
has carried since P5 - and it measured green on Magma across all three transports. That is a real
result but it is NOT exit gate 1. The gate says the INTEGRATION BINARY is green under Magma split,
and the set that names is the one the monolith DirectVulkan. arm runs: the whole suite minus
MGL_ITEST_MONOLITH_MECHANISM_FILTER. 510 cases against tier 2's 102, and the readback matrices,
the same-texture copy shapes and the odd-offset uniform ranges are in it and nowhere else in the
disaggregated registry.
integration-magma-full-{split,spawn,tcp}, in no gating step, private log per entry through a list
of its own. THE RESULT: 453/0/57 on inproc, 453/0/57 on spawn, 454/1/57 on tcp - and not one of
the 1530 entries reaches any of the tree's fifteen @P7 named refusals. The single red is
IterationRPProgram203Scenario.FixedCompleteInputProducesFixedCompleteGoldenOutput on the TCP arm:
one texel of 262656, texel 0, which is the case's own exposure value
(IterationRPProgram203Scenario.cpp:371, actual half bits (0x357a, 0x4b65) against golden
(0x3a74, 0x4821)). Deterministic 5/5 on tcp; 5/5 GREEN on inproc, on spawn and on the monolith
control. spawn and tcp differ only in MOBILEGL_IPC_DATA, so the suspicion is the STREAM data path
and not "two processes"; the GLES tcp lane is green in the same run, so it is not the fixture.
Not fixed here - it is what the census is for.
TWO ENVIRONMENT DIFFERENCES FROM THE GATED TIERS, both measured rather than assumed:
* NO MGITEST_SPLIT_LANE=1. That marker arms ScenarioFixture's destructor assertion - a case in
an armed split lane must have moved the client encoder's record ordinal - which is right for
a curated lane and wrong for a whole-binary census. With it set, the first run produced 13
reds, all `EmitSeq was 20 at SetUp and is 20 now`: AdvertisedLimitsScenario (8),
SpirvShaderBinaryScenario (4) and ProgramPipelineScenario.AReservedName - a limits query is
answered from the client's CapsMirror and a shader-binary error surface never reaches a verb.
Those 13 would red on a DirectGLES census too. What it gives up is stated in the comment: this
tier does not prove its records crossed, and the tcp topology proof is inert in it.
* DualBlockScenario.* filtered out. Its green REQUIRES MOBILEGL_IPC_ROLE_SPLIT_STATE=1 and a
census must not set that knob; without it the case is red by name, which is its documented
negative-control behaviour. The monolith arm skips it for the same class of reason.
CtWireScenario gets MOBILEGL_TEST_SERVER_COUNTERS=1 in this tier - a switch that makes a counter
readable, not an oracle that asserts - because without it the spawn arm's ApplierReset case reds
on "server control-record telemetry missing", which says nothing about Magma. The strict-arming,
pmap and persistent-map-arm markers stay out.
The inproc-only exclusion in spawn_lane_parity.py is now PER TIER: in the gated tier the two
scenarios are registered on the inproc arm alone, in the census they are on every arm and simply
skip, so excluding them there would manufacture a spurious "extra".
Measured after the change: every pre-existing lane unchanged and green (magma-split 73,
spawn 52, tcp 54, all-* 102/102/104, GLES 186/102/104, buffers 19, runahead 14, caches 7), the
dual-block census 257 green / 2 skipped / 0 red with an empty ratchet, both new CI steps green,
parity RC=0 across all three tiers, G14 4155 -> 6095 with removed=0.
The note now carries the grouped red list, the 57 named skips by reason, the four pieces of
evidence that the tcp red is a defect and not plumbing, and six findings - the first being that
wave 2's clusters cannot be scheduled from this lane's red list, because on lavapipe there is
essentially none.
P7 exit gate 1 asks for integration green "under Magma push and split". Until now it was not
merely unmet, it was UNMEASURABLE: every arm of mgl_itest_register_split_arms hardcoded
MOBILEGL_BACKEND_TYPE=DirectGLES (:2002/:2025/:2031) and MGL_ITEST_MAGMA_WIRE_ENVIRONMENT
hardcoded MOBILEGL_TRANSPORT=inproc (:2456), so the shared integration-split / -spawn / -tcp
labels held ZERO DirectVulkan entries and the 73 integration-magma-split cases only ever ran in
one process.
The registration macro now takes the backend as its first argument; the old name is a thin
wrapper, so all 32 call sites are untouched and every DirectGLES name, label and private log
path is byte-for-byte what it was. Two tiers come out of it, and the CMake comment states both:
* GATED - integration-magma-{split,spawn,tcp}. The six hand-laid Magma lanes (Fm, Buffers,
RunAhead, Caches, NamedBlit, P5fRsp) grow arms through one shared shape macro; the inproc
arm keeps its names, its labels and its log paths, so the CI steps and the two magma_*
checker scripts that name them by hand keep working unchanged.
* INFORMATIONAL - integration-magma-all-{split,spawn,tcp}, in no gating step. Every scenario
the split arms run, on Magma, on all three transports, generated by REPLAYING the macro's
recorded call list - so a scenario added to the split arms tomorrow joins the Magma census
the same day. Its job is to be able to go red while CI stays green; a case moves up into the
gated tier with the commit that retires its blocker.
The label spellings are chosen against ctest -L's regex (ID-131 again): the tier word goes in
the MIDDLE, because integration-magma-spawn-all WOULD have been selected by
-L integration-magma-spawn and pulled ~100 expected-red entries into a gated lane. Sub-labels
mirror it: the inproc arm keeps integration-magma-buffers, the others put the arm word first.
MagmaRunAheadScenario and MagmaWireCacheScenario stay on the inproc arm alone, and that is
MEASURED rather than assumed: with all three arms registered, all 21 entries red on spawn and
tcp with no Fatal in either role log - the first arms its hold through
ServerLoopInstance().SetBeforeRetireHookForTesting, a hook on THIS process's server loop, and
the second asserts transportName == "inproc" in its SetUp. Both are named in the CMakeLists and
in spawn_lane_parity.py rather than allowed to widen a tolerance.
split_log_paths.py's is_split prefixes gain DirectVulkan.Spawn. / .Tcp. - on those arms two
processes append to one path, so a shared log does not merely blur attribution, it interleaves
two sessions. spawn_lane_parity.py gains the Magma dimension, normalising only the ARM segment:
RunAhead.Credit1. and .Credit3. are two entries of one case and a tail-stripping normaliser
would fold them together and then report an equal-sized set that had lost one.
Measured on this tree (lavapipe): magma-split 73 unchanged and green, magma-spawn 52/52,
magma-tcp 52/52, the three informational lanes 102 each, GLES split 186 / spawn 102 / tcp 104
all unchanged and green. G14 against ~/w7/p7-before: 4155 to 4565, removed 0, added 410.
Exit gates 9.2 and 9.3. Until now nothing ran the integration suite across a
process boundary at all - the spawn shape was proven only by the retrace lane,
which exercises one trace per case and no scenario assertions.
integration-spawn: 102 cases, 102 green.
THE SET IDENTITY IS THE POINT, AND IT IS NOT MAINTAINED BY HAND. 9.2 asks that
the spawn lane's case-name set equal the split lane's. Registering the spawn
arm as a second copy of each block would satisfy that today and drift the first
time somebody adds a scenario to one of them - and a spawn lane covering LESS
goes green while proving less, with nothing about a smaller green lane looking
wrong. The eleven split-environment blocks become eleven calls to one macro
that emits BOTH arms from one filter, so a case added to either is added to
both or to neither.
scripts/ci/spawn_lane_parity.py asserts it anyway, because a macro is one edit
from a hand-written block. It compares SETS rather than counts - a count can
agree while the membership does not - and carries exactly one sanctioned
asymmetry, named in the file: integration-split also labels the PUSH-MONOLITH
arm, whose scenario is monolith-only by construction. R-16: introduce a
split-only block and it reds naming the case and the macro to use.
THREE CASES SKIP UNDER SPAWN, BY NAME. CtWireScenario reads PipeApplier's
in-process tallies through ServerVerbs(); under spawn those live in the SERVER's
process and this one's copy is 0 forever, so the assertions were failing about
somebody else's memory. That is a harness limit, not a product defect - the
records themselves cross, which the retrace lane proves. They are SKIPPED and
not deregistered: a case dropped from the registration would make the two sets
differ silently, which is the drift the macro exists to prevent. What retires
the skip is cross-process telemetry, which is also what S8's SessionFaultCount
needs.
TWO REAL REGRESSIONS THE EXISTING GATES CAUGHT, both from this refactor:
* the macro dropped the per-block TEST_LIST variables, which were what
SplitLogPaths fed to give every entry a PRIVATE MOBILEGL_LOG_FILE_PATH.
SplitLogPaths.PrivateAndDistinct went red naming the first orphan. The
macro accumulates list NAMES now, so a block added later is covered without
anyone remembering to extend a list - and the names have to be unique,
because gtest_discover_tests' TEST_LIST is populated when CTEST runs, not
at configure time, so one shared variable held only the last block's
entries. That matters more on the spawn arm: there TWO PROCESSES append to
the path, so a shared file interleaves two sessions rather than merely
being ambiguous.
* the configure_file that substitutes those names ran BEFORE the F1 and Ct
blocks registered, so it captured neither. Same gate, naming
F1WireScenario.ClearBufferfiPixels. It now runs last.
junit_tally gains --require-spawn-ran beside --require-split-ran: the prefix IS
the arm, and a shared flag would let one lane tally the other's entries and call
itself run. Verified to discriminate - the spawn junit reads 96/0/5 under its
own flag and 0/0/0 under the split flag, which correctly fails.
Two death tests follow the ADOPT_TIER message the census commit retagged with
Fatal{UnimplementedAdoptTier}; their regexes asserted the old wording.
Unit 2353 (one pre-existing environmental failure), integration-split 186/186,
integration-spawn 102/102, retrace 6/6, fatal census 0 unmarked, G1 0 symbols
and .text unchanged at 0xa52203.