From 63d14c3d6b25716a520bdd13823f01241fe49a7a Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 11 Jun 2026 18:08:46 +0200 Subject: [PATCH] #125 filed + WB-DIAG median crash fixed - the #119 stairs mechanism is a sticky GL upload failure The in-tower ACDREAM_WB_DIAG launch (the saved character spawns inside the #119 tower - a free deterministic repro lever) produced the mechanism evidence in one run (tower-wbdiag3.log): 1. [wb-error] upload of 0x0100321D died on a GL InvalidOperation in ManagedGLTextureArray..ctor (new TextureAtlasManager) - caught, returns null, and the drop is STICKY: _preparationTasks.TryRemove runs BEFORE the upload, so a failed upload is never re-prepared. Permanently invisible mesh, one log line. This failure class is the likely #119 missing-stairs mechanism (dat + extraction + registration + dispatcher all exonerated by read/test this session). 2. The SAME GL error then fired UNCAUGHT in Tick -> GenerateMipmaps -> ProcessDirtyUpdatesInternal and killed the process. Both render- thread - not thread affinity. Filed as #125 (HIGH) with the open question of GL error attribution (a stale error queued by an earlier unchecked call lands on WB's diligent glGetError checks). Also fixed here: WbDrawDispatcher.MedianMicros crashed with IndexOutOfRange on the first diag flush when exactly 1 sample was recorded (copy[copy.Length - nz/2] with nz==1) - the same off-by-one GameWindow's TerrainDiagMedianMicros twin fixed; same fix applied. ACDREAM_WB_DIAG=1 is usable again. Suites: App 236, Core 1419+2skip, UI 420, Net 294. Co-Authored-By: Claude Fable 5 --- docs/ISSUES.md | 44 +++++++++++++++++++ .../Rendering/Wb/WbDrawDispatcher.cs | 7 ++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/docs/ISSUES.md b/docs/ISSUES.md index ae486384..ad437873 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -4225,6 +4225,50 @@ selector. --- +## #125 — GL InvalidOperation during staged texture upload: failed uploads are STICKY (never retried) + uncaught crash in GenerateMipmaps + +**Status:** OPEN — HIGH (the likely #119 missing-stairs mechanism + a process crash) +**Filed:** 2026-06-11 (in-tower WB_DIAG launch, `tower-wbdiag3.log` — preserved in the worktree root) +**Component:** render — WB staged texture pipeline (ObjectMeshManager / ManagedGLTextureArray) + +**Evidence (one launch, character spawned inside the #119 tower):** +1. `[wb-error] Error uploading mesh data for 0x0100321D` — GL + `InvalidOperation` thrown in `ManagedGLTextureArray..ctor:70` + (TextureAtlasManager ctor → CreateTextureArrayInternal), caught by + `UploadMeshData`'s try/catch → returns null. **The drop is STICKY:** + `_preparationTasks.TryRemove` runs BEFORE the upload + (ObjectMeshManager.cs:685), so a failed upload is never re-prepared — + that mesh is permanently invisible for the session (only a one-line + [wb-error] marks it). +2. Same session, `Tick()` → `GenerateMipmaps()` → + `ManagedGLTextureArray.ProcessDirtyUpdatesInternal:283` threw the SAME + GL InvalidOperation **uncaught** → process death (exit 82). Both on the + render thread (Tick/OnRender) — not a thread-affinity bug. + +**Why this matters for #119:** the missing tower stairs are per-cell +Setup statics whose parts are individually uploaded; an intermittent GL +error burst during atlas creation/flush kills whichever uploads are in +flight — "partially invisible", varying with load order, hitting the +late-loading AAB3 interior statics consistently. The dat + extraction + +registration + dispatcher are all exonerated by read/test +(Issue119TowerDumpTests; the [up-null] pair was a separate, legitimate +no-draw class). + +**Open questions (next session):** (a) what makes the GL context error +out — a stale error queued by an earlier unchecked call being +mis-attributed to WB's diligent glGetError checks (classic GL +attribution trap; suspects: the #117 stencil punch state, the new #118 / +#121 passes, or a pre-existing per-frame state leak), vs. a genuine +invalid texture-array creation state; (b) whether upload failures should +re-enqueue instead of dropping (retail has no such failure mode — the +sticky drop is OUR invention and must go regardless); (c) the uncaught +GenerateMipmaps path needs the same handling either way. +**Repro lever:** the test character's save spawns INSIDE the tower — +every launch loads the exact content; `ACDREAM_WB_DIAG=1` prints the +meshMissing counters. + +--- + # Recently closed ## #113 — Phantom staircase: REOPENED 2026-06-11, folded into the HOLISTIC BUILDING-RENDER PORT diff --git a/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs b/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs index 2f5a16f9..f7214e9e 100644 --- a/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs +++ b/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs @@ -1583,7 +1583,12 @@ public sealed unsafe class WbDrawDispatcher : IDisposable int nz = 0; foreach (var v in copy) if (v > 0) nz++; if (nz == 0) return 0; - return copy[copy.Length - nz / 2]; + // Sorted ascending: zero-padding front, samples at the back. (nz - 1) / 2 + // from the end keeps the offset >= 0 for all nz >= 1 — the original + // nz / 2 form indexed copy[copy.Length] (crash) on the first diag flush + // when exactly 1 sample was recorded. Same fix as GameWindow's + // TerrainDiagMedianMicros twin. + return copy[copy.Length - 1 - (nz - 1) / 2]; } private static long Percentile95Micros(long[] samples)