fix(render): re-land near plane 0.1m (retail Render::znear) — #110 resolved, closes the §4 corner see-through; close #105/#110

The 137b4f2 payload, re-landed now that #110 is resolved: the missing-indoor-
textures correlation was the pre-existing #105 staged-texture-flush drop
(fixed in c787201), not a near-plane mechanism. znear=0.1 merely raised #105's
trigger probability — a closer near plane makes close-up geometry newly
visible, inflating per-frame prepare/upload pressure indoors and growing the
never-flushed tail. Exactly the handoff's only-credible-link hypothesis,
verified instead of assumed.

Retail: Render::SetFOVRad sets znear=0.1 flat (decomp :342173, initializer
:1101867). 0.1 < the 0.3m camera-collision sphere, so a wall the collided eye
presses against no longer falls inside the near plane — the §4 corner
see-through-wall closes.

Verification on the 0.1 arm (the arm that struck 2-of-3 on 2026-06-10):
nearplane-reland-1.log — [tex-flush] after=0 on all 45 lines, 68,291 [shell]
lines with zero zh>0 batches, all four dat tripwires silent, no [wb-error].
ISSUES.md: #105 + #110 moved to Recently closed with root cause + evidence.
Pending user re-gate: corner press (wall stays solid) + distance scan for
z-shimmer (none expected; retail ships 0.1 with D24).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-10 12:14:00 +02:00
parent c78720127a
commit d4b5c71e66
5 changed files with 66 additions and 82 deletions

View file

