checkpoint(render): preserve pre-overhaul investigation state
This commit is contained in:
parent
e880860291
commit
b3b7d922f1
45 changed files with 3168 additions and 619 deletions
|
|
@ -45,16 +45,16 @@ public sealed class WalkFrameDriverTests
|
|||
RetailAlphaQueue? alpha = null) : IWalkFrameLeafRenderer
|
||||
{
|
||||
public readonly List<WalkPolygon> Punches = new();
|
||||
public readonly List<(uint CellId, uint ClipSlot)> Shells = new();
|
||||
public readonly List<uint> Shells = new();
|
||||
public readonly List<int> AlphaPendingAtBarrier = new();
|
||||
|
||||
public void DrawSky() => log.Add("SKY");
|
||||
|
||||
public void DrawTerrainSlice(int sliceIndex) => log.Add($"TERRAIN:{sliceIndex}");
|
||||
|
||||
public void DrawCellShell(uint cellId, uint clipSlot)
|
||||
public void DrawCellShell(uint cellId)
|
||||
{
|
||||
Shells.Add((cellId, clipSlot));
|
||||
Shells.Add(cellId);
|
||||
log.Add($"SHELL:{cellId:x8}");
|
||||
}
|
||||
|
||||
|
|
@ -267,7 +267,9 @@ public sealed class WalkFrameDriverTests
|
|||
|
||||
var leaf = new RecordingLeafRenderer(log);
|
||||
var trace = new RecordingTrace(log);
|
||||
var driver = new WalkFrameDriver(fx.Dispatcher, leaf, worldData, trace);
|
||||
using ClipFrame clipFrame = ClipFrame.NoClip();
|
||||
var driver = new WalkFrameDriver(
|
||||
fx.Dispatcher, leaf, worldData, trace, clipFrame);
|
||||
var walk = new RetailFrameWalk();
|
||||
// A minimal, no-op landscape (1x1 window, the one slot unpublished)
|
||||
// — matches RetailFrameWalkTests' own exit-view fixture. LScape::draw
|
||||
|
|
@ -296,6 +298,79 @@ public sealed class WalkFrameDriverTests
|
|||
Assert.All(mdiCalls, c => Assert.Equal(1u, c.DrawCount));
|
||||
// Nothing dropped: every populated record reached exactly one indirect draw.
|
||||
Assert.Equal(2, mdiCalls.Sum(c => (int)c.DrawCount));
|
||||
|
||||
// Exit seals replay through the exact portal_view slices captured by
|
||||
// this walk. The legacy PortalVisibilityFrame is not consulted.
|
||||
Assert.Equal(1, driver.InteriorFloodViewSliceCountAt(0));
|
||||
Assert.Equal(1, driver.InteriorFloodViewSliceCountAt(1));
|
||||
Assert.Equal(4, driver.InteriorFloodViewClipPlanesAt(0, 0).Length);
|
||||
Assert.Equal(4, driver.InteriorFloodViewClipPlanesAt(1, 0).Length);
|
||||
Assert.True(clipFrame.SlotCount >= 3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InteriorFloodAfterLandscape_RearmsRetailPartStampForPostClearCellRepaint()
|
||||
{
|
||||
using var fx = new DispatcherFixture();
|
||||
var log = new List<string>();
|
||||
const ulong gfxObj = 0x0200_0021UL;
|
||||
const uint outdoorCellId = 0xF4180009u;
|
||||
const uint interiorCellId = 0xF4180112u;
|
||||
InjectRenderData(fx.Manager, gfxObj, MakeFlatMesh(
|
||||
MakeBatch(0x08100021u, TranslucencyKind.Opaque, 0, 0, 3, 1)));
|
||||
|
||||
RenderProjectionRecord sharedPart = MakeRecord(
|
||||
0x4F41806Cu,
|
||||
0,
|
||||
Vector3.Zero,
|
||||
[new MeshRef((uint)gfxObj, Matrix4x4.Identity)]);
|
||||
var worldData = new FakeWorldData();
|
||||
worldData.OutdoorStaticsByCell[outdoorCellId] =
|
||||
new WalkFrameStaticRecords(new[] { sharedPart }, 0xF418u);
|
||||
worldData.CellStaticsByCell[interiorCellId] =
|
||||
new WalkFrameStaticRecords(new[] { sharedPart }, 0xF418u);
|
||||
|
||||
var ctx = new TestContext();
|
||||
var interiorCell = new WalkCell { CellId = interiorCellId };
|
||||
interiorCell.PushView();
|
||||
WalkCopyView.AppendFullViewportQuad(
|
||||
interiorCell.TopView,
|
||||
ctx.Rays,
|
||||
ctx.WorldViewpoint,
|
||||
ctx.ViewportWidth,
|
||||
ctx.ViewportHeight);
|
||||
ctx.Cells[interiorCellId] = interiorCell;
|
||||
|
||||
var driver = new WalkFrameDriver(
|
||||
fx.Dispatcher,
|
||||
new RecordingLeafRenderer(log),
|
||||
worldData,
|
||||
new RecordingTrace(log));
|
||||
IWalkEventSink sink = driver;
|
||||
|
||||
using DrawScope draw = fx.BeginDraw();
|
||||
driver.BeginFrame(ctx, Matrix4x4.Identity, Vector3.Zero);
|
||||
sink.Emit(WalkEvent.Landscape(activeViewCount: 1));
|
||||
var landscapeViews = new WalkPortalView();
|
||||
WalkCopyView.AppendFullViewportQuad(
|
||||
landscapeViews,
|
||||
ctx.Rays,
|
||||
ctx.WorldViewpoint,
|
||||
ctx.ViewportWidth,
|
||||
ctx.ViewportHeight);
|
||||
sink.OnLandscapeViews(landscapeViews);
|
||||
sink.OnLandscapeCellTurn(outdoorCellId);
|
||||
sink.OnInteriorFloodDrawTurn([interiorCellId]);
|
||||
driver.EndFrame();
|
||||
driver.Replay(draw.Frame, draw.Pass);
|
||||
|
||||
List<GpuRecordedMultiDrawIndirect> mdiCalls =
|
||||
[.. fx.Device.Calls.OfType<GpuRecordedMultiDrawIndirect>()];
|
||||
Assert.Equal(2, mdiCalls.Count);
|
||||
Assert.All(mdiCalls, call => Assert.Equal(1u, call.DrawCount));
|
||||
Assert.Equal(2, mdiCalls.Sum(call => (int)call.DrawCount));
|
||||
Assert.Equal(2, log.Count(entry => entry == "FLUSH:1:OutdoorStatic"
|
||||
|| entry == "FLUSH:1:CellStatic"));
|
||||
}
|
||||
|
||||
// ── Deliverable: the ov==0 interior case — no exit view survives, so
|
||||
|
|
@ -479,11 +554,7 @@ public sealed class WalkFrameDriverTests
|
|||
Assert.Equal([0x104u], driver.LookInCells);
|
||||
Assert.Collection(
|
||||
leaf.Shells,
|
||||
shell =>
|
||||
{
|
||||
Assert.Equal(0x104u, shell.CellId);
|
||||
Assert.NotEqual(0u, shell.ClipSlot);
|
||||
});
|
||||
shell => Assert.Equal(0x104u, shell));
|
||||
WalkPortalView capturedView = ctx.Cells[0x104].PortalViews[0];
|
||||
WalkViewPoly capturedPoly = Assert.Single(capturedView.View.Polys);
|
||||
Vector2 capturedCenter = Vector2.Zero;
|
||||
|
|
@ -515,6 +586,101 @@ public sealed class WalkFrameDriverTests
|
|||
Assert.Equal(3, mdiCalls.Sum(c => (int)c.DrawCount));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RepeatedFloodTurns_DrawEnvCellShellWholeOncePerRetailFrameStamp()
|
||||
{
|
||||
using var fx = new DispatcherFixture();
|
||||
var log = new List<string>();
|
||||
var leaf = new RecordingLeafRenderer(log);
|
||||
var ctx = new TestContext();
|
||||
const uint cellId = 0xF4180112u;
|
||||
var cell = new WalkCell { CellId = cellId };
|
||||
cell.PushView();
|
||||
WalkCopyView.AppendFullViewportQuad(
|
||||
cell.TopView,
|
||||
ctx.Rays,
|
||||
ctx.WorldViewpoint,
|
||||
ctx.ViewportWidth,
|
||||
ctx.ViewportHeight);
|
||||
ctx.Cells[cellId] = cell;
|
||||
|
||||
var driver = new WalkFrameDriver(
|
||||
fx.Dispatcher,
|
||||
leaf,
|
||||
new FakeWorldData());
|
||||
IWalkEventSink sink = driver;
|
||||
|
||||
using DrawScope draw = fx.BeginDraw();
|
||||
driver.BeginFrame(ctx, Matrix4x4.Identity, Vector3.Zero);
|
||||
sink.OnInteriorFloodDrawTurn([cellId]);
|
||||
sink.OnInteriorFloodDrawTurn([cellId]);
|
||||
driver.EndFrame();
|
||||
driver.Replay(draw.Frame, draw.Pass);
|
||||
|
||||
Assert.Equal([cellId], leaf.Shells);
|
||||
Assert.Equal(1, log.Count(entry => entry == "SHELL:f4180112"));
|
||||
Assert.Equal(2, log.Count(entry => entry == "CLEAR"));
|
||||
Assert.Equal(2, log.Count(entry => entry == "SEALS"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LandscapeStampBoundary_RearmsWholeShellForPostClearRootRepaint()
|
||||
{
|
||||
using var fx = new DispatcherFixture();
|
||||
var log = new List<string>();
|
||||
var leaf = new RecordingLeafRenderer(log);
|
||||
var ctx = new TestContext();
|
||||
const uint cellId = 0xF4180112u;
|
||||
var cell = new WalkCell { CellId = cellId };
|
||||
cell.PushView();
|
||||
WalkCopyView.AppendFullViewportQuad(
|
||||
cell.TopView,
|
||||
ctx.Rays,
|
||||
ctx.WorldViewpoint,
|
||||
ctx.ViewportWidth,
|
||||
ctx.ViewportHeight);
|
||||
ctx.Cells[cellId] = cell;
|
||||
|
||||
var driver = new WalkFrameDriver(
|
||||
fx.Dispatcher,
|
||||
leaf,
|
||||
new FakeWorldData());
|
||||
IWalkEventSink sink = driver;
|
||||
|
||||
using DrawScope draw = fx.BeginDraw();
|
||||
driver.BeginFrame(ctx, Matrix4x4.Identity, Vector3.Zero);
|
||||
|
||||
// LScape::draw has begun. A building look-in reached this cell before
|
||||
// PView::DrawCells advances m_nFrameStamp and clears interior depth.
|
||||
sink.Emit(WalkEvent.Landscape(activeViewCount: 1));
|
||||
var landscapeViews = new WalkPortalView();
|
||||
WalkCopyView.AppendFullViewportQuad(
|
||||
landscapeViews,
|
||||
ctx.Rays,
|
||||
ctx.WorldViewpoint,
|
||||
ctx.ViewportWidth,
|
||||
ctx.ViewportHeight);
|
||||
sink.OnLandscapeViews(landscapeViews);
|
||||
sink.OnBuildingTurn(new WalkBuilding());
|
||||
sink.Emit(WalkEvent.DrawCells(outsideViewCount: 0, [cellId]));
|
||||
|
||||
// The same shell must draw again after the retail stamp increment and
|
||||
// full depth clear; otherwise the pre-clear color survives unpaired
|
||||
// with depth and bleeds through the root's walls.
|
||||
sink.OnInteriorFloodDrawTurn([cellId]);
|
||||
driver.EndFrame();
|
||||
driver.Replay(draw.Frame, draw.Pass);
|
||||
|
||||
Assert.Equal([cellId, cellId], leaf.Shells);
|
||||
Assert.Equal(2, log.Count(entry => entry == "SHELL:f4180112"));
|
||||
Assert.True(
|
||||
log.IndexOf("SHELL:f4180112") < log.IndexOf("CLEAR"),
|
||||
"The look-in shell must precede the interior clear.");
|
||||
Assert.True(
|
||||
log.LastIndexOf("SHELL:f4180112") > log.IndexOf("SEALS"),
|
||||
"The rearmed root shell must repaint after the clear and seals.");
|
||||
}
|
||||
|
||||
// ── Fail-loud: a DrawCells turn with no preceding DrawInside/Building
|
||||
// turn is a walk/driver desync, not a silent skip. ─────────────────────
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue