Separate logical ownership, render publication, and GPU retirement across live entities, landblocks, particles, textures, mesh arenas, portal/UI teardown, and per-frame scratch storage. Add bounded DAT/texture caches, upload budgets, three-frame fence retirement, exact-incarnation appearance reconciliation, frame pacing, and extensive lifetime conformance coverage.\n\nThe seven-destination connected route now cuts peak working/private memory roughly in half, returns Caul to 125-153 FPS locally, and produces no WER or AMD reset.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
302 lines
12 KiB
C#
302 lines
12 KiB
C#
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using AcDream.App.Rendering;
|
|
using Xunit;
|
|
|
|
namespace AcDream.App.Tests.Rendering;
|
|
|
|
public class ClipFrameAssemblerTests
|
|
{
|
|
private static ViewPolygon Square(float cx, float cy, float half) => new(new[]
|
|
{
|
|
new Vector2(cx - half, cy - half),
|
|
new Vector2(cx + half, cy - half),
|
|
new Vector2(cx + half, cy + half),
|
|
new Vector2(cx - half, cy + half),
|
|
});
|
|
|
|
private static CellView ViewOf(params ViewPolygon[] polys)
|
|
{
|
|
var view = new CellView();
|
|
foreach (var p in polys)
|
|
view.Add(p);
|
|
return view;
|
|
}
|
|
|
|
[Fact]
|
|
public void TwoVisibleCells_PlusOutsideView_ProducesCorrectSlotMapAndCounts()
|
|
{
|
|
const uint cellA = 0xA9B40100;
|
|
const uint cellB = 0xA9B40101;
|
|
|
|
var pv = new PortalVisibilityFrame();
|
|
pv.CellViews[cellA] = ViewOf(Square(-0.3f, 0f, 0.3f));
|
|
pv.CellViews[cellB] = ViewOf(Square(0.3f, 0f, 0.2f));
|
|
pv.OrderedVisibleCells.Add(cellA);
|
|
pv.OrderedVisibleCells.Add(cellB);
|
|
pv.OutsideView.Add(Square(0f, 0.5f, 0.25f));
|
|
|
|
var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv);
|
|
|
|
Assert.Equal(4, asm.Frame.SlotCount); // slot 0 + two cells + one outside slice
|
|
Assert.Contains(cellA, asm.CellIdToSlot.Keys);
|
|
Assert.Contains(cellB, asm.CellIdToSlot.Keys);
|
|
Assert.NotEqual(0, asm.CellIdToSlot[cellA]);
|
|
Assert.NotEqual(0, asm.CellIdToSlot[cellB]);
|
|
Assert.NotEqual(asm.CellIdToSlot[cellA], asm.CellIdToSlot[cellB]);
|
|
Assert.Equal(4, asm.PerCellPlaneCounts[cellA]);
|
|
Assert.Equal(4, asm.PerCellPlaneCounts[cellB]);
|
|
|
|
Assert.True(asm.OutdoorVisible);
|
|
Assert.NotEqual(0, asm.OutdoorSlot);
|
|
Assert.Single(asm.OutsideViewSlices);
|
|
Assert.Equal(asm.OutdoorSlot, asm.OutsideViewSlices[0].Slot);
|
|
Assert.Equal(TerrainClipMode.Planes, asm.TerrainMode);
|
|
Assert.Equal(4, asm.OutsidePlaneCount);
|
|
Assert.Equal(0, asm.ScissorFallbacks);
|
|
}
|
|
|
|
[Fact]
|
|
public void NothingVisibleCell_IsExcludedFromSlotMap_AndNotAppended()
|
|
{
|
|
const uint cellA = 0xA9B40100;
|
|
const uint cellB = 0xA9B40101;
|
|
|
|
var pv = new PortalVisibilityFrame();
|
|
pv.CellViews[cellA] = ViewOf(Square(0f, 0f, 0.3f));
|
|
pv.CellViews[cellB] = new CellView();
|
|
pv.OrderedVisibleCells.Add(cellA);
|
|
pv.OrderedVisibleCells.Add(cellB);
|
|
|
|
var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv);
|
|
|
|
Assert.Equal(2, asm.Frame.SlotCount);
|
|
Assert.Contains(cellA, asm.CellIdToSlot.Keys);
|
|
Assert.DoesNotContain(cellB, asm.CellIdToSlot.Keys);
|
|
Assert.False(asm.OutdoorVisible);
|
|
Assert.Empty(asm.OutsideViewSlices);
|
|
Assert.Equal(TerrainClipMode.Skip, asm.TerrainMode);
|
|
Assert.Equal(0, asm.OutsidePlaneCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void OutsideViewMultiPolygon_PreservesRetailSlices()
|
|
{
|
|
const uint cellA = 0xA9B40100;
|
|
|
|
var pv = new PortalVisibilityFrame();
|
|
pv.CellViews[cellA] = ViewOf(Square(0f, 0f, 0.3f));
|
|
pv.OrderedVisibleCells.Add(cellA);
|
|
pv.OutsideView.Add(Square(-0.5f, 0f, 0.1f));
|
|
pv.OutsideView.Add(Square(0.5f, 0f, 0.1f));
|
|
|
|
var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv);
|
|
|
|
Assert.True(asm.OutdoorVisible);
|
|
Assert.NotEqual(0, asm.OutdoorSlot);
|
|
Assert.Equal(2, asm.OutsideViewSlices.Length);
|
|
Assert.NotEqual(asm.OutsideViewSlices[0].Slot, asm.OutsideViewSlices[1].Slot);
|
|
Assert.Equal(TerrainClipMode.Planes, asm.TerrainMode);
|
|
Assert.Equal(4, asm.OutsidePlaneCount);
|
|
Assert.Equal(0, asm.ScissorFallbacks);
|
|
Assert.Equal(4, asm.Frame.SlotCount); // slot 0 + cell + two outside slices
|
|
Assert.Equal(Vector4.Zero, asm.TerrainScissorNdcAabb);
|
|
}
|
|
|
|
[Fact]
|
|
public void CellMultiPolygonView_PreservesRetailViewSlices()
|
|
{
|
|
const uint cellA = 0xA9B40100;
|
|
|
|
var pv = new PortalVisibilityFrame();
|
|
pv.CellViews[cellA] = ViewOf(
|
|
Square(-0.4f, 0f, 0.1f),
|
|
Square(0.4f, 0f, 0.1f));
|
|
pv.OrderedVisibleCells.Add(cellA);
|
|
pv.OutsideView.Add(Square(0f, 0f, 0.3f));
|
|
|
|
var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv);
|
|
|
|
Assert.True(asm.CellIdToSlot[cellA] > 0);
|
|
Assert.Equal(2, asm.CellIdToViewSlots[cellA].Length);
|
|
Assert.Equal(2, asm.CellIdToViewSlices[cellA].Length);
|
|
Assert.NotEqual(asm.CellIdToViewSlots[cellA][0], asm.CellIdToViewSlots[cellA][1]);
|
|
Assert.Equal(4, asm.PerCellPlaneCounts[cellA]);
|
|
Assert.Single(asm.OutsideViewSlices);
|
|
Assert.Equal(4, asm.Frame.SlotCount); // slot 0 + two cell slices + outside slice
|
|
Assert.Equal(0, asm.ScissorFallbacks);
|
|
Assert.Equal(TerrainClipMode.Planes, asm.TerrainMode);
|
|
}
|
|
|
|
[Fact]
|
|
public void Assemble_OutsideViewWithExitPortal_HasOutsideViewTrue_AabbMatchesBounds()
|
|
{
|
|
var pv = new PortalVisibilityFrame();
|
|
pv.OutsideView.Add(Square(-0.3f, 0.2f, 0.25f));
|
|
|
|
var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv);
|
|
|
|
Assert.True(asm.HasOutsideView);
|
|
Assert.Equal(TerrainClipMode.Planes, asm.TerrainMode);
|
|
Assert.Single(asm.OutsideViewSlices);
|
|
|
|
var expected = new Vector4(
|
|
pv.OutsideView.MinX, pv.OutsideView.MinY,
|
|
pv.OutsideView.MaxX, pv.OutsideView.MaxY);
|
|
Assert.Equal(expected, asm.OutsideViewNdcAabb);
|
|
}
|
|
|
|
[Fact]
|
|
public void Assemble_OutsideViewMultiPolygon_PreservesSlicesAndUnionAabb()
|
|
{
|
|
var pv = new PortalVisibilityFrame();
|
|
pv.OutsideView.Add(Square(-0.6f, 0f, 0.15f));
|
|
pv.OutsideView.Add(Square(0.6f, 0f, 0.15f));
|
|
|
|
var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv);
|
|
|
|
Assert.True(asm.HasOutsideView);
|
|
Assert.Equal(TerrainClipMode.Planes, asm.TerrainMode);
|
|
Assert.Equal(2, asm.OutsideViewSlices.Length);
|
|
|
|
var expected = new Vector4(
|
|
pv.OutsideView.MinX, pv.OutsideView.MinY,
|
|
pv.OutsideView.MaxX, pv.OutsideView.MaxY);
|
|
Assert.Equal(expected, asm.OutsideViewNdcAabb);
|
|
Assert.Equal(Vector4.Zero, asm.TerrainScissorNdcAabb);
|
|
}
|
|
|
|
[Fact]
|
|
public void Assemble_EmptyOutsideView_HasOutsideViewFalse_AabbZero()
|
|
{
|
|
const uint cellA = 0xA9B40100;
|
|
var pv = new PortalVisibilityFrame();
|
|
pv.CellViews[cellA] = ViewOf(Square(0f, 0f, 0.3f));
|
|
pv.OrderedVisibleCells.Add(cellA);
|
|
|
|
var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv);
|
|
|
|
Assert.False(asm.HasOutsideView);
|
|
Assert.Equal(TerrainClipMode.Skip, asm.TerrainMode);
|
|
Assert.Equal(Vector4.Zero, asm.OutsideViewNdcAabb);
|
|
}
|
|
|
|
[Fact]
|
|
public void Reset_ReusesFrame_NoSlotLeakAcrossAssemblies()
|
|
{
|
|
var frame = ClipFrame.NoClip();
|
|
|
|
var pv1 = new PortalVisibilityFrame();
|
|
pv1.CellViews[0xA9B40100] = ViewOf(Square(-0.3f, 0f, 0.2f));
|
|
pv1.CellViews[0xA9B40101] = ViewOf(Square(0.3f, 0f, 0.2f));
|
|
pv1.OrderedVisibleCells.Add(0xA9B40100);
|
|
pv1.OrderedVisibleCells.Add(0xA9B40101);
|
|
pv1.OutsideView.Add(Square(0f, 0.4f, 0.2f));
|
|
var asm1 = ClipFrameAssembler.Assemble(frame, pv1);
|
|
Assert.Equal(4, asm1.Frame.SlotCount);
|
|
|
|
var pv2 = new PortalVisibilityFrame();
|
|
pv2.CellViews[0xA9B40200] = ViewOf(Square(0f, 0f, 0.25f));
|
|
pv2.OrderedVisibleCells.Add(0xA9B40200);
|
|
var asm2 = ClipFrameAssembler.Assemble(frame, pv2);
|
|
|
|
Assert.Equal(2, asm2.Frame.SlotCount);
|
|
Assert.Contains(0xA9B40200, asm2.CellIdToSlot.Keys);
|
|
Assert.DoesNotContain(0xA9B40100, asm2.CellIdToSlot.Keys);
|
|
Assert.False(asm2.OutdoorVisible);
|
|
Assert.Equal(TerrainClipMode.Skip, asm2.TerrainMode);
|
|
}
|
|
|
|
[Fact]
|
|
public void Assemble_ReusedAssembly_ClearsStateAndReusesExactLengthArrays()
|
|
{
|
|
const uint firstCell = 0xA9B40100;
|
|
const uint secondCell = 0xA9B40200;
|
|
var frame = ClipFrame.NoClip();
|
|
var reuse = new ClipFrameAssembly();
|
|
|
|
var first = new PortalVisibilityFrame();
|
|
first.CellViews[firstCell] = ViewOf(
|
|
Square(-0.4f, 0f, 0.15f),
|
|
Square(0.4f, 0f, 0.15f));
|
|
first.OrderedVisibleCells.Add(firstCell);
|
|
first.OutsideView.Add(Square(0f, 0.5f, 0.2f));
|
|
ClipFrameAssembly firstResult = ClipFrameAssembler.Assemble(frame, first, reuse);
|
|
Assert.Same(reuse, firstResult);
|
|
int sliceAllocations = reuse.SliceArrayAllocationCount;
|
|
int slotAllocations = reuse.SlotArrayAllocationCount;
|
|
|
|
var second = new PortalVisibilityFrame();
|
|
second.CellViews[secondCell] = ViewOf(
|
|
Square(-0.4f, 0f, 0.15f),
|
|
Square(0.4f, 0f, 0.15f));
|
|
second.OrderedVisibleCells.Add(secondCell);
|
|
second.OutsideView.Add(Square(0f, 0.5f, 0.2f));
|
|
|
|
for (int i = 0; i < 12; i++)
|
|
{
|
|
ClipFrameAssembly actual = ClipFrameAssembler.Assemble(frame, second, reuse);
|
|
Assert.Same(reuse, actual);
|
|
Assert.DoesNotContain(firstCell, actual.CellIdToViewSlices.Keys);
|
|
Assert.Contains(secondCell, actual.CellIdToViewSlices.Keys);
|
|
Assert.Equal(2, actual.CellIdToViewSlices[secondCell].Length);
|
|
Assert.Equal(2, actual.CellIdToViewSlots[secondCell].Length);
|
|
Assert.Single(actual.OutsideViewSlices);
|
|
Assert.Equal(sliceAllocations, reuse.SliceArrayAllocationCount);
|
|
Assert.Equal(slotAllocations, reuse.SlotArrayAllocationCount);
|
|
}
|
|
|
|
var empty = new PortalVisibilityFrame();
|
|
ClipFrameAssembly cleared = ClipFrameAssembler.Assemble(frame, empty, reuse);
|
|
Assert.Empty(cleared.CellIdToSlot);
|
|
Assert.Empty(cleared.CellIdToViewSlots);
|
|
Assert.Empty(cleared.CellIdToViewSlices);
|
|
Assert.Empty(cleared.PerCellPlaneCounts);
|
|
Assert.Empty(cleared.OutsideViewSlices);
|
|
Assert.Equal(TerrainClipMode.Skip, cleared.TerrainMode);
|
|
}
|
|
|
|
[Fact]
|
|
public void Assemble_ReusedAssembly_ChangingCardinalityStaysBoundedThenReachesSteadyState()
|
|
{
|
|
const uint cellId = 0xA9B40100;
|
|
var frame = ClipFrame.NoClip();
|
|
var reuse = new ClipFrameAssembly();
|
|
|
|
static PortalVisibilityFrame FrameWithSlices(uint id, int count)
|
|
{
|
|
var portalFrame = new PortalVisibilityFrame();
|
|
var view = new CellView();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
// The assembler consumes the retail view_poly list directly. Distinct placement is
|
|
// irrelevant to this cache-cardinality stress; avoid CellView dedup collapsing it.
|
|
view.Polygons.Add(Square(i * 0.001f, 0f, 0.0004f));
|
|
}
|
|
portalFrame.CellViews[id] = view;
|
|
portalFrame.OrderedVisibleCells.Add(id);
|
|
return portalFrame;
|
|
}
|
|
|
|
for (int count = 1; count <= 180; count++)
|
|
ClipFrameAssembler.Assemble(frame, FrameWithSlices(cellId, count), reuse);
|
|
|
|
// Flush the final live arrays into the cache too.
|
|
ClipFrameAssembler.Assemble(frame, new PortalVisibilityFrame(), reuse);
|
|
Assert.InRange(reuse.RetainedSliceItems, 0, ClipFrameAssembly.MaxRetainedSliceItems);
|
|
Assert.InRange(reuse.RetainedSlotItems, 0, ClipFrameAssembly.MaxRetainedSlotItems);
|
|
Assert.InRange(reuse.RetainedSliceArrays, 0, ClipFrameAssembly.MaxRetainedArraysPerPool);
|
|
Assert.InRange(reuse.RetainedSlotArrays, 0, ClipFrameAssembly.MaxRetainedArraysPerPool);
|
|
|
|
PortalVisibilityFrame steady = FrameWithSlices(cellId, 73);
|
|
ClipFrameAssembler.Assemble(frame, steady, reuse);
|
|
int sliceAllocations = reuse.SliceArrayAllocationCount;
|
|
int slotAllocations = reuse.SlotArrayAllocationCount;
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
ClipFrameAssembler.Assemble(frame, steady, reuse);
|
|
Assert.Equal(sliceAllocations, reuse.SliceArrayAllocationCount);
|
|
Assert.Equal(slotAllocations, reuse.SlotArrayAllocationCount);
|
|
}
|
|
}
|
|
}
|