fix(render): Phase A8 — Step 5 gate-off-by-default + restore GL state

First user-driven visual gate (1,595 indoor frames, 109 other-buildings)
reported textures flickering / can barely move / client crashed
indoors. Root causes:

1. Step 5 (cross-building visibility) iterates EVERY loaded other-
   building per frame with NO frustum culling. At Holtburg that's 109
   other-buildings × 5 GL draws each = ~545 extra draws/frame on top
   of the 4 setup steps. Each Render() within Step 5c also re-uploads
   the instance SSBO via glBufferData(orphan) + glBufferSubData and
   a glMemoryBarrier. Combined with rapid 109-iteration back-to-back
   state churn, the driver hits TDR and crashes.

   GATE: Step 5 is now OFF BY DEFAULT. Set ACDREAM_A8_STEP5=1 to opt
   in once we add per-building frustum culling on otherBuildings.
   Cross-building visibility is a polish feature; M1.5 indoor walking
   doesn't require it.

2. WB's RenderInsideOut cleanup at line 234-238 exits with
   ColorMask(t,t,t,FALSE) — alpha-bit OFF — matching WB's editor
   pipeline expectations. acdream's subsequent rendering (particles,
   anything writing alpha) needs alpha-bit ON. The flicker symptom
   is consistent with subsequent passes mis-writing alpha.

   FIX: cleanup now restores ColorMask(t,t,t,t), DepthMask(true),
   DepthFunc.Less, Enable(CullFace) — all to acdream defaults so the
   outer render frame sees a clean slate. Step 5's loop also leaves
   DepthMask/CullFace in non-default states; defensive restore makes
   this safe whether Step 5 ran or not.

Build green. Tests unchanged.

Expectation for next relaunch: indoor frames hit only Steps 1+2+3+4
(camera-building stencil + cell render + stencil-gated outdoor scenery).
Cross-building visibility (Step 5) is intentionally inert. Flicker
should be resolved by the ColorMask alpha restore. Perf should be
closer to pre-A8 outdoor (one extra full-screen pass + a small
stencil mask).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-27 16:06:07 +02:00
parent 5d41876ba6
commit 9c5991061f

View file

@ -10727,7 +10727,16 @@ public sealed class GameWindow : IDisposable
// Step 5: per-other-building 3-bit stencil pipeline.
// WB VisibilityManager.cs:157-232
if (didInsideStencil && otherBuildings.Count > 0)
//
// FIX 2026-05-28 (post-first-visual-gate): Step 5 is GATED OFF BY DEFAULT.
// First visual gate showed perf collapse + texture flicker indoors because
// Step 5 iterates EVERY loaded other-building per frame (109 at Holtburg),
// each doing 5 GL draws (mark/end-query/punch/render-opaque/render-trans/reset)
// = ~545 extra draws/frame with no frustum culling. The driver hit TDR
// limits. Set ACDREAM_A8_STEP5=1 to re-enable for cross-building visibility
// testing once we add per-building frustum culling.
bool step5Enabled = string.Equals(Environment.GetEnvironmentVariable("ACDREAM_A8_STEP5"), "1", StringComparison.Ordinal);
if (step5Enabled && didInsideStencil && otherBuildings.Count > 0)
{
gl.Enable(EnableCap.StencilTest);
gl.ColorMask(false, false, false, false);
@ -10791,7 +10800,18 @@ public sealed class GameWindow : IDisposable
{
gl.Disable(EnableCap.StencilTest);
gl.StencilMask(0xFFu);
gl.ColorMask(true, true, true, false);
// FIX 2026-05-28: WB exits with ColorMask(t,t,t,FALSE) — alpha-bit
// off. That breaks subsequent acdream rendering (particles +
// anything that relies on alpha-to-coverage writing alpha). Restore
// full ColorMask before returning to the outer render frame.
//
// Step 5's iteration loop (when ACDREAM_A8_STEP5=1) leaves
// DepthMask=false / CullFace=disabled / ColorMask=(f,f,f,f) on
// its last iteration. Restore to acdream-default before returning.
gl.ColorMask(true, true, true, true);
gl.DepthMask(true);
gl.DepthFunc(DepthFunction.Less);
gl.Enable(EnableCap.CullFace);
}
}