test(render): add the repeat-run connected gate; record V4c/V4d re-land conditions

The V4c blank-world regression was intermittent - roughly one launch in three at the worst location, zero in seven at the parent - so a single connected capture passes the broken binary most of the time and gates nothing. The new gate runs N full connect-teleloc-render-screenshot cycles with graceful logout and a per-run verdict by screenshot content size, refuses to start if a client is already using the shared test account, and pins the teleloc because the failure rate is location-sensitive. Ten clean runs bound a one-in-three defect below roughly four percent.

Campaign doc 5.5 records the revert evidence and the binding re-land conditions: the GL ring write path moves to mapped unsynchronized writes, and V4c/V4d re-land only at 10/10 rendered plus a passing offline gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-27 22:43:03 +02:00
parent 543bc79f8a
commit 61f3c5d803
2 changed files with 152 additions and 0 deletions

View file

@ -603,6 +603,40 @@ as well, and the `SceneLighting` UBO (binding 1) by terrain and the four viewpor
portal renderers. GL binding points are global, so the safe move while those consumers
are still raw GL is to leave both bound as they are and convert them with the spine.
### 5.5 The V4c/V4d revert (2026-07-27) and the re-land conditions
V4c and V4d were reverted at `543bc79f`/`b537f3a9`/`ad61f250` after the first
connected sessions showed a **blank world** — UI rendered, sounds played, the log
was clean, `world-reveal` reported `visible=True`, and the user hit one AMD driver
timeout. The offline gate had passed both slices at noise level.
What the debugging established, with the connected screenshot-probe apparatus:
the defect is **intermittent (~1 in 3 at the worst location, 0 of 7 at the V4c
parent)** and scene-dependent; after the first world frame the default
framebuffer's colour reads return garbage (float depth bytes) and `glClear`
stops having any effect, with `glGetError` clean throughout — a GPU-side fault,
not an API error. Every added CPU↔GPU sync point monotonically suppresses it,
which also made the mechanism unprovable in situ. Best-supported cause: the
frame ring performs 1040 partial `glBufferSubData` updates per frame into a
buffer object that already-submitted same-frame draws are still reading; the
offline flat path issues 24 such updates, the connected PView path dozens —
which is exactly the offline/connected axis. This likely also explains the TDR.
The contract amendments (`111e7236` InverseAlpha, `c7f5f251` integer vertex
attributes + tiling binding) were **kept** — they are dark, test-covered, and
correct.
**Re-land conditions, binding:**
1. The GL ring's write path moves to `glMapBufferRange(WRITE | UNSYNCHRONIZED |
INVALIDATE_RANGE)` — the canonical GL ring idiom, which states the non-overlap
invariant to the driver instead of leaving `glBufferSubData`-into-an-in-use-
buffer to driver heuristics.
2. V4c and then V4d re-land as reverts-of-the-reverts plus the ring change, each
gated by `tools/run-repeat-connected-gate.ps1` at **10/10 rendered** — a single
connected run passes a broken binary ~70% of the time and gates nothing.
3. The offline pixel gate still passes, and the gate location stays pinned
(failure rate is location-sensitive; stray input moves the character).
### 5.4 The null-target `BeginPass` divergence (V4c) — must be undone at V6
V4c had to stop GL's `BeginPass` from binding framebuffer 0 when a pass declares