From 096b81657b20c81860ed2485f426adcca54f042b Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 10 Jun 2026 12:17:09 +0200 Subject: [PATCH] docs: #105 x #110 CLOSED - staged-texture-flush drop close-out (evidence chain + lesson); handoff marked historical Co-Authored-By: Claude Fable 5 --- ...05-110-CLOSED-staged-texture-flush-drop.md | 88 +++++++++++++++++++ ...05-110-white-textures-nearplane-handoff.md | 6 ++ 2 files changed, 94 insertions(+) create mode 100644 docs/research/2026-06-10-105-110-CLOSED-staged-texture-flush-drop.md diff --git a/docs/research/2026-06-10-105-110-CLOSED-staged-texture-flush-drop.md b/docs/research/2026-06-10-105-110-CLOSED-staged-texture-flush-drop.md new file mode 100644 index 00000000..0fe9d3b9 --- /dev/null +++ b/docs/research/2026-06-10-105-110-CLOSED-staged-texture-flush-drop.md @@ -0,0 +1,88 @@ +# CLOSED — #105 white indoor walls × #110 near-plane correlation: one root cause + +**Date:** 2026-06-10 (evening). **Branch:** `claude/thirsty-goldberg-51bb9b`. +**Commits:** `c787201` (#105 fix + apparatus) · `d4b5c71` (#110 close + znear=0.1 re-land). +**Supersedes the plan in** `2026-06-10-105-110-white-textures-nearplane-handoff.md` +(its §3 anatomy and §4 "only credible link" both turned out exactly right — the staged +plan was short-circuited by a static find before any stochastic repro was needed). + +## The root cause (#105) + +`TextureAtlasManager.AddTexture` only **stages** texture content: pixel bytes go into a +per-array PBO (`ManagedGLTextureArray.UpdateLayerInternal`) plus a `_pendingUpdates` +list. The actual `TexSubImage3D` copies into the texture array layers + mipmap +regeneration happen in `ProcessDirtyUpdates()` — and WB drives that **once per frame** +from its render loop: + +> `references/WorldBuilder/Chorizite.OpenGLSDLBackend/GameScene.cs:975` +> `_meshManager?.GenerateMipmaps();` (immediately before the first opaque pass) + +`GameScene.cs` is the host-loop file the N.4/O-T4 extraction replaced with our +`GameWindow` — so the per-frame driver was silently dropped (`git log -S` shows +`GenerateMipmaps` arrived with `d16d8cd` O-T4 and never gained a caller). The only +remaining flush was the incidental one inside `UpdateLayerInternal` when a PBO must +**grow** (it flushes pending updates before orphaning the buffer). Consequence: every +layer staged after an array's *last* PBO growth kept **undefined `TexStorage3D` +content** behind a perfectly valid, resident bindless sampler handle. + +That one sentence explains every #105 observation: + +| Observation | Explanation | +|---|---| +| Dat tripwires silent on every bad run | dat → decode → stage all delivered; the loss was the missing flush after staging | +| White/garbage surfaces, `zh==0` | handle valid + resident; *content* undefined | +| Intermittent, per-run lottery | background decode-completion order shuffles which textures land in the post-last-growth tail | +| Persists the whole run at standstill | nothing grows a PBO at standstill ⇒ nothing flushes | +| Indoor walls only | only `ObjectRenderBatch.BindlessTextureHandle` consumers are affected = `EnvCellRenderer` cell shells; entities resolve per-frame via `TextureCache` (immediate `TexImage2D`), terrain via `TerrainAtlas` (immediate `GenerateMipmap`) | +| Struck on `znear=1.0` builds too (2026-06-09 clean launch) | the tail exists on every run; visibility of it is luck | + +## The fix + +`WbMeshAdapter.Tick()` now calls `_meshManager.GenerateMipmaps()` after the +staged-upload drain. `Tick()` runs at the top of `GameWindow` `OnRender`, before all +draw passes — the exact WB-equivalent position. One call; no retry loops, no +back-patching machinery needed. + +## Evidence chain (apparatus: `ACDREAM_PROBE_TEXFLUSH=1`, kept env-gated) + +- **Pre-fix** (`texflush-prefix.log`): pending updates climb `0→48→…→142`, dip only at + PBO-growth crossings (`86→76`, `87→68` — the incidental flush, live), then **park at + 126 across 34/34 atlas arrays forever** (19 heartbeats at standstill). Deterministic, + first run — the broken contract did not need a stochastic white-wall repro. +- **Post-fix** (`texflush-postfix.log`): `after=0` on every line — staged updates drain + the same frame they are staged. +- **0.1-arm verification** (`nearplane-reland-1.log`, `nearplane-reland-2.log` — the arm + that struck 2-of-3 on 2026-06-10): `after=0` on all 45/39 tex-flush lines; 68,291 + + 56,097 `[shell]` lines with **zero** `zh>0` batches; all four dat tripwires silent; + zero `[wb-error]`. + +## #110 resolution + +The near plane was **mechanism-innocent** — precisely the handoff's only-credible-link: +`znear=0.1` makes close-up geometry newly visible → more prepare/upload pressure indoors +→ a larger never-flushed tail → higher #105 strike probability. With the flush restored, +retail `Render::znear = 0.1` (decomp `:342173`, initializer `:1101867`) is re-landed on +all four cameras (`d4b5c71`), closing the §4 corner see-through (0.1 < the 0.3 m +camera-collision sphere, so a pressed wall no longer near-clips away). + +**Pending user re-gate:** (a) corner press — the wall must stay solid at the camera; +(b) a distance scan for z-shimmer (none expected — retail ships 0.1 with D24); +(c) general indoor texture watch over the next several launches. + +## Durable lesson + +Memory: `feedback_extraction_perframe_drivers.md` — when extracting a library from a +host app, the host loop's per-frame calls *into* the library are invisible contracts; +grep the host's frame loop and re-wire every one. Staged/deferred APIs are the worst +case: everything looks wired and works most of the time via incidental side-effect +flushes. + +## Status of the old #105 exonerations (all stand) + +Concurrent dat reads SAFE (hammer-verified); teardown AVs were dispose-during-read +(fixed `8fadf77`); probes don't cause white walls; membership/flood healthy. The four +dat-side tripwires (`7433b70`) stay as permanent anomaly logging. + +**Next per the priority order:** #107 indoor-login spawn wedge +(`ACDREAM_CAPTURE_RESOLVE` apparatus ready) → #108 cellar grass-sweep + #109 far-door +oscillation → #99/A6.P4 per-cell shadow architecture. diff --git a/docs/research/2026-06-10-105-110-white-textures-nearplane-handoff.md b/docs/research/2026-06-10-105-110-white-textures-nearplane-handoff.md index 2f146a6c..23c60a25 100644 --- a/docs/research/2026-06-10-105-110-white-textures-nearplane-handoff.md +++ b/docs/research/2026-06-10-105-110-white-textures-nearplane-handoff.md @@ -1,5 +1,11 @@ # HANDOFF — #105 intermittent missing indoor textures × #110 near-plane correlation +> **✅ CLOSED 2026-06-10 (same day).** Root cause: the per-frame staged-texture flush +> (WB `GameScene.cs:975` → `ObjectMeshManager.GenerateMipmaps()`) was dropped in the +> N.4/O-T4 extraction; fix `c787201`, znear=0.1 re-landed `d4b5c71`. §4's "only credible +> link" (upload pressure) was exactly right. **Read the close-out instead:** +> `2026-06-10-105-110-CLOSED-staged-texture-flush-drop.md`. This document is historical. + **Date:** 2026-06-10 (late). **Branch:** `claude/thirsty-goldberg-51bb9b`, HEAD `8bd3492`. **Status:** #105 struck twice today with the dat-side tripwires SILENT (= GL-side); the retail near-plane fix (`137b4f2`, 0.1 m) was bisect-implicated in those two runs and