From 7433b704fbc2b9041d9dd8ce5546a4232b13de74 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 9 Jun 2026 21:28:32 +0200 Subject: [PATCH] diag(render): tripwires on every silent dat-miss path (white-walls attribution, #105) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intermittent white-cottage-walls failure has NEVER produced a log line: every dat-read failure on the walls-relevant paths exits silently, and the failed result is cached for the session (mesh batches build once). Today it reproduced on a probe-free launch with a 35-line, zero-error log — so the prior heavy-probes-starve-the-dat-reader framing is not the whole story. Tripwires (print ONLY on anomaly; zero cost healthy; keep until #105 closes): - [dat-miss] DatDatabaseWrapper.TryGet — a miss for an id whose BTree entry EXISTS (re-probed under the same lock); legit not-found fallbacks stay quiet. - [tex-miss] TextureCache.DecodeFromDats x3 — render-thread decode fell back to magenta (Surface / SurfaceTexture / RenderSurface miss). - [cell-miss] GameWindow interior hydration x2 — EnvCell or Environment read returned null, so a cell''s WALLS are silently never registered while its statics still draw (the exact observed geometry signature). Color discriminates the layer on the next occurrence: magenta = TextureCache; see-through + [tex-skip]/[dat-miss] = mesh build; see-through + [cell-miss] = hydration; broken with NO tripwire output = GL-side upload/residency. Co-Authored-By: Claude Fable 5 --- src/AcDream.App/Rendering/GameWindow.cs | 17 ++++++++++++++++- src/AcDream.App/Rendering/TextureCache.cs | 12 ++++++++++++ .../Rendering/Wb/DatCollectionAdapter.cs | 11 +++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 4f1cc79d..56ed5095 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -5471,13 +5471,28 @@ public sealed class GameWindow : IDisposable { uint envCellId = firstCellId + offset; var envCell = _dats.Get(envCellId); - if (envCell is null) continue; + if (envCell is null) + { + // TEMP diagnostic (dat-race investigation 2026-06-09, strip with fix): + // every id in [0x0100, 0x0100+NumCells) is derived from LandBlockInfo and + // MUST exist in the cell dat — a null here is always a read anomaly. + Console.WriteLine($"[cell-miss] EnvCell 0x{envCellId:X8} null during interior hydration (NumCells={lbInfo.NumCells})"); + continue; + } // Phase 7.1: build and register room geometry for this EnvCell. DatReaderWriter.Types.CellStruct? cellStruct = null; if (envCell.EnvironmentId != 0) { var environment = _dats.Get(0x0D000000u | envCell.EnvironmentId); + if (environment is null) + { + // TEMP diagnostic (dat-race investigation 2026-06-09, strip with fix): + // a null Environment means this cell's WALLS are silently never + // registered while its static objects still draw — the exact + // white-walls geometry signature. + Console.WriteLine($"[cell-miss] Environment 0x{0x0D000000u | envCell.EnvironmentId:X8} null for EnvCell 0x{envCellId:X8} -> walls not registered"); + } if (environment is not null && environment.Cells.TryGetValue(envCell.CellStructure, out cellStruct)) { diff --git a/src/AcDream.App/Rendering/TextureCache.cs b/src/AcDream.App/Rendering/TextureCache.cs index 5aea0758..056ec01f 100644 --- a/src/AcDream.App/Rendering/TextureCache.cs +++ b/src/AcDream.App/Rendering/TextureCache.cs @@ -385,7 +385,11 @@ public sealed unsafe class TextureCache : Wb.ITextureCachePerInstance, IDisposab { var surface = _dats.Get(surfaceId); if (surface is null) + { + // TEMP diagnostic (dat-race investigation 2026-06-09, strip with fix) + Console.WriteLine($"[tex-miss] Surface 0x{surfaceId:X8} -> magenta (thread={System.Environment.CurrentManagedThreadId})"); return DecodedTexture.Magenta; + } // Base1Solid surfaces (and any with OrigTextureId==0) carry a ColorValue // instead of a texture chain. Overrides are irrelevant here — there's @@ -401,12 +405,20 @@ public sealed unsafe class TextureCache : Wb.ITextureCachePerInstance, IDisposab uint surfaceTextureId = origTextureOverride ?? (uint)surface.OrigTextureId; var surfaceTexture = _dats.Get(surfaceTextureId); if (surfaceTexture is null || surfaceTexture.Textures.Count == 0) + { + // TEMP diagnostic (dat-race investigation 2026-06-09, strip with fix) + Console.WriteLine($"[tex-miss] SurfaceTexture 0x{surfaceTextureId:X8} (surface 0x{surfaceId:X8}) -> magenta (thread={System.Environment.CurrentManagedThreadId})"); return DecodedTexture.Magenta; + } uint renderSurfaceId = (uint)surfaceTexture.Textures[0]; if (!_dats.Portal.TryGet(renderSurfaceId, out var rs) && !_dats.HighRes.TryGet(renderSurfaceId, out rs)) + { + // TEMP diagnostic (dat-race investigation 2026-06-09, strip with fix) + Console.WriteLine($"[tex-miss] RenderSurface 0x{renderSurfaceId:X8} (surface 0x{surfaceId:X8}) -> magenta (thread={System.Environment.CurrentManagedThreadId})"); return DecodedTexture.Magenta; + } // Start with the texture's default palette, then apply overlays. // ACViewer's Render/TextureCache.IndexToColor does the same and never diff --git a/src/AcDream.App/Rendering/Wb/DatCollectionAdapter.cs b/src/AcDream.App/Rendering/Wb/DatCollectionAdapter.cs index 9f49a157..60ed18c7 100644 --- a/src/AcDream.App/Rendering/Wb/DatCollectionAdapter.cs +++ b/src/AcDream.App/Rendering/Wb/DatCollectionAdapter.cs @@ -149,6 +149,17 @@ internal sealed class DatDatabaseWrapper : IDatDatabase _cache.TryAdd((typeof(T), fileId), value); return true; } + + // TEMP diagnostic (dat-race investigation 2026-06-09, strip with fix): + // a miss for an id whose BTree entry EXISTS is always an anomaly — + // either Unpack returned false or the lookup flickered transiently. + // Legit not-found probes (e.g. Portal→HighRes fallback) stay silent. + if (_db.Tree.TryGetFile(fileId, out _)) + { + Console.WriteLine( + $"[dat-miss] {typeof(T).Name} 0x{fileId:X8} entry EXISTS but TryGet failed " + + $"(thread={Environment.CurrentManagedThreadId})"); + } } return false;