It was one case on one backend under one transport: OpenRA, DirectGLES,
inproc. It is now every CI case, both backends, inproc AND spawn - 154 CI
entries, 156 local ctest entries - because every defect the spawn work turned
up was a question about the SERVER's process and not about any one trace or
either backend.
THE DirectVulkan ARM. tools/trace_replay/CMakeLists.txt carried "a
DirectVulkan arm would be measuring a server nobody has written yet". True
when P5 wrote it; false since P5f/P7 landed Magma's server consumers and its
run-ahead readiness gate. Leaving it would have shipped a split shape whose
second backend nothing ever ran - the same class of hole as a lane that runs
monolith under a name that says split. Magma now brings up a server process
of its own and OpenRA retraces across it at ssim=1.000000, which is only
possible because this branch also taught the child to load its config: before
that, a DirectVulkan session brought up a DirectGLES server and the two
halves disagreed about which backend the capability mask describes.
`split` BECOMES AN OPT-OUT. It began as an opt-in naming one case, because
one case was the work. Marking 39 cases `"split": true` would carry no
information and would reinstate the hole the key was written to close, only
mirrored: a case added later would get the monolith arms and be silently
absent from the split ones, and absent is the one state nothing reds on. A CI
case is now in the subset unless it says `"split": false`, which is a
reviewable claim that a trace cannot cross a role boundary.
THE CI MATRIX GAINS A THIRD DIMENSION. The job took {backend, case} and
hard-coded MOBILEGL_TRANSPORT=inproc in its own run block, so `spawn` had no
way into CI at all; the .SPAWN ctest entries existed only for a local build.
It is {backend, case, transport} now, the backends are the case's own
ci_backends rather than a hard-coded DirectGLES, and the artifact names carry
the transport or the two arms of one {backend, case} would overwrite each
other's uploads.
inproc STAYS AS THE CONTROL. Same library, same codec, same applier, same
picture; the only difference is which process the server role runs in.
Running only spawn would leave "did the transport break it?" and "did the
phase break it?" answerable only together - which is the question every
failure in this phase has turned on, including the SIGSEGV that D1 explained.
BOTH NEGATIVE CONTROLS FOLLOW THE ARM. retrace_pull_library_control.sh and
retrace_drop_draw_control.sh each exported MOBILEGL_TRANSPORT=inproc
themselves, so on a spawn job they would have gone on proving something about
the other arm while spawn ran unguarded. They read SPLIT_TRANSPORT now, and
default to inproc so a caller that sets nothing behaves exactly as before.
scripts/ci/control_smoke_test.sh: 22 passed, 0 failed against the stubbed
ctest, including the two modes that matter most - a red whose SSIM never fell
and a knob that armed and dropped nothing.
The spawn arm needs a second binary and the inproc arm needs none, so the job
places libMobileGLServer.so only on the spawn arm and a missing one is a
named failure there rather than a LaunchServer refusal 78 jobs deep.
Measured locally as this lands: 39 of the 78 SPAWN entries complete, all
green. The rest are still running.
BRIEF-P5E section 3 item 4 calls for three negative controls. Running them showed E1
(MOBILEGL_IPC_VERB_BARRIER=0) had THREE problems stacked, with the first two hiding the third.
This fixes the first two and narrows the third into something executable.
1. It selected a case that can never run in this lane.
E1's selection regex is (Triangle|ClearThenReadPixels), and the positive control gl added
under ID-115, TriangleScenario.TheServerStampedAVerbBoundaryOnThisDrawingFrame, matches it.
That case reads a PipeStats window out of the lane's OWN private log, so it runs only in the
strict-arming lane and skips everywhere else BY DESIGN, whatever this knob is set to. ID-62
makes a SKIPPED SELECTED entry a hard failure - correctly, because a control that silently
loses its subjects proves nothing. So E1 reported "the knob killed the pre-flight, not the
entry" while the knob was in fact working: four other selected entries went red with the
abort in the same run.
The fix excludes a case that can never run here, rather than teaching E1 to tolerate skips,
which would have thrown ID-62 away. ctest -R is POSIX ERE with no negative lookahead, so
run_control gains a fifth positional: a real ctest -E.
2. ctest and the log helper disagreed about who was selected.
run_control passed -E to ctest, but split_log_paths.py still filtered on -R alone, so it
counted the excluded entry as selected and then reported it as "did not run". The helper now
reads the same exclusion (SPLIT_LOG_EXCLUDE). An env var rather than another positional
because the helper's positional layout differs per mode - and a control whose two halves
disagree about its own subject set is exactly the class of bug this script exists to
prevent.
3. The real defect. ID-122 was right, and there is now a sharper statement of it.
With the first two fixed E1 is still red, and red on "10 selected entries did not fail": 4
of 14 went red, AND Fatal{BarrierViolation} appears ZERO times in the entire run - those 4
are red for other reasons. The evidence E1 demands has never appeared. The Fatal at
ClientSession.cpp:878 asserts an overlap OBSERVED BY THE APPLY THREAD, and at
MOBILEGL_IPC_BATCH_WAITS's default of 1 the barrier is taken at the next residual-reading
verb, so that overlap does not happen.
Further: with run-ahead armed, VERB_BARRIER=0 ALSO disarms run-ahead (it is the first
conjunct of RunAheadArmed(), ID-114), so the arm this knob produces is neither lockstep nor
run-ahead - it cannot serve as the negative control for either side.
E1 needs its falsification target REDEFINED, not its threshold adjusted. Left as a named
outstanding item, recorded in ID-122 and the open items.
E1's counting rule ("every selected entry must go red") is deliberately NOT relaxed here: it
was set to block the "any non-zero exit counts" false green, and loosening it before knowing
what E1 should falsify would only trade one false green for another.