Three-lens review of c7ab5b6d8 found the round-0 port of
DrawPortalPolyInternal's degenerate-input guard inverted retail's
quantifier: it rejected a polygon when ANY vertex sat on ANY of the
four +/-12 planes. The PDB-paired bytes (VA 0x59BCD6-0x59BD66: four
per-plane fld/fcomp 12.0/fnstsw/test ah,0x44/jnp sequences, each
accumulating a "every vertex on this plane" boolean, cleared by the
first vertex NOT on it) and the named decomp's own nested-if reading
of the post-loop branch (0x59bd42-0x59bd6c) agree: retail rejects only
when EVERY local vertex lies on the SAME one of the four planes — a
polygon degenerate onto a plane, not one that merely touches a plane
at one vertex.
F1 — the quantifier. WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard
now tracks four booleans (everyVertexOnPlusX/MinusX/PlusY/MinusY), each
starting true and cleared by the first vertex that fails its own plane
test; the guard rejects iff any of the four survives the whole vertex
loop. T2 (WalkVisibilityMathTests.cs) is rewritten to the five §7
cases: every vertex on x=+12 -> rejected; every vertex on y=-12 ->
rejected; one vertex on x=+12, rest inside -> KEPT; every vertex at
11.999 -> kept; vertices split across x=+12 and y=+12 (no common
plane) -> kept. WalkFrameDriverTests' punch-side pin
(OnPunchGeometry_RejectsOnlyWhenEveryVertexSharesOnePlane_ButPunchesAnyOtherShape)
carries the same correction — its old "one vertex on the plane"
polygon was wrongly rejected under round 0; it punches now, and a new
all-on-plane polygon is added as the actual rejected case. The doc
comment's "essentially never" sentence is deleted and replaced with a
fresh DAT-scan citation (see datScan below).
MUTATION (verified): restore the any-vertex form -> the one-vertex and
split-plane admit cases fail (Assert.False Expected:False Actual:True,
x2; the WalkFrameDriverTests punch pin's Assert.Equal Expected:2
Actual:1). Delete the guard (always return false) -> the all-on-plane
reject cases fail (Assert.True Expected:True Actual:False, x3 —
plusX-plane, minusY-plane, and the vacuous-empty-polygon pin; the
punch pin's Assert.Equal Expected:2 Actual:3).
datScan (DAT counts backing F1's doc comment): a throwaway xunit test
(deleted after this commit — not part of the diff) walked the full
0x0000-0xFFFF landblock-prefix space (LandBlockInfo/EnvCell carry no
usable DatCollection.GetAllIdsOfType range — both are landblock-
relative — matching the method CellStructSurfaceConstructionInstalledDatTests
already established), resolved each EnvCell's Environment/CellStruct,
and for every CellPortal's polygon tested each vertex's local x/y
against +/-12. Against the installed client_cell_1.dat: 3,405
landblocks with cells, 1,854,237 total portal polygons. 2,889 polygons
have at least one vertex on a +/-12 plane; 2,163 have EVERY vertex on
the SAME plane — all 2,163 are EXIT portals (OtherCellId==0xFFFF, out
of 16,939 exit portals total); 0 interior (non-exit) portals ever
qualify (interior: 1,837,298 total, 340 any-vertex, 0 all-on-plane).
Duration ~15s. This exactly reproduces the review's cited numbers.
F2 — seal-path count order. RetailPViewPassExecutor's exit-seal loop
dropped the `localVertices.Length < 3` pre-filter that ran BEFORE both
the boundary guard and the `submitted` increment (round 0's order:
length check -> guard -> transform -> draw -> count). Retail's real
order is guard -> transform -> clip -> count, with the counter
incremented BEFORE polyClipFinish runs (0x59BD70-0x59BD74 precedes
0x59BDB0) — the counter records accepted ATTEMPTS, not successful GPU
fans. `submitted++` now runs immediately after the guard-gated
transform+EmitSeamMask, before DrawDepthFan (whose own `< 3` guard is
this port's stand-in for retail's post-clip `var_4 >= 3` check). A new
pin (RetailPViewPassExecutorTests.DrawExitPortalMask_CountsAnUnclippableTwoVertexPolygon_ButDrawsNothing)
constructs a real PortalDepthMaskRenderer (RecordingGpuDevice-backed)
and a constructor-bypassed executor (RuntimeHelpers.GetUninitializedObject,
the same pattern WalkOutsideViewReassemblyTests already uses) with a
2-vertex ordinary-coordinate portal polygon: DrawExitPortalMask returns
submitted=1 while zero draw calls reach the device.
MUTATION (verified): restore the `< 3` pre-filter ahead of the guard
-> the pin's Assert.Equal fails, Expected:1 Actual:0.
F3 — the depth-event transcript gate, automated. New
WalkAlphaDepthTrace.cs (beside WalkOracleTrace.cs) parses the four
`PM poly=<ptr> mode=<0|1> counterBefore=<hex>` / `PC ov=<n>
counter=<hex> fc=<0|1>` lines per oh-capture/*.alphadepth.log frame,
using the same F-marker frame delimiting as WalkOracleTrace. New
WalkTraceConformanceTests.AlphaDepthTranscript.cs (beside
WalkTraceConformanceTests.cs, which is now `partial`) replays each
pose's frame 2 through the real WalkFrameDriver/RetailFrameWalk over
WalkLandscapeDatBuilder-built installed-DAT world data, with:
- AlphaDepthCollectSink: an IWalkEventSink decorator forwarding every
hook to the real driver unchanged, while recording mode=1 punch
events (OnPunchGeometry, counterBefore = driver.PortalsDrawnCount
at COLLECT time — Collect finishes entirely before Replay starts,
so reading the counter at Replay time would already see the SAME
turn's post-reset value; holtburg-doorway-still's own capture
proves this: its punches show counterBefore=2 immediately followed
by its seals' counterBefore=0/1 in the identical turn) and every
PC tuple (Emit, WalkEventKind.DrawCells — fires for the root's own
turn AND every building look-in's re-entrant call, matching
retail's breakpoint not distinguishing them).
- AlphaDepthLeaf: an IWalkFrameLeafRenderer whose DrawExitSeals
reproduces DrawPortalDepthWrite's real per-portal loop (F1's guard,
F2's count-before-clip order) directly over
driver.InteriorFloodCells / WalkCell.Portals / .PortalPolygons —
the identical data the render-side production path reads — since
the interface gives DrawExitSeals no cell/portal parameter at all.
- Two Collect+Replay passes per pose (a fresh driver reaches the SAME
steady-state a running session already carries by the time
retail's capture began — holtburg-doorway-still and foundry-deep
both show a stable per-frame accepted-seal count that regenerates
identically every captured frame): the first pass's own output is
discarded, only the second is compared.
Five Facts (one per pose — the spec's "four" undercounts the five
*.alphadepth.log files actually present in oh-capture/):
AlphaDepthTranscript_CathedralArrival_MatchesRetailFrame2 — PASS
AlphaDepthTranscript_CathedralLeak_MatchesRetailFrame2 — PASS
AlphaDepthTranscript_FoundryDeep_MatchesRetailFrame2 — PASS
AlphaDepthTranscript_HoltburgDoorwayStill_MatchesRetailFrame2 — PASS
(proves the incrementing 0/1/2 accepted-seal sequence AND the
pre-reset counterBefore=2 punches, end to end, against real DAT
portal data)
AlphaDepthTranscript_TerraceEdge_MatchesRetailFrame2 — KnownFailure
(tagged [Trait("Status","KnownFailure")]): terrace-edge never runs
a qualifying interior turn in its own capture (every PC line reads
ov=0) — its punches' counterBefore=2 is a session value carried
over from BEFORE the capture even started (the file's own pre-"F 1"
content already reads 2; nothing inside the captured frames ever
touches the counter). A fresh two-pass replay from
PortalsDrawnCount=0 has no mechanism to derive that leftover value.
Printed divergence: expected [(mode=1,counterBefore=2) x2], actual
[(mode=1,counterBefore=0) x2]; the PC sequence (ov=0 both times)
matches exactly. An initial-condition gap in the fixture, not a
guard/count defect — the identical harness reproduces holtburg-
doorway-still's and foundry-deep's own steady states exactly from
cold starts.
MUTATION (verified): swap AlphaDepthLeaf's recorded seal mode from 0
to 1 -> AlphaDepthTranscript_HoltburgDoorwayStill_MatchesRetailFrame2
fails (Assert.Equal Collections differ at pos 5: expected
(0,0),(0,1)... actual (1,0),(1,1)...).
DispatcherFixture and DrawScope (WalkFrameDriverTests.cs) are widened
from private to internal so the new gate can drive a real
WbDrawDispatcher without duplicating that fixture.
F4 — manifest comment truth. VulkanShaderManifestTests.cs's
portal_depth.vert.spv re-pin comment had the ULP direction backward
(claimed the OLD 0x3F7FFFFE literal was NEARER the camera than
retail's real 0x3F7FFFEF constant; portal_depth.vert's own shader-
source comment already had this correct — FARTHER, larger z/w) and
cited T1 as living in WalkVisibilityMathTests when
PortalDepthVert_FarPunchConstant_MatchesRetailExactBits is in this
same file. Both corrected.
Gates: dotnet build tests/AcDream.App.Tests -c Release -> 0 warnings/0
errors. Hermetic lane (Lane!=InstalledDat&...&Status!=KnownFailure):
6829/6829 passed. InstalledDat lane (real client_cell_1.dat): 253
total, 247 passed, 5 failed — exactly the two #383 layout tests
(LayoutImporterMediaBearingChildSweepTests/LayoutImporterInvisibleSweepTests),
TowerAscentReplayTests' KnownFailure, Oh_doorway_still_first_frame_diff
(#458), and this round's own AlphaDepthTranscript_TerraceEdge
KnownFailure — no other divergence. Shader tests (VulkanShaderDescriptorContractTests
+ VulkanShaderManifestTests + RenderPackSpirvValidatorTests +
SkyVertexLayoutTests): 35/35. Register/divergence tests: 52/52.
dotnet build src/AcDream.App -c Release: 0 warnings/0 errors.
Register: no new AD row. F1/F2 are straight bug fixes toward retail
fidelity (not deviations); AD-119 (the C2 blend-vs-write-mask row)
does not describe the count order, so its neighbour needed no
correction per F2's own instruction.
Deviation from the fix-round spec's literal text: §7 F1 said "swap the
any-vertex hits instead of the four per-plane accumulators" as the
split-plane case's mutation — the code actually ported uses per-vertex
`if (x != 12f) flag = false;` clears, not a per-vertex OR; the mutation
verified above (restore the any-vertex FORM entirely) exercises the
same defect class and is the mutation actually run. §7 also names
"four" alphadepth.log fixtures where five exist on disk
(cathedral-arrival, cathedral-leak, foundry-deep,
holtburg-doorway-still, terrace-edge); all five are gated rather than
four, with cathedral-leak an unremarked addition alongside the four
the spec names.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
482 lines
24 KiB
C#
482 lines
24 KiB
C#
using AcDream.App.Tests.Rendering;
|
||
using AcDream.App.Rendering.Walk;
|
||
using DatReaderWriter;
|
||
using DatReaderWriter.Options;
|
||
|
||
namespace AcDream.App.Tests.Rendering.Walk;
|
||
|
||
/// <summary>
|
||
/// FW1's conformance gate, first slice: replay pose-stamped oracle
|
||
/// fixtures through the ported walk and require the identical event
|
||
/// sequence. The projection-light interior fixtures gate first
|
||
/// (docs/research/2026-08-30-fw-walk-oracle/README.md, posed round);
|
||
/// the outdoor/landscape fixtures join as the landscape world build
|
||
/// lands.
|
||
/// </summary>
|
||
[Trait("Lane", "InstalledDat")]
|
||
public sealed partial class WalkTraceConformanceTests
|
||
{
|
||
/// <summary>S3 chunk 1 fix round 1 (G10): besides the plain
|
||
/// <see cref="WalkEvent"/> list the four-kind
|
||
/// <see cref="WalkTraceReplayContext.Signature(IEnumerable{WalkEvent})"/>
|
||
/// still uses, also builds the full eight-kind token stream — LS/BLD/
|
||
/// DI/DC/LC/SC/EC/OC — AS each hook fires, for
|
||
/// <see cref="WalkTraceReplayContext.Signature8(IReadOnlyList{string})"/>.
|
||
/// <see cref="_lookIn"/> mirrors <c>WalkFrameDriver</c>'s own private
|
||
/// <c>_currentDcStage</c>: null until the frame's first
|
||
/// <see cref="WalkEventKind.DrawInside"/> (the interior root's own
|
||
/// flood defers its EC/OC to <see cref="OnInteriorFloodDrawTurn"/>),
|
||
/// flips true forever once a building's portal pass begins
|
||
/// (<see cref="OnBuildingTurn"/>) — see
|
||
/// <see cref="WalkTraceReplayContext.AppendFloodTurns"/>'s own doc
|
||
/// comment for why this never resets mid-frame. S3 chunk 1 fix round 2
|
||
/// (§11.6 H3): this REPLAY-side derivation is unchanged — only the
|
||
/// ORACLE side (<see cref="WalkTraceReplayContext.Signature8(WalkOracleFrame)"/>)
|
||
/// moved off derivation onto its own literal captured EC/OC lines.
|
||
///
|
||
/// <para>KNOWN LIMIT (S3 review fix round 1, F4a): the production
|
||
/// transcript prints <c>EC</c> once per LIVE VIEW of a flood cell
|
||
/// (retail's <c>setup_view</c>/<c>DrawEnvCell</c> loop,
|
||
/// <c>PView::DrawCells</c> @0x005a4ab1–0x005a4acc), but this replay
|
||
/// side's <see cref="WalkTraceReplayContext.AppendFloodTurns"/> still
|
||
/// derives ONE <c>EC</c> per cell — the sink event carries no per-cell
|
||
/// view count. Every fixture passes only because no captured flood cell
|
||
/// holds more than one live view at DrawCells time. A fixture with a
|
||
/// two-view flood cell would fail here on the second <c>EC</c>; S5's
|
||
/// transcript closeout carries the per-cell view count through the
|
||
/// sink event and removes this note.</para></summary>
|
||
private sealed class Recorder : IWalkEventSink
|
||
{
|
||
public readonly List<WalkEvent> Events = new();
|
||
public readonly List<string> Tokens = new();
|
||
|
||
private bool? _lookIn;
|
||
|
||
public void Emit(in WalkEvent walkEvent)
|
||
{
|
||
Events.Add(walkEvent);
|
||
switch (walkEvent.Kind)
|
||
{
|
||
case WalkEventKind.Landscape:
|
||
Tokens.Add("LS");
|
||
break;
|
||
case WalkEventKind.Building:
|
||
Tokens.Add($"BLD:{walkEvent.CellId:x8}");
|
||
break;
|
||
case WalkEventKind.DrawInside:
|
||
Tokens.Add($"DI:{walkEvent.CellId:x8}");
|
||
_lookIn = false;
|
||
break;
|
||
case WalkEventKind.DrawCells:
|
||
Tokens.Add(
|
||
$"DC:ov={walkEvent.OutsideViewCount}:"
|
||
+ string.Join(',', walkEvent.Cells.Select(c => c.ToString("x8"))));
|
||
// A look-in flood's DC draws its EC/OC immediately
|
||
// (RetailFrameWalk.HandleDrawCellsTurn calls
|
||
// EmitFloodTurns right here for WalkDrawStage.
|
||
// LookInStatic); the interior root's OWN flood (stage
|
||
// CellStatic, _lookIn still false) defers to
|
||
// OnInteriorFloodDrawTurn below — see that method.
|
||
if (_lookIn == true)
|
||
WalkTraceReplayContext.AppendFloodTurns(Tokens, walkEvent.Cells);
|
||
break;
|
||
}
|
||
}
|
||
|
||
public void OnLandCellTurn(uint landblockId, int sideCellCount, int cellIndex)
|
||
=> Tokens.Add(
|
||
$"LC:{WalkTranscriptDump.LodCellId(landblockId, sideCellCount, cellIndex):x8}");
|
||
|
||
public void OnSortCellTurn(uint landblockId, int sideCellCount, int cellIndex)
|
||
=> Tokens.Add(
|
||
$"SC:{WalkTranscriptDump.LodCellId(landblockId, sideCellCount, cellIndex):x8}");
|
||
|
||
public void OnBuildingTurn(WalkBuilding building) => _lookIn = true;
|
||
|
||
public void OnInteriorFloodDrawTurn(IReadOnlyList<uint> cells, int outsideViewCount)
|
||
=> WalkTraceReplayContext.AppendFloodTurns(Tokens, cells);
|
||
|
||
/// <summary>S3 review fix round 1 (F4b): retail's trailing per-frame
|
||
/// weather turn (<c>GameSky::Draw(sky,1)</c> @0x00506ff0, fired by
|
||
/// <c>RetailFrameWalk.DrawLandscape</c> when <c>ctx.WeatherGateOpen</c>
|
||
/// holds) prints its own "OC" line
|
||
/// (<c>WalkFrameDriver.OnWeatherTurn</c>'s production sibling) —
|
||
/// before this fix the interface's default no-op left the replay
|
||
/// side of the eight-kind signature silently blind to it, so
|
||
/// <see cref="WalkTraceReplayContext.Signature8(WalkOracleFrame)"/>
|
||
/// had to drop the SAME line from the oracle side just to keep both
|
||
/// sides comparable (a pin that could never fail on the weather
|
||
/// turn's own placement/value). Recording it here the SAME way
|
||
/// <see cref="Emit"/> records every other "OC" (identical token
|
||
/// shape) lets the oracle side compare literally instead.</summary>
|
||
public void OnWeatherTurn(uint viewerCellId) => Tokens.Add($"OC:{viewerCellId:x8}");
|
||
}
|
||
|
||
private static DatCollection OpenDats()
|
||
{
|
||
string? datDir = CornerFloodReplayTests.ResolveDatDir();
|
||
if (datDir is null)
|
||
{
|
||
Assert.Fail("Lane=InstalledDat requires an installed retail DAT directory; see docs/release-gate.md.");
|
||
}
|
||
return new DatCollection(datDir!, DatAccessType.Read);
|
||
}
|
||
|
||
[Fact]
|
||
public void Street_outdoor_first_frame_diff()
|
||
{
|
||
// The first landscape-involving conformance case: diff-first (the
|
||
// assert prints both signatures on mismatch for adjudication).
|
||
IReadOnlyList<WalkOracleFrame> frames =
|
||
WalkOracleTrace.Load("posed/holtburg-street-outdoor");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1]; // frame 2: pose settled (marker timing)
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, null, world.Landscape, ctx, recorder);
|
||
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
[Fact]
|
||
public void Doorway_still_first_frame_diff()
|
||
{
|
||
// Interior flood adjudication: DI + DC(ov=2, n=3) + the landscape
|
||
// through two exit views — tests the flood depth in isolation from
|
||
// the building look-in machinery.
|
||
IReadOnlyList<WalkOracleFrame> frames =
|
||
WalkOracleTrace.Load("posed/holtburg-doorway-still");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1];
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell camera = Assert.Contains(frame.Pose.CellId, world.Cells);
|
||
// Render::deg_mul is DYNAMIC (auto-tuned by frame load), and this
|
||
// capture ran right after the heavy terrace-edge capture with the
|
||
// multiplier depressed: at mul ≤ 0 the degrade thresholds sit at or
|
||
// below each level's ideal, so 001e (eff 29.3), 0026 (27.1), and
|
||
// 002f (43.4) all select the portless level 1 — retail's zero
|
||
// look-in floods. Every other fixture pins ≈ +0.99 (thresholds at
|
||
// max). The recon session's live dump read −0.99 under the same cdb
|
||
// load. Re-dump deg_mul per capture at the next retail session.
|
||
var walk = new RetailFrameWalk { DegradeMultiplier = 0f };
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// PINNED RED 2026-09-03 (S3 chunk 1 fix round 1, G10 — un-skip with the
|
||
/// fix): the eight-kind signature is the FIRST comparison ever run at
|
||
/// LC/SC placement/content against a DAT-built world for this fixture
|
||
/// (the old four-kind Signature filtered LC/SC out entirely — how the
|
||
/// G7 SC-ordering regression shipped green). One real divergence
|
||
/// survives after fixing the LandblockId=0 fixture bug this same round
|
||
/// uncovered (WalkLandscapeDatBuilder.cs — a genuinely separate,
|
||
/// already-fixed defect): at token index 165, the replay draws ONE
|
||
/// EXTRA land-cell pair retail never does —
|
||
/// <c>…SC:a8c90001|LC:a8c90001|SC:a8c90001|</c> retail jumps straight to
|
||
/// <c>LC:95c60001</c>; the replay inserts <c>LC:a9c90001|SC:a9c90001</c>
|
||
/// first. A single spurious block admission at a ring-2/ring-3 LOD
|
||
/// boundary near this pose — a real <c>WalkLandscape.CheckBlocks</c>
|
||
/// visibility question, not a print-site or ordering bug, and out of
|
||
/// this round's print-only scope (§11.4) — needs the retail oracle
|
||
/// (decomp/cdb) to resolve. Filed as docs/ISSUES.md #458.
|
||
/// terrace-edge/cathedral-arrival/foundry-deep all reproduce EXACTLY at
|
||
/// this same eight-kind level; only this row's specific pose hits the
|
||
/// boundary. S3 chunk 1 fix round 2 (§11.6 H3) switched the ORACLE side
|
||
/// of the eight-kind signature from a derived EC/OC to retail's own
|
||
/// LITERAL captured EC/OC lines — re-verified 2026-09-03 that this
|
||
/// divergence stays at the SAME token index 165 (an LC/SC-only mismatch,
|
||
/// upstream of any EC/OC content) under the new comparison; #458's
|
||
/// position is unchanged.
|
||
///
|
||
/// <para>RESOLVED 2026-09-03 (lead, two live cdb captures — see
|
||
/// docs/ISSUES.md #458 and register row AD-118): it is a PRECISION
|
||
/// BOUNDARY, not a structural walk divergence. At this fixture pose the
|
||
/// replay's block test for <c>a9c9</c> (ring 21 north, z slab 75..330)
|
||
/// has three of the four block corners OUTSIDE the doorway's fourth
|
||
/// edge plane and the south-west corner just INSIDE it, so
|
||
/// <c>block_plane_check</c> says PartiallyInside and the block draws;
|
||
/// retail's plane sits about 0.5% away (measured at a nearby pose:
|
||
/// retail's clip heights 300.4/310.2 m vs the replay's 298.8/308.5 m
|
||
/// for the same block, with the SAME sentinel pattern and the SAME
|
||
/// verdicts 1 then 0 in the two views) and puts that last corner
|
||
/// outside too. The row stays KnownFailure on purpose: the fixture is
|
||
/// retail's literal frame, and no bit-exact fix exists short of
|
||
/// reproducing D3D's x87 projection.</para>
|
||
/// </summary>
|
||
[Fact]
|
||
[Trait("Status", "KnownFailure")]
|
||
public void Oh_doorway_still_first_frame_diff()
|
||
{
|
||
// S3 chunk 1 (§11.2 B3): the OH kit pose's own doorway-still capture
|
||
// — DI a9b4013f, DC(ov=2, n=3) per §6b. Mirrors
|
||
// Doorway_still_first_frame_diff's own structure (this pose needs
|
||
// the interior camera cell, unlike the plain outdoor/theory rows).
|
||
// S3 chunk 1 fix round 1 (G6): kept as its own Fact rather than
|
||
// folded into Still_fixture_first_frame_reproduces_exactly's Theory
|
||
// — it and Oh_foundry_deep_... predate that Theory's OH rows and
|
||
// mirror their own FW0 siblings' structure; either shape works, no
|
||
// functional reason blocks merging them later.
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(
|
||
"docs/research/2026-09-01-overhaul/oh-capture", "holtburg-doorway-still.walk");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1];
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell camera = Assert.Contains(frame.Pose.CellId, world.Cells);
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
// S3 chunk 1 fix round 1 (G10): the OH kit pose carries LC/SC/EC/OC
|
||
// — compare at the full eight-kind level (see Signature8's own doc
|
||
// comment).
|
||
string expected = WalkTraceReplayContext.Signature8(frame);
|
||
string actual = WalkTraceReplayContext.Signature8(recorder.Tokens);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
private const string FwOracleRoot = "docs/research/2026-08-30-fw-walk-oracle";
|
||
|
||
/// <summary>S3 chunk 1 (§11.2 B3): the OH capture directory — its OWN
|
||
/// pose-stamped kit-pose captures, a DIFFERENT root than the FW0 still
|
||
/// fixtures above.</summary>
|
||
private const string OhCaptureRoot = "docs/research/2026-09-01-overhaul/oh-capture";
|
||
|
||
[Theory]
|
||
[InlineData(FwOracleRoot, "posed/terrace-center")]
|
||
[InlineData(FwOracleRoot, "posed/terrace-edge")]
|
||
[InlineData(FwOracleRoot, "posed/cathedral-arrival")]
|
||
// S3 chunk 1 (§11.2 B3): the OH kit poses, as NEW rows — the OH
|
||
// cathedral-arrival root is f4180108, NOT FW0's f4180106 (a new pose,
|
||
// not a replacement of the FW0 row above).
|
||
[InlineData(OhCaptureRoot, "terrace-edge.walk")]
|
||
[InlineData(OhCaptureRoot, "cathedral-arrival.walk")]
|
||
public void Still_fixture_first_frame_reproduces_exactly(string root, string fixture)
|
||
{
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(root, fixture);
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1];
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell? camera = (frame.Pose.CellId & 0xFFFFu) >= 0x100
|
||
? Assert.Contains(frame.Pose.CellId, world.Cells)
|
||
: null;
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
// S3 chunk 1 fix round 1 (G10): the OH kit-pose rows carry
|
||
// LC/SC/EC/OC — compare THOSE two rows at the full eight-kind level
|
||
// (Signature8's own doc comment); the FW0 rows have none of those
|
||
// lines at all (captured before those breakpoints existed), so they
|
||
// stay on the original four-kind comparison.
|
||
string expected = root == OhCaptureRoot
|
||
? WalkTraceReplayContext.Signature8(frame)
|
||
: WalkTraceReplayContext.Signature(frame);
|
||
string actual = root == OhCaptureRoot
|
||
? WalkTraceReplayContext.Signature8(recorder.Tokens)
|
||
: WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail ({fixture})\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
[Fact]
|
||
public void Foundry_entry_reproduces_every_frame_before_the_f67_order_segment()
|
||
{
|
||
// F67–F79 (the fixture's terminal standing segment) diverge in ONE
|
||
// aspect: the flood SET through building a9b40036 is exact, but
|
||
// retail orders its DCs 116,118,11d where the replay orders
|
||
// 11d,116,118. The microscope isolates the flip to the ROOT plane
|
||
// of 0036's drawing BSP (N=(0,0,−1) D=2.8): the replay's
|
||
// building-local eye z is 2.33 (d=+0.47 → NEG-subtree first), while
|
||
// retail behaves as d<0 (POS-first) — a structural ~0.5 m frame
|
||
// question (positionPush(2) / part-scale semantics), only
|
||
// adjudicable by a live cdb dump of FrameCurrent->viewer.viewpoint
|
||
// during 0036's build_draw_portals_only (script:
|
||
// tools/walk-oracle/fw1-f67-viewpoint-probe.cdb). Frames 1–66 must
|
||
// reproduce exactly.
|
||
MovingFixtureReplay("posed/foundry-entry", stopBeforeFrame: 67);
|
||
}
|
||
|
||
[Theory]
|
||
[InlineData("posed/holtburg-walkout")]
|
||
[InlineData("posed/holtburg-transitions")]
|
||
[InlineData("posed/holtburg-walkabout")]
|
||
public void Moving_fixture_reproduces_every_pairable_frame(string fixture)
|
||
=> MovingFixtureReplay(fixture);
|
||
|
||
private void MovingFixtureReplay(string fixture, int stopBeforeFrame = int.MaxValue)
|
||
{
|
||
// Marker timing: the pose stamped at frame N+1 is the camera state
|
||
// frame N drew with (fixture README) — pair events(N) with
|
||
// pose(N+1). One walk + one landscape carry state across frames,
|
||
// reproducing retail's cross-frame caches (the stale cell-order
|
||
// quirk included).
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(fixture);
|
||
Assert.True(frames.Count >= 3);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOraclePose anchor = frames[1].Pose!;
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, anchor.CellId, anchor.Origin);
|
||
var walk = new RetailFrameWalk();
|
||
|
||
for (int n = 1; n < frames.Count - 1; n++)
|
||
{
|
||
WalkOracleFrame frame = frames[n];
|
||
if (frame.Number >= stopBeforeFrame) break;
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
// The marker dumps the PREVIOUS frame's camera, so frame N's
|
||
// true camera state lies between pose(N) and pose(N+1) — a
|
||
// capture artifact, not a port ambiguity. A frame passes when
|
||
// the replay matches under either adjacent pose.
|
||
string? firstActual = null;
|
||
bool matched = false;
|
||
foreach (WalkOraclePose pose in new[] { frames[n + 1].Pose!, frame.Pose! })
|
||
{
|
||
Assert.NotNull(pose);
|
||
WalkLandscapeDatBuilder.SetViewer(world.Landscape, pose.CellId, pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell? camera = null;
|
||
if ((pose.CellId & 0xFFFFu) >= 0x100)
|
||
{
|
||
Assert.True(
|
||
world.Cells.TryGetValue(pose.CellId, out camera),
|
||
$"frame {frame.Number}: interior camera cell {pose.CellId:x8} not loaded");
|
||
}
|
||
var recorder = new Recorder();
|
||
walk.WalkFrame(pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
firstActual ??= actual;
|
||
if (actual == expected)
|
||
{
|
||
matched = true;
|
||
break;
|
||
}
|
||
}
|
||
Assert.True(
|
||
matched,
|
||
$"frame {frame.Number} diverged under both adjacent poses ({fixture})\n"
|
||
+ $"EXPECTED: {expected}\nACTUAL: {firstActual}");
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void Foundry_deep_reproduces_every_complete_frame_exactly()
|
||
{
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load("posed/foundry-deep");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
Dictionary<uint, WalkCell> cells =
|
||
WalkWorldDatAdapter.BuildInteriorCells(dats, 0xA9B40000u);
|
||
var landscape = new WalkLandscape { MidWidth = 1, Blocks = new WalkLandBlock?[1] };
|
||
|
||
foreach (WalkOracleFrame frame in frames)
|
||
{
|
||
Assert.NotNull(frame.Pose);
|
||
WalkCell camera = Assert.Contains(frame.Pose!.CellId, cells);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, cells);
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, landscape, ctx, recorder);
|
||
|
||
Assert.Equal(
|
||
WalkTraceReplayContext.Signature(frame),
|
||
WalkTraceReplayContext.Signature(recorder.Events));
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void Oh_foundry_deep_reproduces_every_complete_frame_exactly()
|
||
{
|
||
// S3 chunk 1 (§11.2 B3): the OH kit pose's own foundry-deep capture
|
||
// — DI a9b40176, DC(ov=1, n=2), 12 town buildings drawn through the
|
||
// surviving exit chain (§6b). UNLIKE the FW0 sibling above, this
|
||
// capture's own retail transcript shows real BLD content at that
|
||
// depth, so the stub 1x1 landscape (which has no blocks/buildings to
|
||
// walk at all) undershoots it — first divergence, run without the
|
||
// fix below: "DI:a9b40176|DC:ov=1:a9b40176,a9b40177|LS" (nothing
|
||
// after LS) vs retail's "…|LS|BLD:a9b40031|BLD:a9b…" (12 real
|
||
// buildings). The full landscape/building assembler
|
||
// (WalkLandscapeDatBuilder.Build — the SAME one the shared theory
|
||
// and Oh_doorway_still_first_frame_diff use) reproduces them; the
|
||
// camera is stationary across this still pose, so one build serves
|
||
// every frame in the loop, matching the FW0 sibling's "build once"
|
||
// shape.
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(
|
||
"docs/research/2026-09-01-overhaul/oh-capture", "foundry-deep.walk");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
Assert.NotNull(frames[0].Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world = WalkLandscapeDatBuilder.Build(
|
||
dats, frames[0].Pose!.CellId, frames[0].Pose!.Origin);
|
||
|
||
foreach (WalkOracleFrame frame in frames)
|
||
{
|
||
Assert.NotNull(frame.Pose);
|
||
WalkCell camera = Assert.Contains(frame.Pose!.CellId, world.Cells);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
// S3 chunk 1 fix round 1 (G10): full eight-kind comparison —
|
||
// see Signature8's own doc comment.
|
||
Assert.Equal(
|
||
WalkTraceReplayContext.Signature8(frame),
|
||
WalkTraceReplayContext.Signature8(recorder.Tokens));
|
||
}
|
||
}
|
||
}
|