fix(render): FW4 slice 3 - the outside-stage predicate reads the walk flood

The cathedral falls persisted through the slice-2 seal fix; the probe
already held the tell: the same two emitter entities (f4243/f4285) draw
phase=pre on good frames and phase=post on bad ones. They are
interior-parented, so DynamicDrawsInOutsideStage takes the indoor arm,
whose first gate is flood membership - and that still read the OLD
apparatus flood, which drops cells at the #456 seam band. On those
frames the falls fell to the post-clear last pass and splatted across
the cleared depth (terrain and water alike).

Both predicate call sites (the frame product's BuildOutsideDynamicRoutes
via BuildAndBorrow's input, and the renderer's own outside-stage
classification) now receive the walk's VisitedCells on walk frames -
root flood plus look-in floods, all of whose objects retail draws inside
LScape::draw, pre-clear. drawableCells keeps its other roles. Also adds
the probe-gated [walk-dyn] per-entity classification dump (parent cell,
outside/cone verdicts) so a surviving repro pins the failing gate
directly.

Hermetic 6,762/0, Walk lane 213/1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-30 19:28:57 +02:00
parent c40aecfc8c
commit 5f7ccdeab5
2 changed files with 40 additions and 13 deletions

View file

@ -465,6 +465,18 @@ public sealed class RetailPViewRenderer
_candidateObserver?.BeginPViewFrame();
try
{
// FW4 slice 3: the outside-stage predicate's flood-membership set
// is THE WALK'S visited cells on walk frames (root flood +
// look-in floods — retail draws all of their objects inside
// LScape::draw, pre-clear). The old apparatus's drawableCells
// misses cells at the #456 seam band, dropping interior-parented
// outdoor emitters (the cathedral falls weenies) to the
// post-clear last pass, where the cleared depth lets them splat
// across terrain and water. drawableCells keeps its other roles
// (prepare filter, frame result) unchanged.
HashSet<uint> outsideStageFlood =
walkActive ? walkDriver!.VisitedCells : drawableCells;
if (_sceneFrameProduct is not null)
{
frameView = _sceneFrameProduct.BuildAndBorrow(
@ -472,7 +484,7 @@ public sealed class RetailPViewRenderer
clipAssembly,
viewcone,
_lookInFrames,
drawableCells,
outsideStageFlood,
ctx.Cells,
ctx.AnimatedEntityIds,
ctx.RootCell.IsOutdoorNode);
@ -544,7 +556,11 @@ public sealed class RetailPViewRenderer
foreach (var e in partition.Dynamics)
{
EntitySphere(e, out var c, out float r);
if (DynamicDrawsInOutsideStage(e.ParentCellId, c, r, drawableCells, ctx.Cells))
// FW4 slice 3: same walk-flood membership set as the
// frame product's BuildOutsideDynamicRoutes — the two
// predicate call sites must agree or routed and drawn
// stages diverge.
if (DynamicDrawsInOutsideStage(e.ParentCellId, c, r, outsideStageFlood, ctx.Cells))
_outsideStageDynamics.Add(e);
}
}