@ -44,48 +44,6 @@ Copy this block when adding a new issue:
---
## #105 — Intermittent silent dat-load failure: white/missing cottage walls until relaunch
**Status:** OPEN
**Severity:** HIGH
**Filed:** 2026-06-09
**Component:** render, dat, streaming
**Description:** Intermittently (twice user-confirmed: once under heavy probe logging
2026-06-08, once on a completely clean launch 2026-06-09), the Holtburg cottage wall surfaces
render as background/clear color while the cells' static objects (paintings, furniture,
windows) draw normally. Once broken, broken for the whole session — the failed result is
cached (mesh batches build once, hydration runs once). The failure has never produced a single
log line: every dat-read failure on the walls-relevant paths exits silently.
**Root cause / status:** LARGELY SUPERSEDED BY #106 (2026-06-09 evening): the live capture
pinned "whole interior missing / enter house and see outside" to the cross-landblock outdoor
membership freeze — most "broken house" reports were that. What REMAINS under #105 is the
narrower residual observed twice earlier: a single wall section missing (sky/clear color)
**while membership and viewer cell were demonstrably correct and INDOOR** (`viewerCell=0171`,
props drawn, collision present). All data/upload/registration layers are exonerated for it:
four rounds of tripwires (`[dat-miss]`/`[tex-miss]`/`[tex-skip]`/`[cell-miss]`,
`[geom-null]`/`[geom-misroute]`/`[up-null]`, `[finalize-replace]`/`[late-register]`) were
silent across every reproduction, the dat library is exonerated (`DatConcurrencyStressTests`,
~1.1M concurrent reads), and the capture analysis shows no never-flooding building and no
empty indoor floods. The teardown dispose-during-read AccessViolation was fixed nearby
(`8fadf77`). Known-benign noise: `[up-null]` for `0x010002B4`/`0x010008A8` (deterministic,
legitimately empty meshes). If the residual reproduces, the remaining suspects are the
draw-level clip path (the §4 edge-on clip family) — note `BuildFromExterior` has NO pv-trace
hook yet (add one for attribution). Keep all tripwires until this closes.
**Files:** `src/AcDream.App/Rendering/Wb/ObjectMeshManager.cs` (texture chains),
`src/AcDream.App/Rendering/Wb/DatCollectionAdapter.cs` (`DatDatabaseWrapper.TryGet`),
`src/AcDream.App/Rendering/TextureCache.cs` (`DecodeFromDats`),
`src/AcDream.App/Rendering/GameWindow.cs` (`BuildInteriorEntitiesForStreaming` hydration).
**Research:** `docs/research/2026-06-09-dat-reader-thread-safety-investigation.md`.
**Acceptance:** the next white-wall occurrence is attributed by a tripwire line (or by their
collective silence, pointing GL-side); the attributed root cause is fixed; walls survive 10+
launches including heavy-load ones. Tripwires can then be stripped (or kept as permanent
anomaly logging — decide at close).
---
## #104 — Scene VFX particles not clipped to the PView visible cell set
@ -3768,35 +3726,59 @@ stable now; this is a draw-order/depth oscillation localized to the door surface
---
## #110 — Near plane 0.1 m (retail znear) correlates with missing indoor textures; corner see-through blocked on it
**Status:** OPEN
**Severity:** HIGH (blocks the §4 corner see-through fix)
**Component:** render / camera projection (+ possibly texture upload / #105 interaction)
Retail runs `Render::znear = 0.1` (decomp :342173, initializer :1101867); ours is 1.0 m.
Because the camera-collision sphere holds the eye 0.3 m from walls, the 1.0 m near plane
clips away any wall the camera presses against — the §4 "camera clipping into the wall"
corner background (user-gated 2026-06-10, unchanged by the flood fix `dac8f6a`).
Landing 0.1 (`137b4f2`) fixed the geometry relationship but correlated with MISSING INDOOR
TEXTURES on two consecutive runs; the 1.0 bisect run immediately rendered clean, and the
change was reverted same-day. The #105 dat-miss tripwires were SILENT on the bad runs
(GL-side per the #105 protocol). No mechanism is known by which the near plane affects
texturing — candidates: (a) coincidence with the intermittent #105 (2/2-then-1/1 is a small
sample), (b) a depth-precision interaction (near 0.1 + far 5000 = 50k ratio) breaking a
depth-dependent pass indoors, (c) something in the visible-set/prepare path scaling with
the wider near frustum. Investigation: re-land 0.1 locally, reproduce, RenderDoc the
missing-texture frame; or flip-test 0.1↔1.0 over N runs to settle (a) statistically.
User question pending: did the missing textures render WHITE (upload path) or INVISIBLE
(visibility/depth)?
---
---
# Recently closed
## #105 — Intermittent white/missing indoor wall textures — [DONE 2026-06-10 · c787201]
**Status:** DONE (probe-verified both directions; visual gate pending user)
**Closed:** 2026-06-10
**Commit:** `c787201` (fix + the `ACDREAM_PROBE_TEXFLUSH` apparatus)
**Component:** render (GL texture upload)
**Root cause:** `TextureAtlasManager.AddTexture` only STAGES texture content (PBO write +
`ManagedGLTextureArray._pendingUpdates`); the actual `TexSubImage3D` copies + mipmap
regeneration happen in `ProcessDirtyUpdates`, which WB drives once per frame via
`ObjectMeshManager.GenerateMipmaps()` from its render loop (WB `GameScene.cs:975`).
GameScene is the file the N.4/O-T4 extraction replaced with `GameWindow`, so the per-frame
driver was silently dropped. Staged updates only reached the GPU as a side effect of PBO
growth; every layer staged after an array's LAST growth kept undefined `TexStorage3D`
content behind a valid resident bindless handle — white/garbage walls, `zh==0`, all dat
tripwires silent (the dat→decode→stage side had delivered correctly). Only
`ObjectRenderBatch.BindlessTextureHandle` consumers were affected (EnvCellRenderer cell
shells = indoor walls); entities resolve via `TextureCache` (immediate) and terrain via
`TerrainAtlas` (immediate) — which is why only indoor walls ever struck. Intermittency =
background decode-completion order shuffling which textures land in the never-flushed tail.
**Fix:** `WbMeshAdapter.Tick()` now calls `GenerateMipmaps()` after the staged-upload
drain (Tick runs before all draw passes — the WB-equivalent position).
**Evidence:** pre-fix `texflush-prefix.log`: pending updates climb 0→48→…→142 and park at
126 across 34/34 atlas arrays forever at standstill. Post-fix `texflush-postfix.log` +
`nearplane-reland-1.log`: `after=0` on every line. The earlier exonerations (dat reads
safe, membership healthy, "not the probes") all stand — this was the predicted
"between staging and the draw" GL-side loss.
**Tripwires:** the four dat-side tripwires stay (permanent anomaly logging);
`ACDREAM_PROBE_TEXFLUSH` stays env-gated (zero cost off).
## #110 — Near plane 0.1 m vs missing indoor textures — [DONE 2026-06-10 · c787201 + re-land]
**Status:** DONE (mechanism resolved; near plane exonerated and re-landed)
**Closed:** 2026-06-10
**Component:** render / camera projection
**Resolution:** the missing-texture correlation was the pre-existing #105
(staged-texture-flush drop, see above), NOT a near-plane mechanism. `znear=0.1` merely
raised #105's trigger probability exactly as the handoff's only-credible-link predicted:
a closer near plane makes close-up geometry newly visible → more prepare/upload pressure
indoors → a larger never-flushed tail. With #105 fixed, retail `Render::znear = 0.1`
(decomp :342173, initializer :1101867) is re-landed on all four cameras — closing the §4
corner see-through (the 0.3 m-collided eye no longer near-clips the pressed wall).
User re-gate: corner press (wall stays solid) + distance scan for z-shimmer (none
expected; retail ships 0.1 with D24).
## #106 — Outdoor membership freezes at landblock boundaries — [DONE 2026-06-09 · 7078264 + 23adc9c + 6dbbf95 + e6913ac]
**Status:** DONE (user-verified: collision + solid walls everywhere; probe-verified crossings)

View file

@ -59,9 +59,9 @@ public sealed class ChaseCamera : ICamera
public Matrix4x4 View =>
Matrix4x4.CreateLookAt(Position, _lookAt, Vector3.UnitZ);
// Near plane: retail is 0.1 m, parked pending #110 (see RetailChaseCamera.Projection).
// Near plane 0.1 m = retail Render::znear (see RetailChaseCamera.Projection).
public Matrix4x4 Projection =>
Matrix4x4.CreatePerspectiveFieldOfView(FovY, Aspect, 1f, 5000f);
Matrix4x4.CreatePerspectiveFieldOfView(FovY, Aspect, 0.1f, 5000f);
/// <summary>
/// Update the camera position to follow the player. <paramref name="isOnGround"/>

View file

@ -32,9 +32,9 @@ public sealed class FlyCamera : ICamera
}
}
// Near plane: retail is 0.1 m, parked pending #110 (see RetailChaseCamera.Projection).
// Near plane 0.1 m = retail Render::znear (see RetailChaseCamera.Projection).
public Matrix4x4 Projection
=> Matrix4x4.CreatePerspectiveFieldOfView(FovY, Aspect, 1f, 5000f);
=> Matrix4x4.CreatePerspectiveFieldOfView(FovY, Aspect, 0.1f, 5000f);
/// <summary>
/// Integrate position for one frame based on WASD + vertical keys.

View file

@ -23,7 +23,7 @@ public sealed class OrbitCamera : ICamera
}
}
// Near plane: retail is 0.1 m, parked pending #110 (see RetailChaseCamera.Projection).
// Near plane 0.1 m = retail Render::znear (see RetailChaseCamera.Projection).
public Matrix4x4 Projection
=> Matrix4x4.CreatePerspectiveFieldOfView(FovY, Aspect, 1f, 5000f);
=> Matrix4x4.CreatePerspectiveFieldOfView(FovY, Aspect, 0.1f, 5000f);
}

View file

@ -38,16 +38,18 @@ public sealed class RetailChaseCamera : ICamera
public float Aspect { get; set; } = 16f / 9f;
public float FovY { get; set; } = MathF.PI / 3f;
public Matrix4x4 View { get; private set; } = Matrix4x4.Identity;
// ⚠️ Near plane SHOULD be retail Render::znear = 0.1 m (decomp :342173/:1101867), and
// must eventually be smaller than the 0.3 m camera-collision sphere — at 1.0 m a wall
// the collided eye sits 0.3 m from falls INSIDE the near plane and is clipped away
// (the §4 corner see-through, user-gated 2026-06-10). The 0.1 change was landed
// (137b4f2) and REVERTED the same day: two consecutive runs lost indoor textures on
// 0.1 and recovered on the 1.0 bisect run. Mechanism not yet understood (a near-plane
// change shouldn't touch texturing; the intermittent #105 may have coincided). Issue
// #110 tracks the investigation — do not re-land 0.1 without it.
// Near plane = retail Render::znear = 0.1 m (decomp :342130/:342173/:1101867 —
// Render::SetFOVRad sets 0.1 flat; the legacy set_vdst variant is max(0.1, vdst·0.25)).
// MUST be smaller than the 0.3 m camera-collision sphere (PhysicsCameraCollisionProbe.
// ViewerSphereRadius): with a 1.0 m near, a wall the collided eye sits 0.3 m from
// falls INSIDE the near plane and is clipped away — pressing the camera into a corner
// let you see straight through the wall (§4 corner residual). History: 0.1 landed
// (137b4f2), was reverted (8bd3492) after correlating with missing indoor textures,
// and re-landed once #110 resolved: the textures were the pre-existing #105
// staged-texture-flush drop (WbMeshAdapter.Tick), and 0.1 merely raised its trigger
// probability by making more close-up geometry visible (more uploads in flight).
public Matrix4x4 Projection =>
Matrix4x4.CreatePerspectiveFieldOfView(FovY, Aspect, 1f, 5000f);
Matrix4x4.CreatePerspectiveFieldOfView(FovY, Aspect, 0.1f, 5000f);
// ── Public tunables (per-instance) ──────────────────────────────