Checkpoint of the unified retail-faithful indoor render. The two-week HANG/grey is fixed and the interior seals (live-verified by the user). Commits the session render-rewrite foundation together with the fixes that made it functional. - HANG fix: PortalVisibilityBuilder.Build portal flood did not terminate (the faithful ProjectToClip near-side clip drifts per round, defeating the CellView dedup; the BFS had no bound after U.2a removed MaxReprocessPerCell). Fix = drift-tolerant snapped/canonical CellView.Add dedup (PortalView.cs) plus restored MaxReprocessPerCell=16 bounded re-enqueue (PortalVisibilityBuilder.cs). Re-enqueue is kept (load-bearing for late-slice propagation, Build_ViewGrowthAfterDoneCell_PropagatesNewSlicesToExit); only its count is capped. CellViewDedupTests added. - Seal (DrawCells Task 2): RetailPViewRenderer.DrawEnvCellShells draws EVERY visible cell via IndoorDrawPlan.ShellPass (was gated on the ClipFrameAssembler slot filter, leaving slot-less cells grey). - Look-in FPS: GameWindow exterior look-in candidates limited to the player landblock +-1 (was all ~81 loaded LBs iterated every outdoor frame). No behaviour change (far cells were >48m, already culled). Remaining dominant issue = the FLAP at transitions: viewer-cell metastability (render roots at the camera-eye cell, which oscillates outdoor-indoor as the 3rd-person boom drifts across the doorway, confirmed in render-sig). SEPARATE fix, NOT the DrawCells port. Full handoff + flap fix plan + tracked follow-ups (#78 terrain, look-in-from-inside, look-in FPS, L-spotlight): docs/research/2026-06-07-indoor-render-session-handoff.md. Baselines: build 0 err; App.Tests 210/210; Core.Tests 1331 pass / 4 fail (pre-existing) / 1 skip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
160 lines
12 KiB
Markdown
160 lines
12 KiB
Markdown
# Indoor Render — Session Handoff: HANG fixed + interior SEALS; the FLAP is next — 2026-06-07
|
||
|
||
> Worktree `thirsty-goldberg-51bb9b`, branch `claude/thirsty-goldberg-51bb9b`. PowerShell on Windows;
|
||
> launch logs UTF-16; build before launch; acceptance is the user's eyes. Live ACE `127.0.0.1:9000`,
|
||
> `testaccount`/`testpassword`, char `+Acdream` (spawns near the Holtburg / "Arcanum" cottage —
|
||
> landblock `0xA9B4`, cottage cells `0xA9B4016F–0175`). Do NOT branch/worktree, push, or `git stash`/`gc`.
|
||
|
||
## TL;DR
|
||
|
||
The two-week indoor-render **HANG is FIXED** and the interior **SEALS** (walls/floor/ceiling draw,
|
||
textured) — both committed this session and live-verified by the user ("Ok now it runs!"). A
|
||
structured live test pinned the remaining dominant visible issue, the **FLAP at transitions**, as
|
||
**viewer-cell metastability**: the render roots at the camera-eye cell, which oscillates
|
||
outdoor↔indoor as the 3rd-person boom drifts across the doorway plane. **The flap is a SEPARATE,
|
||
already-designed fix — it is NOT the verbatim DrawCells port; finishing the port will not fix it.**
|
||
Next session: **fix the flap** (camera-boom stability + viewer-cell dead-zone). Tracked follow-ups:
|
||
#78 terrain gating, look-in-from-inside sealing, look-in FPS, L-spotlight.
|
||
|
||
## What shipped this session (committed — see `git log` on this branch)
|
||
|
||
### 1. The HANG fix (the blocker)
|
||
Indoor frames froze (`AppHangB1`; not a crash — captured the spinning managed stack via a
|
||
`dotnet-stack` hang-watcher). Root cause: `PortalVisibilityBuilder.Build`'s portal-visibility flood
|
||
**did not terminate** for real cottage geometry. Two layers, two fixes (both kept):
|
||
- **A — drift-tolerant `CellView.Add` dedup** (`src/AcDream.App/Rendering/PortalView.cs`). The flood
|
||
re-queues a cell every time its view GROWS; growth only stops when the dedup recognises a re-clipped
|
||
region as a duplicate. The faithful `ProjectToClip` near-side clip drifts per round, so the old
|
||
exact index-by-index match (eps 1e-4) never caught the near-duplicate → unbounded growth → O(n²)
|
||
CPU-spin in `CellView.Add`. Fix: key each polygon by its vertices **snapped to a 1e-3 NDC grid**,
|
||
consecutive-dedup'd, **canonically rotated** to a lex-min start → finite key space → convergence.
|
||
Tests: `tests/AcDream.App.Tests/Rendering/CellViewDedupTests.cs` (3).
|
||
- **B — bounded re-enqueue** (`src/AcDream.App/Rendering/PortalVisibilityBuilder.cs`). A alone did not
|
||
fully converge (the spin relocated to `ScreenPolygonClip.ClipByEdge` — bounded loops — inside the
|
||
still-non-terminating BFS). Restored the **`MaxReprocessPerCell = 16`** hard cap that Phase U.2a
|
||
deleted ("fixpoint termination" left the loop with NO bound). **Kept the re-enqueue** — it is
|
||
load-bearing for late-slice propagation (`Build_ViewGrowthAfterDoneCell_PropagatesNewSlicesToExit`).
|
||
Pure enqueue-once was tried and **broke that test**, so re-enqueue is kept and merely bounded.
|
||
- Deep diagnosis + the reassessment that led to B: `docs/research/2026-06-06-indoor-render-hang-rootcause.md`.
|
||
- **Verified:** clean exit (255→0); runs indoors with no freeze; the indoor flood converges in ~1
|
||
round/cell at normal positions (measured 3–5 pops/frame, 1 view-poly/cell). The cap only bites at
|
||
the metastable doorway.
|
||
|
||
### 2. The SEAL (verbatim DrawCells port — Task 2)
|
||
`RetailPViewRenderer.DrawEnvCellShells` now iterates `IndoorDrawPlan.ShellPass(pvFrame)` — **every**
|
||
visible cell's shell draws (was gated on `ClipFrameAssembler`'s slot filter → cells without a slot
|
||
were silently dropped → grey clear-color void). Verified: interior seals + textured. (Task 1
|
||
`IndoorDrawPlan` + its test committed earlier as `bff1955`.)
|
||
|
||
### 3. Look-in FPS
|
||
`GameWindow` exterior-look-in candidate cells limited to the player's landblock **±1** (was **all
|
||
~81 loaded landblocks** iterated every outdoor frame just to discard them via the 48 m seed cutoff).
|
||
Provably no behavior change (excluded cells are >48 m, already culled). Outdoor FPS improved but
|
||
still **~110 fps / ~9 ms (was ~200)** — `DrawPortal` still draws ~12 building interiors/frame (see
|
||
follow-up).
|
||
|
||
## Baselines (must hold at next session start)
|
||
- `dotnet build -c Debug` **0 errors**.
|
||
- App.Tests **210/210** (205 baseline + IndoorDrawPlanTests 2 + CellViewDedupTests 3).
|
||
- Core.Tests **1331 pass / 4 fail / 1 skip** — the 4 are pre-existing Physics door/step-up, unrelated.
|
||
|
||
## Structured live test — findings (Holtburg/Arcanum cottage, 2026-06-07)
|
||
User walked a 6-step protocol (inside-still → camera-pan → doorway-threshold → just-outside →
|
||
looking-at-cottage → cellar) and reported 8 behaviours; `ACDREAM_PROBE_FLAP` `[render-sig]`
|
||
correlated each.
|
||
|
||
| # | Observed | Cause | Bucket |
|
||
|---|---|---|---|
|
||
| 2,3,6,8 | walls briefly transparent / window+entrance "covered by the world background" / abrupt "teleport" through the doorway — all **at transitions (camera crossing a threshold)** | **THE FLAP** | viewer-cell stability (NEXT) |
|
||
| 1 | outdoor grass covers the cellar-entrance hole (steady, looking in from outside) | outdoor terrain not gated over the indoor floor opening | **#78** terrain gating |
|
||
| 7 | from inside, a building seen through the doorway has transparent walls (world-bg shows); pops back when you step outside | look-out shows other buildings unsealed | look-in/look-out completeness |
|
||
| 5 | spotlight blobs on textures from the ceiling lamp (always been there) | point-light artifact | **L-spotlight** (separate) |
|
||
| FPS | inside very high; outside **110 fps / ~9 ms** (was ~200) | `DrawPortal` draws ~12 interiors/frame | look-in cost |
|
||
| 4 | cellar transitions **stable** ✓ | vertical transition doesn't cross the outdoor boundary | — |
|
||
|
||
### The FLAP — pinned (render-sig evidence)
|
||
`[render-sig]` over the doorway shows the render branch + the cell it roots at flip-flopping while the
|
||
**player cell stays inside**:
|
||
```
|
||
50× branch=OutdoorRoot viewer=0xA9B40031 (outdoor) player=0xA9B40171 (indoor) gate=in
|
||
16× branch=RetailPViewInside viewer=0xA9B40170 (indoor) player=0xA9B40171 gate=in
|
||
113× branch=RetailPViewInside viewer=0xA9B40171 (indoor) player=0xA9B40171 gate=in
|
||
... oscillates 0x0031 ↔ 0x0170 ↔ 0x0171 frame-to-frame ...
|
||
```
|
||
**Mechanism:** the render roots at the **viewer (camera-eye) cell** (`clipRoot = viewerRoot`, Phase W
|
||
"one viewpoint"). The 3rd-person boom drifts the eye across the doorway plane; acdream re-resolves the
|
||
viewer cell fresh each frame with **no hysteresis** → it flips between outdoor `0x0031` and indoor
|
||
`0x0170/0x0171` → the render flips `OutdoorRoot`↔`RetailPViewInside` → the indoor seal drops (walls
|
||
transparent, outdoor world/grass shows) then re-seals → **flapping**. This is exactly the 2026-06-05
|
||
viewer-cell-flicker diagnosis, now confirmed against the live render branch.
|
||
|
||
## RECOMMENDED NEXT WORK — fix the FLAP (separate, already-designed)
|
||
Per `docs/research/2026-06-05-viewer-cell-flicker-rootcause-and-fix-plan-handoff.md`, 3 retail-faithful parts:
|
||
1. **Viewer-cell dead-zone (do this first)** — ±0.2 mm cell hysteresis so a sub-mm eye drift can't flip
|
||
the cell (`PhysicsCameraCollisionProbe.SweepEye`; retail `point_inside_cell_bsp` 0x53c1f0). Highest
|
||
leverage — likely kills most of the flap on its own.
|
||
2. **Camera-boom stability** — stop the boom drifting at rest (`RetailChaseCamera.UpdateCamera`; retail
|
||
`UpdateCamera` 0x456660).
|
||
3. **w-space (w=0) portal clip** — close-portal projection degeneracy (`PortalProjection` /
|
||
`PortalVisibilityBuilder`; retail `GetClip` 0x5a4320 / `polyClipFinish` 0x6b6d00). Lower priority.
|
||
|
||
Apparatus ready: `ACDREAM_PROBE_FLAP` emits `[render-sig]` (branch/viewer/player/gate per frame),
|
||
`[flap]`, `[flap-cam]`, `[flap-sweep]` — light enough to launch with (the heavy `ACDREAM_PROBE_SHELL`
|
||
firehose is what previously caused an I/O stall; avoid it).
|
||
|
||
## Tracked follow-ups (logged; not yet fixed)
|
||
- **#78 terrain gating** — outdoor terrain (grass) draws over the indoor cellar-entrance hole (and likely
|
||
other indoor floors). Decomp anchor `CEnvCell::find_visible_child_cell` (`acclient_2013_pseudo_c.txt:311397`).
|
||
- **Look-in-from-inside** — buildings seen through your door/window from inside render unsealed
|
||
(transparent walls); the look-out pass doesn't draw other buildings' shells. DrawCells port Task 5/7
|
||
territory (or R2 "outside-looking-in").
|
||
- **Look-in FPS** — `DrawPortal` draws ~12 building interiors every outdoor frame (~110 vs ~200 fps).
|
||
Optimize: only look into buildings whose exit portals are frustum-visible; skip when no door is in view.
|
||
- **L-spotlight** — ceiling-lamp point light makes spotlight blobs on textures. Pre-existing, separate.
|
||
|
||
## verbatim DrawCells port — remaining tasks (deferred)
|
||
Plan: `docs/superpowers/plans/2026-06-06-verbatim-retail-indoor-render-port.md`. Task 1 + Task 2 done.
|
||
**Task 3** (objects no-clip) is effectively already satisfied (objects draw membership-gated with no
|
||
clip; no half-characters observed). **Tasks 4–8** (per-slice trim, look-out, delete `ClipFrameAssembler`,
|
||
look-in, final) are **cleanup with no current visible payoff** — the seal works and there is **no visible
|
||
bleed** (the "glitches between cells" were the FLAP, not bleed). **Task 4 (trim) is intricate** (its
|
||
per-slice `_clipFrame.Reset()` is coupled with the landscape/particle passes that still read
|
||
`clipAssembly` slots) and **risks re-slicing the working seal** — do it carefully, fresh, and only when
|
||
clean architecture is the priority.
|
||
|
||
## DO NOT re-litigate
|
||
- The HANG fix (A drift-dedup + B bounded re-enqueue) is correct + verified. **Do NOT try pure
|
||
enqueue-once** — it breaks `Build_ViewGrowthAfterDoneCell_PropagatesNewSlicesToExit` (late-slice
|
||
propagation needs the re-enqueue; the cap, not removal, is the termination guarantee).
|
||
- The grey was the `drawableCells` / `ClipFrameAssembler` slot filter; Task 2 fixed it. The clip math is
|
||
faithful — do not "harden the w-clip".
|
||
- **The FLAP is NOT the DrawCells port.** It is viewer-cell metastability (camera/membership). Tasks 4–8
|
||
will NOT fix it.
|
||
- The render roots at the VIEWER (camera-eye) cell intentionally (Phase W "one viewpoint"). The flap fix
|
||
is to STABILISE the viewer cell (dead-zone + boom), NOT to re-root at the player cell (superseded).
|
||
|
||
## Copy-paste pickup prompt (next session)
|
||
```
|
||
Pick up the indoor-render work in worktree thirsty-goldberg-51bb9b (branch
|
||
claude/thirsty-goldberg-51bb9b). PowerShell; launch logs UTF-16; build before launch; acceptance is
|
||
the user's eyes. Do NOT branch/worktree, push, git stash/gc, or revert the dirty tree.
|
||
|
||
Read first: docs/research/2026-06-07-indoor-render-session-handoff.md (state, what shipped, the FLAP
|
||
diagnosis, do-not-relitigate). Then docs/research/2026-06-05-viewer-cell-flicker-rootcause-and-fix-plan-handoff.md
|
||
(the flap fix plan).
|
||
|
||
Confirm baselines: build 0 errors; App.Tests 210/210; Core.Tests 1331 pass / 4 fail (pre-existing) / 1 skip.
|
||
|
||
The indoor HANG is fixed and the interior SEALS (shipped + committed last session). The remaining
|
||
dominant visible issue is the FLAP at transitions — viewer-cell metastability: the render roots at the
|
||
camera-eye cell, which oscillates outdoor↔indoor as the 3rd-person boom drifts across the doorway (no
|
||
hysteresis), confirmed in [render-sig]. FIX THE FLAP, starting with the viewer-cell dead-zone
|
||
(PhysicsCameraCollisionProbe.SweepEye; retail point_inside_cell_bsp 0x53c1f0), then camera-boom
|
||
stability (RetailChaseCamera.UpdateCamera; retail UpdateCamera 0x456660). Launch with ACDREAM_PROBE_FLAP
|
||
only (NOT ACDREAM_PROBE_SHELL — it stalls on I/O). Gate on the user's eyes at the cottage doorway.
|
||
|
||
Do NOT: retry pure enqueue-once (breaks late-slice propagation); re-root render at the player cell
|
||
(viewer-cell rooting is intentional); finish DrawCells port Tasks 4-8 expecting it to fix the flap (it
|
||
won't). Tracked follow-ups (not the flap): #78 terrain gating (grass over cellar hole), look-in-from-
|
||
inside sealing, look-in FPS (DrawPortal ~12 interiors/frame), L-spotlight.
|
||
```
|