- snapshots doubled the cache restore (25 to 52 s) and device-offline retries went from 3 to 10
- the AVD job boots the new AVD once and powers it off; retrace jobs cold-boot it as before
- the AVD job boots the new AVD once per GPU mode and saves a snapshot into the cache
- retrace jobs load their mode's snapshot, log whether it loaded, and skip SDK setup on a hit
- ubuntu-latest moves to 26.04 job by job from 2026-10-19 to 11-19; a build on one image and its tests on the other would fail on glibc, so all 32 jobs move together now, pinned
- the ccache keys say ubuntu26: the 24.04 compiler's objects would not hit anyway
- unit, integration and integration-verify ctest run at -j nproc; the same labels already run at -j in the split lanes, with the same names and results
- build-linux, -split and -verify also publish the library (and the server image) alone; the retrace legs load nothing else, yet downloaded and unpacked the 0.5-0.8 GB test runtime
- build-retrace no longer waits for build-linux: it fetched the runtime only to test -f a path configure does not read
- every branch saves its own rolling ccache entry, deleting only its own old one (--ref); only dev saved, and dev has not run since the key rename, so every build was cold
- !cancelled() replaces job-level always() on the retrace, summary and skip-census jobs: a superseded run kept starting its pending legs and held the next run in the concurrency group for up to 154 minutes
- retrace and retrace-split run 8 legs at a time, not 4; the cap dates from per-leg LFS checkouts
- apt requests time out after 30 s and retry 3 times; install steps time out at 15 minutes, not 45
- the APK trace case matrix no longer waits for the build, which it does not read
- retrace-split prepares its metadata once, after the split library is unpacked
The Android retrace matrix gains a third lane, spawn-acceptance, beside
monolith-control and inproc-acceptance. Until now nothing in CI had ever
started a second MobileGL process on a device at all.
The apk.yml retrace job already carried a `transport` matrix dimension and
already branched on it, so the lane itself is three edits: a row in
github_transport_matrix, an elif in the retrace step, and a summary to render.
What needed thought was the gate.
THE SPAWN LANE IS APK-ONLY, and that is a statement about where evidence is
missing rather than about what spawn can do. Linux already has
test.yml's `retrace-split`, which is {backend, case, transport} over inproc AND
spawn with the two negative controls that make it mean anything. A spawn row in
the Linux transport matrix would pay for a second full sweep proving nothing -
and test.yml's `retrace` job stages no server image, so the lane would not
merely be redundant, it would be red. self_test_transport_matrices now asserts
the absence in both directions, because an accidental removal of the `if apk`
has to be caught in the self-test rather than in CI.
--require-spawn REQUIRES TWO SENTENCES, and the second is the one that matters.
ConfigLoader's marker proves the VALUE RESOLVED - it exists only in the Spawn
arm, which exists only under MOBILEGL_BUILD_DISAGGREGATED - but that is a
statement about a parser. `spawn ARMED - the server role runs in pid N` is
written only after the launch, the connect and the handshake have all
succeeded, and the pid in it is the SERVER's. It is the only line in the log a
same-process run cannot produce.
Both halves red once, measured:
* a monolith run demanded to prove spawn: REFUSED, transport_resolution_marker
false, server_pid null - while the retrace itself scored ssim=1.000000,
which is exactly the trap. OpenRA matches its golden under monolith too, so
the picture has never been the evidence.
* the same run's real spawn log with the one ARMED line deleted: REFUSED,
transport_resolution_marker TRUE and server_pid null. That is the
"resolved the transport and then never left the process" case, isolated.
THE PROOF USES `any` WHERE THE inproc GATE USES `all`, and the difference is
load-bearing rather than a relaxation. Under spawn two processes append to one
MOBILEGL_LOG_FILE_PATH, and the launcher deliberately scrubs every
MOBILEGL_IPC_* from the child's environment (anti-recursion catch (b)) - so the
server's own `Config: IPC` line reads strict=0 role-split-state=0 BY
CONSTRUCTION. Demanding every line match would red every spawn run for the one
thing the design requires.
--use-pbuffer is not optional on this lane: until P12 a spawned server cannot be
handed a window, because an ANativeWindow* is a pointer into the CLIENT's
process (Rule H). MOBILEGL_IPC_SERVER_PATH is deliberately NOT passed -
nativeLibraryDir carries an install-time hash that nothing off the device can
spell, and the Activity resolves it.
The local runner now passes the same knobs and the same --require-* flag CI
does, so "it works locally" can no longer mean "it ran monolith locally". That
change immediately caught its own omission: the first local run with the gate
armed failed on client_ipc_line_present=false because the runner had been
sending MOBILEGL_TRANSPORT alone.
Verified on device 2f7cbe2e: OpenRA.DirectGLES spawn, ssim=1.000000,
mismatchPixels=0, "real spawn transport, server role in pid 9885, and zero
Fatal records confirmed".
The step located the Android CMake identity by reading CMakeCache.txt next
to each compile_commands.json, but the trace flavor's build also produces
.cxx/tools/<flavor>/<abi>/compile_commands.json copies whose cache lives in
the real per-ABI build dir, so the proof failed on a file-layout assumption
rather than on missing evidence. Remove the step and its artifact upload.
The remove-artifact-clutter jobs deleted trace fixtures and results while
reading job/artifact inventories through unchecked process substitution, so
a failed GitHub API page looked like 'no failed cases' and fixtures for a
red retrace could be deleted. test.yml cleanup also did not wait for the
retrace-split lane even though those jobs download the same fixtures.
Read both paginated inventories into files before any deletion so an API
failure fails the step, add retrace-split to the cleanup dependencies, and
retain fixtures plus result artifacts for failed cases in all three lanes.
- the MGPipe phases land as a series of pushes and each one needs the full lane; dispatching
by hand after every merge is a step that gets forgotten
- both entries carry a remove-before-merging-to-dev note: dev's trigger set is what ships
Program 203 reuses prefixSumCache for a second subgroup reduction before every workgroup invocation has consumed the first result. Add a fingerprint-gated SPIR-V pass that inserts the missing Workgroup acquire-release barrier while preserving native subgroup operations.
Keep the repair opt-in behind MOBILEGL_ITERATIONRP_FIX_BARRIER, cover insertion, pass-through, and idempotence, and enable it together with the existing iterationRP subgroup repairs for the matching Linux and Android CI retraces.
iterationRP's Program 203 declares shared vec2 prefixSumCache[32] for a
512-invocation workgroup indexed by gl_SubgroupID; any device narrower
than 16 lanes partitions into more than 32 subgroups and the pack writes
shared memory out of bounds (heap corruption on lavapipe's CPU
rasterizer, ssim 0.028 on the CI retrace). Fix it where the fault lies -
in the fixture - and keep the GL contract sound everywhere else:
- FixIterationRPSubgroupScratchPass: fingerprint-gated SPIR-V pass that
grows exactly that array to ceil(invocations/width) entries on sub-16-lane devices; every other module passes through byte-identical.
- DeriveNumSubgroupsPass stays default-on for the Adreno topology bug
and is made spec-sound: pipelines request REQUIRE_FULL_SUBGROUPS
whenever the workgroup shape makes the flag legal (computeFullSubgroups
enabled, local_size_x a multiple of the native width, subgroup count
within maxComputeWorkgroupSubgroups).
- EmulateSubgroupsPass: 32-lane virtual-subgroup lowering kept in-tree
as a last resort, enabled only by MOBILEGL_MAGMA_EMULATE_SUBGROUP=1 on
devices with no native subgroup support; fails closed on extended
subgroup instructions and on modules whose added scratch would exceed
maxComputeSharedMemorySize.
- IterationRPFirstReductionScenario skips gracefully outside the pack's
16..256-lane source domain; the new IterationRPScratchFixScenario runs
the fixture-shaped reduction on any width and asserts the exact
width-independent total. DriverPost keeps reporting FAIL on
out-of-domain devices.
- Program203 -> IterationRP rename throughout; the per-trace
num_subgroups_quirk plumbing is removed from the trace replayer, JNI
chain, and CI workflows.