acdream/docs/research/2026-05-28-a8-cellar-flap-handoff.md
Erik 5dc4140c11 feat(render): Phase A8 — indoor visibility + streaming fixes batch
Lands the working A8 indoor-rendering and streaming fixes accumulated this
session. User has verified these visually to some degree (e.g. lifestone /
translucent meshes confirmed fine under the FrontFace flip; bridge / wall /
collision regressions confirmed fixed after travel); not every path has been
exhaustively gated. The cellar-flap defect remains OPEN and will be solved
the retail-faithful way via a dedicated brainstorm (see handoff docs).

Rendering core (reviewed, high confidence):
- EnvCellRenderer SSBO stride fix: upload packed Matrix4x4[] (64B) instead of
  the 80B CPU InstanceData struct the shader never expected — fixes the
  transform/texture "explosion" for any draw with >1 instance (cells that
  dedupe to a shared cellGeomId). Real root cause.
- WB-style global FrontFace(CW) + per-batch CullMode carried through the MDI
  layout (GroupKey + BuildIndirectArrays + DrawIndirectRange split into
  same-cull runs with absolute uDrawIDOffset per run).
- EntitySet partitioning (IndoorPass / OutdoorScenery / LiveDynamic) +
  WorldEntity.BuildingShellAnchorCellId so building shells scope to their
  dat-derived building cell instead of rendering everywhere.
- RenderOutsideInAcdream (look into buildings from outside) +
  CollectVisiblePortalBuildings frustum cull of portal bounds.
- Sky-when-inside-building + per-cell audit probe + GL-state probe.

Streaming / perf (test-covered; not independently code-reviewed this session):
- Near/far priority queues so near work wins over far; PromoteToNear carries
  full landblock + mesh data; LandblockEntriesWithoutAnimatedIndex avoids
  rebuilding the animated-lookup dict in the hot draw path. Fixes the
  bridge-not-appearing / missing-walls / broken-collision-after-travel
  regressions and improves post-transition FPS.

Tooling + docs:
- tools/A8CellAudit: offline dat cell/portal/building dumper (portals +
  buildings modes) — reproduces the cellar-flap investigation with no launch.
- docs/research cellar-flap root-cause + option-2 handoff (the didInsideStencil
  double-duty finding + the WB-recursive design decision + brainstorm prompt),
  entity-taxonomy, replan, issue-78 visibility investigation.

Diagnostics retained on purpose: ACDREAM_A8_DIAG_* gates, portal_stencil.vert
provisional pos.w clamp, and the probe families are kept (env-var gated, zero
cost when off) because the pending option-2 cellar-flap brainstorm needs them.
Strip in the option-2 ship commit.

Indoor branch stays behind ACDREAM_A8_INDOOR_BRANCH=1 (default off = pre-A8
visual). Build green; App tests + Core (streaming/dispatcher/loader) tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 10:14:50 +02:00

7.9 KiB

Phase A8 Cellar-Flap Handoff - 2026-05-28

Status

The remaining cottage/cellar artifact is still visible. The user sees a short-lived green terrain-like rectangle/flap over the cellar entrance or floor opening when entering/exiting a building and when the chase camera angle changes.

This thread hit the project rule: 3 visual-gated fixes failed for this specific artifact. Stop here. Do not ship a fourth speculative renderer change. The next step must be source-provenance instrumentation or an architecture comparison.

The client was stopped after the failed visual gate to avoid build/file locks.

Last Good Context

The broader A8 indoor renderer is much improved and shippable aside from this artifact:

  • Theory A front-face alignment plus per-batch CullMode fixed major missing/inside-out geometry.
  • The InstanceData stride fix explained the texture explosion/distortion root cause.
  • Building shell scoping, portal bounds, streaming/promotion fixes, and FPS fixes remain important.
  • The current artifact is narrow: a green terrain-like flap at/near cellar entrance transitions.

Failed Fixes In This Micro-Loop

1. Camera branch gate relaxation

Hypothesis: strict PointInCell camera-inside-building gating was causing the renderer to switch branches while the chase camera crossed walls/portals.

Change tried: allow the inside-out branch to continue based on CameraCell.BuildingId, without the strict PointInCell check.

Result: visual gate failed. User still saw the artifact. Change was reverted back to strict PointInCell.

Evidence ruled out: the artifact is not solely caused by the render branch dropping out when the camera is slightly outside the cell.

2. Portal stencil pos.w clamp

Hypothesis: near-zero clip W for portal stencil triangles was exploding the screen-space mask as the camera crossed a portal plane.

Change tried: restored WorldBuilder-style clamp in src/AcDream.App/Rendering/Shaders/portal_stencil.vert:

vec4 pos = uViewProjection * vec4(aPosition, 1.0);
if (abs(pos.w) < 0.001)
    pos.w = pos.w < 0.0 ? -0.001 : 0.001;
gl_Position = pos;

Result: visual gate failed. User still saw the artifact.

Interpretation: this may still be WB parity and may be worth keeping, but it is not the root cause of the cellar flap.

3. Visible-cell portal mask

Hypothesis: RenderInsideOutAcdream was punching outdoor terrain through all exit portals in the camera building, not just exits reached by the current portal traversal. A window/door portal could project over the cellar opening and let terrain draw through it.

