feat(render) Campaign FW3.2b-1: the walk frame driver
WalkFrameDriver executes one full static-content frame from the walk's turns so GPU command-buffer order equals retail's walk order. One rule does the interleaving: the accumulated OrderedDrawStream flushes through SubmitOrderedStream immediately before EVERY non-stream draw (sky, terrain slice, cell shell, punch fan, alpha barrier). Turn script, all decomp-cited and two of them corrected in review: - Interior flood: per cell IN FLOOD ORDER, shell first then contents (PView::DrawCells @0x005a4840: DrawEnvCell @0x005a4abe precedes DrawObjCellForDummies @0x005a4b0d). - Landscape: sky once, terrain per active slice, then blocks far-to-near; per cell the building turn precedes the cell's outdoor statics (DrawSortCell @0x0059f140). - Building (DrawBuilding @0x0059f2a0): the BLD probe event stays at entry, but the ENTIRE body - alpha barrier, portal passes, shell - sits inside retail's gfxobj[deg_level]!=0 gate @0x0059f2d3, and the order is FlushAlphaList @0x0059f30b -> the two-pass punch/look-in walk -> THEN the shell draw @0x0059f345. The driver review caught both the missing gate and a shell-before-punch inversion; fixed with the addresses cited. Walk seam: three additive default-implemented IWalkEventSink hooks (OnLandscapeCellTurn / OnBuildingTurn / OnBuildingShellTurn / OnPunchGeometry) - every existing sink and all FW1 conformance fixtures unchanged. WalkLandBlock gains LandblockId for the cell-id encoding. Leaf draws go through IWalkFrameLeafRenderer so FW3.2b-2 wires the real renderers and the referee suite runs on fakes + RecordingGpuDevice. Flagged for FW3.2b-2/FW4 adjudication (documented in code): FlushFartherThan(building distance) vs retail flush-all FlushAlphaList(0f); terrain-before-statics within the landscape turn. Suites: full Release build 0 warnings; Walk lane 200/1 skip; InstalledDat Walk conformance 40/1 untouched; hermetic 6,752/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
81c6531727
commit
03f63686cc
6 changed files with 1303 additions and 7 deletions
|
|
@ -51,8 +51,78 @@ public readonly record struct WalkEvent(
|
|||
/// <summary>
|
||||
/// The sink a walk emits into. FW1 conformance tests collect events; FW2's
|
||||
/// 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
|
||||
/// 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
|
||||
/// <see cref="WalkBuilding"/> reference a driver needs to look up shell
|
||||
/// content, degrade state, or world transform; and punches are not part of
|
||||
/// the FW1 conformance vocabulary at all — the oracle traces do not log
|
||||
/// them). Every FW1/FW2 sink that only implements <see cref="Emit"/>
|
||||
/// continues to compile and behave identically — these are C# default
|
||||
/// interface members, never called by any pre-FW3.2b-1 code path.</para>
|
||||
/// </summary>
|
||||
public interface IWalkEventSink
|
||||
{
|
||||
void Emit(in WalkEvent walkEvent);
|
||||
|
||||
/// <summary>
|
||||
/// Fires once per visited landscape cell, AFTER that cell's building
|
||||
/// turn (if any) — <c>RenderDeviceD3D::DrawSortCell</c> @0x0059f140
|
||||
/// calls <c>DrawBuilding(building)</c> first, then
|
||||
/// <c>DrawObjCell(cell)</c> UNCONDITIONALLY (decomp-confirmed
|
||||
/// 2026-08-30). <paramref name="cellId"/> follows retail's outdoor cell
|
||||
/// encoding — <c>(landblockId & 0xFFFF0000) | (cellIndex + 1)</c> —
|
||||
/// the same convention <see cref="WalkLandscapeAssembler.BuildSlot"/>
|
||||
/// and <see cref="WalkProductionFrameContext.SetViewer"/> +
|
||||
/// <see cref="WalkLandscapeAssembler.SetViewer"/> already use. Default
|
||||
/// no-op.
|
||||
/// </summary>
|
||||
void OnLandscapeCellTurn(uint cellId) { }
|
||||
|
||||
/// <summary>
|
||||
/// Fires at <see cref="RetailFrameWalk.DrawBuilding"/> once retail's own
|
||||
/// gate has passed — <c>RenderDeviceD3D::DrawBuilding</c> @0x0059f2a0
|
||||
/// wraps its ENTIRE body (the alpha barrier, the portal pass, the shell
|
||||
/// draw) in <c>if (part->gfxobj[part->deg_level] != 0)</c>
|
||||
/// @0x0059f2d3 — a degraded-out slot draws NOTHING beyond the
|
||||
/// unconditional <see cref="WalkEventKind.Building"/> <see cref="Emit"/>
|
||||
/// call. <see cref="WalkBuilding.HasGeometry"/> and a non-null
|
||||
/// <see cref="WalkBuilding.SelectDrawingBsp"/> result together model that
|
||||
/// one gate, so this hook fires only after both have passed. This is the
|
||||
/// ALPHA BARRIER turn (<c>D3DPolyRender::FlushAlphaList(0f)</c>
|
||||
/// @0x0059f30b) — retail's own order runs it BEFORE the portal pass
|
||||
/// (<c>CPhysicsPart::Draw(parts, 1)</c>), which in turn runs BEFORE the
|
||||
/// shell draw (<c>CPhysicsPart::Draw(parts, 0)</c> — see
|
||||
/// <see cref="OnBuildingShellTurn"/>, fired separately, after the portal
|
||||
/// pass completes). Default no-op.
|
||||
/// </summary>
|
||||
void OnBuildingTurn(WalkBuilding building) { }
|
||||
|
||||
/// <summary>
|
||||
/// Fires at the END of <see cref="RetailFrameWalk.DrawBuilding"/>, after
|
||||
/// its two-pass portal walk (punches + look-ins, across every active
|
||||
/// view) has fully completed — <c>CPhysicsPart::Draw(parts, 0)</c>
|
||||
/// @0x0059f331, retail's plain-mesh draw of the building's own shell,
|
||||
/// which runs strictly AFTER <c>CPhysicsPart::Draw(parts, 1)</c> (the
|
||||
/// portal flavor) per the decomp sequence at @0x0059f30b–0x0059f345. Only
|
||||
/// fires when <see cref="OnBuildingTurn"/> also fired (same gate; see its
|
||||
/// doc comment) — a degraded-out slot reaches neither. Default no-op.
|
||||
/// </summary>
|
||||
void OnBuildingShellTurn(WalkBuilding building) { }
|
||||
|
||||
/// <summary>
|
||||
/// Fires when the two-pass portal machinery
|
||||
/// (<see cref="WalkBuildingPortals"/>) would draw a far-Z punch fan
|
||||
/// (<c>DrawPortalPolyInternal</c> @0x0059bc90, pass 1) — carries the
|
||||
/// owning building and the polygon in BUILDING-LOCAL space (as stored on
|
||||
/// the emitted <see cref="WalkPortalRef"/>) so a driver can resolve the
|
||||
/// world transform itself. The FW1 oracle traces never log punches
|
||||
/// (<see cref="RetailFrameWalk"/>'s internal <c>PortalPassSink.OnPunch</c>
|
||||
/// stayed a no-op through FW1/FW2 for exactly that reason). Default
|
||||
/// no-op.
|
||||
/// </summary>
|
||||
void OnPunchGeometry(WalkBuilding building, WalkPolygon polygon) { }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue