Phase U (U.1-U.4) shipped: the unified retail-faithful render pipeline replacing the abandoned two-pipe split (#103). Indoor rendering VISUALLY VERIFIED — solid walls, no terrain bleed, per-cell clip gating works. Two root-caused EnvCellRenderer self-contained-GL-state fixes landed (uViewProjection stale-matrix; inherited blend/depth-mask). Residual threshold "flap" (OutsideView instability from the per-frame view-dependent portal BFS) is precisely root-caused via ACDREAM_PROBE_VIS and scoped to U.4c (PVS / stab_list grounding, retail-faithful). Handoff captures the [vis] evidence, the retail anchors, and the next-session pickup. U.5 (outdoor->building peering) + U.6 (dungeon scale) remain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 KiB
Phase U.4 — shipped (unified pipeline + indoor rendering) + the threshold "flap" handoff (2026-05-30)
TL;DR
The unified retail-faithful render pipeline (Phase U) is built and shipped through U.4, and indoor rendering is visually verified correct: standing inside a Holtburg cottage / cellar / inn, the cell-shell walls render solid, terrain no longer bleeds into interiors, and the per-cell clip gating works. This replaced the abandoned two-pipe (inside/outside) split (#103). Modern code, retail behavior.
One residual remains: the threshold "flap" — crossing a doorway (inside↔outside), terrain
- building-shells briefly vanish leaving only un-gated geometry (particles + live entities)
over the bluish clear color. This is precisely root-caused (not a mystery): our per-frame
view-dependent portal BFS is unstable at multi-hop exit paths, so
OutsideViewflickers empty → terrain getsSkip-ped. The retail-faithful fix is PVS (stab_list) grounding — a focused sub-step, U.4c. It was deliberately NOT attempted at the end of this (very long) session to avoid thrashing a fragile cell-resolution area (the #98 lesson + the "don't push tired design calls late-session" rule).
Visual gate status: PASS for the indoor case; the seamless-threshold criterion is deferred to U.4c.
What shipped this session (Phase U, all committed on claude/thirsty-goldberg-51bb9b)
| Stage | What | Commit(s) |
|---|---|---|
| Spec + plan | Phase U design + implementation plan | 8601137, 0f7b395 |
| U.1 | Delete the two-pipe machinery (kept all audited fixes) | 3fc77be |
| U.2a | Portal BFS: closest-first ordering + retail fixpoint termination | d880775 (+ 306cdb0 review fixups) |
| U.2b | Reciprocal OtherPortalClip (+ CRITICAL fix: resolve by other_portal_id, not first-OtherCellId-scan) |
3916b2b → 65781f5 |
| U.2c | ClipPlaneSet (NDC convex region → gl_ClipDistance planes, 8-cap + scissor fallback) |
a83b430 |
| U.2d | ACDREAM_PROBE_VIS visibility probe (in RenderingDiagnostics, Core) |
0b12583 |
| U.3 | GPU gate: gl_ClipDistance in mesh+terrain shaders, ClipFrame, scoped clip bracket |
bf2e559 → 864fc5f |
| U.4 | Unified gated draw pass (ClipFrameAssembler, per-instance slots, EnvCellRenderer.Render wired, terrain Skip/Scissor/Planes) + ResolveEntitySlot tests |
7993e06 → 354ca74 |
| U.4 fix 1 | EnvCellRenderer.Render uploads its own uViewProjection (was inheriting WbDrawDispatcher's → stale → seam flicker) |
d6d4671 |
| U.4 fix 2 | EnvCellRenderer.Render sets its own BLEND + DepthMask per pass (was inheriting → opaque walls blended against clear color → "transparent walls") |
9be9547 |
Build green, App tests 151/151 throughout. Core failures are the documented pre-existing static-leak flakiness (zero Core production files touched by U.4). Branch is UNPUSHED — push decision is the user's.
Two reviews caught real CRITICALs (the process earned its keep)
- U.2b: reciprocal-portal resolved by scanning for the first
OtherCellIdmatch → mis-resolved when a cell has two portals to one neighbour (real on Holtburg cellar0x148↔0x149) → hidden geometry. Fixed by plumbing the dat'sOtherPortalIdback-link. - U.3:
GL_CLIP_DISTANCE0..7enabled globally while 6 non-clip-writing shaders ran → undefined behavior (benign on the dev driver, a portability landmine). Fixed by scoping the enable to the world-geometry draws.
The recurring lesson (now 3×): EnvCellRenderer must own its GL state
EnvCellRenderer.Render was dormant pre-U.4 (only the deleted two-pipe path called it). When
U.4 wired it into the live loop, it surfaced THREE inherited-GL-state bugs in sequence:
- (2026-05-28, pre-U.4) cull state → "missing walls".
- (U.4 fix 1)
uViewProjection→ stale-matrix seam flicker. - (U.4 fix 2) BLEND + DepthMask → opaque walls blending against the clear color.
A renderer that runs mid-frame after other consumers MUST establish every GL state it depends
on (matrix, blend, depth-mask, cull, front-face, A2C) — never inherit. See the memory note
render-self-contained-gl-state.
The flap — root cause (evidence-based)
Symptom
Crossing a doorway (the user's screenshot, inside→outside): terrain + building-shells + cell-
shells vanish, leaving only particles + live entities (NPCs/doors/items, slot 0 = no-clip) over
the bluish clear color (glClearColor(0.05,0.10,0.18)).
Evidence — ACDREAM_PROBE_VIS [vis] lines, SAME cell across frames
root=0xA9B40171 cells=4 ids=[...,0xA9B40170] outside(polys=1,planes=4) ← window cell reached → terrain draws
root=0xA9B40171 cells=3 ids=[0xA9B40171,75,74] outside(polys=0,planes=0) ← window cell dropped → terrain SKIPPED
Over one cellar traversal: 10 empty-OutsideView frames vs 16 non-empty — for the same
cells. The ground-floor cell 0xA9B40170 (which holds the window / 0xFFFF exit portal)
flickers in and out of the visible set as the camera moves.
Mechanism
- Our
PortalVisibilityBuilderruns a per-frame, view-dependent portal BFS. TheCameraOnInteriorSideportal-side test culls portals based on the camera's exact pose. - Near a portal boundary, a tiny camera move flips which portals pass the test → the multi-hop path (cellar → ground floor → window) breaks in some frames.
- When the exit-portal cell isn't reached,
OutsideViewis empty. ClipFrameAssemblermaps emptyOutsideView→TerrainMode.Skip(the bleed fix) ANDoutdoorVisible=false→ building-shells culled.- Result: terrain + building-shells flap off whenever the exit path momentarily breaks.
Why retail is seamless (the fix direction)
Retail grounds visibility in a precomputed potentially-visible-set: on cell entry,
CEnvCell::grab_visible_cells populates the visible_cell_table from the cell's stab_list
(a STABLE per-cell PVS), and seen_outside is a stable per-cell flag ("this cell is adjacent to
the exterior"). The per-frame PView clip refines WHERE things draw, but the SET of reachable
cells (and thus whether the exit portal is reachable) is stable. Our pure per-frame view-dependent
BFS has no such anchor → it flaps. Retail anchors: CEnvCell::grab_visible_cells ~311878,
seen_outside set in find_cell_list ~311044, stab_list on CEnvCell.
U.4c — proposed scope (stabilize portal visibility)
Goal: make the visible-cell set (and therefore OutsideView / the terrain-draw decision)
stable so the threshold is seamless, the retail way — NOT a hysteresis/last-frame band-aid
(that's a workaround; forbidden).
Candidate approaches to settle in a brainstorm (do NOT jump to code — fragile area, #98 history):
- PVS / stab_list grounding (most retail-faithful). Load each cell's
stab_list(the dat has it) into a stable per-cell visible set on cell entry; the per-frame BFS operates within / is anchored by it, so the exit-portal cell never drops out. This is what makes retail seamless by construction. Largest change; needs the stab_list dat read + integration. - Stable
seen_outsideterrain-draw decision. Decouple "should terrain draw" (stable: does the camera cell statically reach a0xFFFFexit portal within its building's portal graph?) from "where to clip it" (OutsideView). Still needs a clip region whenOutsideViewmomentarily empties (else bleed) — likely the raw exit-portal projection as a fallback. - Investigate the specific instability first. Why does
0xA9B40170drop from the cellar's BFS at certain angles — is itCameraOnInteriorSideon the stairwell portal being pose-brittle? A more robust (epsilon / reciprocal-aware) side test might stabilize the common case before a full PVS port. Cheapest; verify it's retail-faithful, not a fudge.
Recommendation: brainstorm U.4c (superpowers:brainstorming) starting from approach 1 vs 3,
using the [vis] probe as the apparatus. Build a stable visible-set; the clip stays per-frame.
Also deferred (not the flap, separately tracked)
- U.5 — outdoor-camera → building-interior peering (retail
outdoor_pview/DrawBuilding/DrawPortal/ConstructView(CBldPortal)). Standing OUTSIDE looking INTO a house still shows no interior; that's U.5, not the flap. Open data dependency: render-side building-exterior portal geometry (we carryBldPortalInfophysics-side). - U.6 — dungeon-scale validation; close/relate #95 + the residual #102 diamond-clip note.
- Minor leftovers flagged in the U.4 review:
AppendSlotcollapses the 3Count==0states (U.4c should branchIsNothingVisible/UseScissorFallbackbefore calling it); orphanedLandblockEntriesWithoutAnimatedIndex; deadBuildingShellAnchorPass/Rejectcounters.
Apparatus (use this, evidence-first)
ACDREAM_PROBE_VIS=1—[vis]line per cell change:rootcell, visible-cell count + ids,outside(polys=N,planes=M), per-cell plane counts, scissorfallbacks. The flap shows asoutside(polys=0)frames interleaved withoutside(polys=1)for the same cell. Owner:AcDream.Core.Rendering.RenderingDiagnostics.EmitVis.- Launch block: CLAUDE.md "Running the client" +
ACDREAM_PROBE_VIS=1, pipe to a fresh log. Read it with PowerShellSelect-String(the Tee log is UTF-16) ortr -d '\0' | grepin bash.
Next-session pickup prompt
Phase U.4c — stabilize portal visibility (fix the threshold "flap"). The unified render
pipeline (Phase U, U.1-U.4) is shipped and indoor rendering is visually verified correct.
The remaining issue is the doorway "flap": terrain + building-shells flicker off when
crossing the threshold. Root cause is in
docs/research/2026-05-30-phase-u4-shipped-and-flap-handoff.md — READ IT FIRST. It is the
per-frame view-dependent portal BFS being unstable at multi-hop exit paths (OutsideView
flickers empty → terrain Skip'd). The retail-faithful fix is PVS / stab_list grounding
(retail grab_visible_cells / seen_outside). This is a FRAGILE cell-resolution area (#98
saga) — start with superpowers:brainstorming on PVS-grounding vs a targeted side-test
stabilization; use ACDREAM_PROBE_VIS as the apparatus; NO workarounds (no hysteresis
band-aid). Do NOT touch the indoor rendering (it works). U.5 (outdoor→building peering)
and U.6 (dungeon scale) remain after U.4c.
Git state
- All Phase U work committed on
claude/thirsty-goldberg-51bb9b, unpushed (push is the user's call). - Two
git stashentries on the branch (#98/#101physics WIP, pre-triage backup) — preserve, do not drop.