fix(render) Campaign FW3.2b-1: interior draw order - landscape before cells

The oracle trace order DI|DC|LS is breakpoint-ENTRY order; retail's
actual DRAW order inside PView::DrawCells @0x005a4840 for an interior
root is LScape::draw FIRST (pc:432719, only when exit views survive),
then the depth clear (pc:432731-432732), the exit-portal seals
(pc:432785-432786), THEN the flood's own cells far-to-near. The
driver drew flood cells before the landscape - inverted.

RetailFrameWalk.DrawInside gains the additive
OnInteriorFloodDrawTurn(cells) hook firing after the conditional
landscape turn; the DC EVENT stays at its original point (conformance
untouched - 40/1 InstalledDat green). WalkFrameDriver records the
interior flood at the DC turn and draws it at the new turn:
flush -> ClearInteriorDepth -> flush -> DrawExitSeals -> per-cell
shell-then-contents. Building look-in floods still draw immediately at
their building turn (retail's reentrant DrawCells with no clear/seal).
Two new leaf members map to IWorldPassScope.ClearInteriorDepth and the
seal-fan machinery at FW3.2b-2. Reconciliation note recorded: the
driver clears unconditionally for interior roots while production
stages the clear on OutsideViewSlices>0 - observably equivalent at
ov=0, awaiting a firmer decomp read of the clear's gate.

Suites: full Release build 0 warnings; Walk lane 201/1 skip;
hermetic 6,753/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-30 14:34:45 +02:00
parent 03f63686cc
commit 035d7b0148
4 changed files with 247 additions and 20 deletions

View file

@ -53,7 +53,7 @@ public readonly record struct WalkEvent(
/// production sink will additionally receive the ordered draw stream.
///
/// <para>Campaign FW3.2b-1 additive seam: <see cref="RetailFrameWalk"/> also
/// calls the four richer, default-no-op members below at turns the
/// calls the five richer, default-no-op members below at turns the
/// vocabulary-only <see cref="WalkEvent"/> stream cannot express (a visited
/// landscape cell with no building emits no <see cref="WalkEvent"/> at all;
/// <see cref="WalkEventKind.Building"/> carries only a cell id, not the
@ -125,4 +125,29 @@ public interface IWalkEventSink
/// no-op.
/// </summary>
void OnPunchGeometry(WalkBuilding building, WalkPolygon polygon) { }
/// <summary>
/// Fires once per interior root, at the point <c>PView::DrawCells</c>
/// @0x005a4840 actually DRAWS the root flood's own cells — NOT where the
/// <see cref="WalkEventKind.DrawCells"/> <see cref="Emit"/> call for the
/// SAME flood fires (that one sits at breakpoint-ENTRY order, matching
/// the FW0 oracle traces; it only RECORDS the flood list). Retail's own
/// order inside <c>DrawCells</c> is: <c>LScape::draw</c> FIRST
/// (pc:432719, only when exit views survived — see
/// <see cref="RetailFrameWalk.DrawLandscape"/> and
/// <see cref="WalkEventKind.Landscape"/>), then a full depth clear
/// (pc:432731-432732), then the exit-portal seals (pc:432785-432786) —
/// BOTH unconditional for an interior root's
/// own flood, whether or not a landscape turn just ran — and ONLY THEN
/// the flood's cells far-to-near (shell then contents per cell, same
/// discipline as <see cref="OnLandscapeCellTurn"/>'s per-cell contents
/// and a building's look-in). This hook fires at that later point, so
/// this is where a driver should actually draw <paramref name="cells"/>.
/// Building look-in floods are UNAFFECTED — retail calls
/// <c>DrawCells</c> re-entrantly there with <c>ov==0</c> and no
/// landscape/clear/seal step, so their <see cref="WalkEventKind.DrawCells"/>
/// <see cref="Emit"/> call still fires at the actual draw point (a
/// driver may keep drawing those immediately, as before). Default no-op.
/// </summary>
void OnInteriorFloodDrawTurn(IReadOnlyList<uint> cells) { }
}