Changes tried:

  • Added CellVisibility.TryGetCell(uint, out LoadedCell?).
  • Added IndoorCellStencilPipeline.DrawUploadedPortalMesh(...) to draw the already-uploaded visible-cell portal mesh.
  • Changed RenderInsideOutAcdream Step 1/2 to upload portal triangles from visibleCellIds, not camBuildings.
  • Wrapped Step 4 terrain/outdoor scenery draw inside if (didInsideStencil).
  • Added pure math test BuildTriangles_OnlyIncludesProvidedVisibleCells.

Verification before launch:

dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --no-restore
dotnet test tests\AcDream.App.Tests\AcDream.App.Tests.csproj -c Debug --filter FullyQualifiedName~IndoorCellStencilPipelineTests --no-restore

Both passed after shutting down build servers. The first parallel test attempt only failed because VBCSCompiler locked AcDream.App.dll while a parallel build was running.

Visual result: failed. User still saw the flap.

Evidence ruled out: the artifact is not explained only by building-wide vs visible-cell exit portal masking.

Current Uncommitted Changes From This Micro-Loop

These are not visually validated as a fix:

  • src/AcDream.App/Rendering/Shaders/portal_stencil.vert
    • WB-style pos.w clamp.
  • src/AcDream.App/Rendering/CellVisibility.cs
    • TryGetCell.
  • src/AcDream.App/Rendering/IndoorCellStencilPipeline.cs
    • DrawUploadedPortalMesh.
  • src/AcDream.App/Rendering/GameWindow.cs
    • visible-cell portal mask in RenderInsideOutAcdream.
    • Step 4 fail-closed when no portal mesh uploaded.
  • tests/AcDream.App.Tests/Rendering/IndoorCellStencilPipelineTests.cs
    • visible-cell-only triangle generation test.

Do not commit these as "the fix" unless a later source-provenance check proves they are correct and harmless. If the next architecture route goes elsewhere, revert or split them honestly.

What We Know About The Cellar Entrance

Prior inspection found:

  • The cellar transition around Holtburg cottage cells 0xA9B40143 -> 0x0146 -> 0x0147 is indoor-to-indoor.
  • The relevant transition portals are not literal OtherCellId == 0xFFFF outside exits.
  • The portal cap polygons inspected there are NoPos.
  • That made "cell mesh uploads the outdoor floor polygon" unlikely, but not proven impossible for the visible green patch.

What The Failed Fixes Suggest

The green pixels may not be caused by the portal mask at all. We need to prove which render pass writes them before touching behavior again.

Candidate explanations now:

  1. The green patch is not Step 4 terrain. It could be an EnvCell surface with the wrong texture/material, stale texture binding, or an indoor static object surface resolving to a grass texture.

  2. The patch is Step 4 terrain, but the stencil/depth mask source is not the exit portal list. It may be a stale stencil/depth state leak, a Step 3/4 depth ordering issue, or a missing retail occlusion/scissor lifecycle detail.

  3. The cellar opening has a missing or late-loaded indoor object. If a hatch/stair/floor object should cover or occlude that area and is missing for one or more frames, outdoor/terrain pixels behind it become visible.

  4. Camera clipping is exposing an intentionally hidden surface. Retail camera collision is incomplete in acdream. If the camera clips through the wall/floor volume, the renderer may be showing a view retail never permits. This does not explain all cases by itself, because the user also sees it during transitions, but it must be separated from a renderer bug.

Next Step - Evidence Only

Add a provenance diagnostic, not a fix. One visual launch should answer what writes the green flap.

Recommended diagnostic:

  • Add an env-gated pass tint or disable switch for only the inside-out Step 4 terrain draw.
  • Add a separate tint/disable for Step 4 OutdoorScenery.
  • Add a tint/disable for Step 3 EnvCell opaque.
  • Keep default behavior unchanged.

Example env names:

ACDREAM_A8_DIAG_DISABLE_INSIDE_STEP4_TERRAIN=1
ACDREAM_A8_DIAG_DISABLE_INSIDE_STEP4_OUTDOOR=1
ACDREAM_A8_DIAG_DISABLE_INSIDE_ENVCELL_OPAQUE=1

Run only one diagnostic at a time:

  • If disabling Step 4 terrain removes the green flap, the issue is still terrain leaking through the indoor view mask.
  • If disabling Step 4 terrain does not remove it, stop looking at portal exits and inspect EnvCell/static texture/material assignment for the specific surface.
  • If disabling EnvCell opaque removes it, dump the exact CellStruct polygon/material under the camera/screenshot area.

Do not ship the diagnostic switches. Strip them after the source is known.

Launch/Test Notes

Last clean visual launch:

$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call"
$env:ACDREAM_LIVE = "1"
$env:ACDREAM_TEST_HOST = "127.0.0.1"
$env:ACDREAM_TEST_PORT = "9000"
$env:ACDREAM_TEST_USER = "testaccount"
$env:ACDREAM_TEST_PASS = "testpassword"
$env:ACDREAM_A8_INDOOR_BRANCH = "1"
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug

The log files from the last launch were empty because this was a clean no-probe run:

  • a8-visible-cell-portalmask-cellar-flap-20260528-161702.out.log
  • a8-visible-cell-portalmask-cellar-flap-20260528-161702.err.log

Stop Rule

This artifact has now consumed three visual failures in this micro-loop. Next session should not make another fix until it has hard evidence identifying the pass/polygon/source texture responsible for the green pixels.