fix(rendering): bound portal resource lifetime
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>
This commit is contained in:
parent
3971997689
commit
749e8ceeb1
225 changed files with 29107 additions and 3914 deletions
|
|
@ -39,6 +39,198 @@ public class PortalVisibilityBuilderTests
|
|||
private static PortalVisibilityFrame Build(LoadedCell cam, Dictionary<uint, LoadedCell> all)
|
||||
=> PortalVisibilityBuilder.Build(cam, Vector3.Zero, id => all.TryGetValue(id, out var c) ? c : null, ViewProj());
|
||||
|
||||
[Fact]
|
||||
public void Build_ReusedFrame_NestedGraph_MatchesFreshAndStabilizesScratch()
|
||||
{
|
||||
var root = Cell(0x0001,
|
||||
new CellPortalInfo(0x0002, 0, 0, 0),
|
||||
new CellPortalInfo(0x0003, 1, 0, 0));
|
||||
root.PortalPolygons.Add(QuadX(-0.8f, -0.05f, -2f));
|
||||
root.PortalPolygons.Add(QuadX(0.05f, 0.8f, -3f));
|
||||
var left = Cell(0x0002, new CellPortalInfo(0x0004, 0, 0, 0));
|
||||
left.PortalPolygons.Add(QuadX(-0.8f, -0.05f, -5f));
|
||||
var right = Cell(0x0003, new CellPortalInfo(0x0004, 0, 0, 0));
|
||||
right.PortalPolygons.Add(QuadX(0.05f, 0.8f, -5f));
|
||||
var exit = Cell(0x0004, new CellPortalInfo(0xFFFF, 0, 0, 0));
|
||||
exit.PortalPolygons.Add(QuadX(-0.8f, 0.8f, -8f));
|
||||
var cells = new Dictionary<uint, LoadedCell>
|
||||
{
|
||||
[root.CellId] = root,
|
||||
[left.CellId] = left,
|
||||
[right.CellId] = right,
|
||||
[exit.CellId] = exit,
|
||||
};
|
||||
LoadedCell? Lookup(uint id) => cells.TryGetValue(id, out LoadedCell? cell) ? cell : null;
|
||||
|
||||
PortalVisibilityFrame fresh = PortalVisibilityBuilder.Build(
|
||||
root, Vector3.Zero, Lookup, ViewProj());
|
||||
uint[] expectedOrder = fresh.OrderedVisibleCells.ToArray();
|
||||
uint[] expectedCells = fresh.CellViews.Keys.OrderBy(id => id).ToArray();
|
||||
int expectedOutsidePolygons = fresh.OutsideView.Polygons.Count;
|
||||
|
||||
var reuse = new PortalVisibilityFrame();
|
||||
PortalVisibilityBuilder.Build(root, Vector3.Zero, Lookup, ViewProj(), reuseFrame: reuse);
|
||||
int scratchHighWater = reuse.ClipRegionScratchCount;
|
||||
int vertexArrayHighWater = reuse.PolygonVertexAllocationCount;
|
||||
Assert.True(scratchHighWater > 0);
|
||||
Assert.True(vertexArrayHighWater > 0);
|
||||
|
||||
for (int frame = 0; frame < 16; frame++)
|
||||
{
|
||||
PortalVisibilityFrame actual = PortalVisibilityBuilder.Build(
|
||||
root, Vector3.Zero, Lookup, ViewProj(), reuseFrame: reuse);
|
||||
Assert.Same(reuse, actual);
|
||||
Assert.Equal(expectedOrder, actual.OrderedVisibleCells);
|
||||
Assert.Equal(expectedCells, actual.CellViews.Keys.OrderBy(id => id).ToArray());
|
||||
Assert.Equal(expectedOutsidePolygons, actual.OutsideView.Polygons.Count);
|
||||
Assert.Equal(scratchHighWater, reuse.ClipRegionScratchCount);
|
||||
Assert.Equal(vertexArrayHighWater, reuse.PolygonVertexAllocationCount);
|
||||
Assert.Empty(reuse.TodoScratch);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_ReusedFrame_AfterLookupException_HasNoStaleWorkOrViews()
|
||||
{
|
||||
var root = Cell(0x0001, new CellPortalInfo(0x0002, 0, 0, 0));
|
||||
root.PortalPolygons.Add(Quad(0f, 0f, 0.5f, 0.5f, -2f));
|
||||
var reuse = new PortalVisibilityFrame();
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => PortalVisibilityBuilder.Build(
|
||||
root,
|
||||
Vector3.Zero,
|
||||
_ => throw new InvalidOperationException("fixture lookup failure"),
|
||||
ViewProj(),
|
||||
reuseFrame: reuse));
|
||||
|
||||
var isolatedRoot = Cell(0x0010);
|
||||
PortalVisibilityFrame recovered = PortalVisibilityBuilder.Build(
|
||||
isolatedRoot, Vector3.Zero, _ => null, ViewProj(), reuseFrame: reuse);
|
||||
|
||||
Assert.Same(reuse, recovered);
|
||||
Assert.Equal(new[] { isolatedRoot.CellId }, recovered.OrderedVisibleCells);
|
||||
Assert.Equal(new[] { isolatedRoot.CellId }, recovered.CellViews.Keys);
|
||||
Assert.Empty(recovered.CrossBuildingViews);
|
||||
Assert.True(recovered.OutsideView.IsEmpty);
|
||||
Assert.Empty(reuse.TodoScratch);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResetForBuild_DropsPathologicalContainerHighWater_AfterIdleHysteresis()
|
||||
{
|
||||
const int pathologicalCount =
|
||||
PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity * 4;
|
||||
var frame = new PortalVisibilityFrame();
|
||||
var placeholder = new LoadedCell();
|
||||
|
||||
for (int i = 0; i < pathologicalCount; i++)
|
||||
{
|
||||
uint id = (uint)i + 1u;
|
||||
frame.CellViews.Add(id, new CellView());
|
||||
frame.CrossBuildingViews.Add(id, new CellView());
|
||||
frame.OrderedVisibleCells.Add(id);
|
||||
frame.QueuedScratch.Add(id);
|
||||
frame.DrawListedScratch.Add(id);
|
||||
frame.ProcessedViewCountsScratch.Add(id, i);
|
||||
frame.TodoScratch.Add((placeholder, i));
|
||||
}
|
||||
|
||||
Assert.True(
|
||||
frame.CellViewMapCapacity
|
||||
> PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
Assert.True(
|
||||
frame.QueuedCapacity
|
||||
> PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
|
||||
frame.ResetForBuild();
|
||||
|
||||
Assert.Empty(frame.CellViews);
|
||||
Assert.Empty(frame.CrossBuildingViews);
|
||||
Assert.Empty(frame.OrderedVisibleCells);
|
||||
Assert.Empty(frame.QueuedScratch);
|
||||
Assert.Empty(frame.DrawListedScratch);
|
||||
Assert.Empty(frame.ProcessedViewCountsScratch);
|
||||
Assert.Empty(frame.TodoScratch);
|
||||
Assert.InRange(
|
||||
frame.RetainedCellViewCount,
|
||||
0,
|
||||
PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
Assert.True(
|
||||
frame.CellViewMapCapacity
|
||||
> PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
|
||||
for (int i = 0; i < PortalVisibilityFrame.CapacityTrimIdleFrames; i++)
|
||||
frame.ResetForBuild();
|
||||
|
||||
Assert.InRange(
|
||||
frame.CellViewMapCapacity,
|
||||
0,
|
||||
PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
Assert.InRange(
|
||||
frame.CrossBuildingViewMapCapacity,
|
||||
0,
|
||||
PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
Assert.InRange(
|
||||
frame.QueuedCapacity,
|
||||
0,
|
||||
PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
Assert.InRange(
|
||||
frame.DrawListedCapacity,
|
||||
0,
|
||||
PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
Assert.InRange(
|
||||
frame.ProcessedViewCountCapacity,
|
||||
0,
|
||||
PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
Assert.InRange(
|
||||
frame.OrderedVisibleCells.Capacity,
|
||||
0,
|
||||
PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
Assert.InRange(
|
||||
frame.TodoScratch.Capacity,
|
||||
0,
|
||||
PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity);
|
||||
|
||||
LoadedCell root = Cell(0x1000);
|
||||
PortalVisibilityFrame rebuilt = PortalVisibilityBuilder.Build(
|
||||
root,
|
||||
Vector3.Zero,
|
||||
_ => null,
|
||||
ViewProj(),
|
||||
reuseFrame: frame);
|
||||
|
||||
Assert.Same(frame, rebuilt);
|
||||
Assert.Equal(new[] { root.CellId }, rebuilt.OrderedVisibleCells);
|
||||
Assert.True(rebuilt.CellViews.ContainsKey(root.CellId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResetForBuild_RecurringLargeFlood_PreservesWarmCapacity()
|
||||
{
|
||||
const int recurringCount =
|
||||
PortalVisibilityFrame.MaxRetainedBuildCollectionCapacity + 163;
|
||||
var frame = new PortalVisibilityFrame();
|
||||
|
||||
for (int iteration = 0;
|
||||
iteration < PortalVisibilityFrame.CapacityTrimIdleFrames + 5;
|
||||
iteration++)
|
||||
{
|
||||
for (int i = 0; i < recurringCount; i++)
|
||||
{
|
||||
uint id = (uint)i + 1u;
|
||||
frame.QueuedScratch.Add(id);
|
||||
frame.OrderedVisibleCells.Add(id);
|
||||
}
|
||||
|
||||
int queuedCapacity = frame.QueuedCapacity;
|
||||
int orderedCapacity = frame.OrderedVisibleCells.Capacity;
|
||||
frame.ResetForBuild();
|
||||
|
||||
Assert.Equal(queuedCapacity, frame.QueuedCapacity);
|
||||
Assert.Equal(orderedCapacity, frame.OrderedVisibleCells.Capacity);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Builder_Cellar_WindowClippedToStairwell_NotFullWindow()
|
||||
{
|
||||
|
|
@ -627,6 +819,40 @@ public class PortalVisibilityBuilderTests
|
|||
"exterior seed should be clipped to the door opening, not full-screen");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildFromExterior_ReusedFrame_ClearsPriorOutputAndProducesSameResult()
|
||||
{
|
||||
var room = Cell(0x0001, new CellPortalInfo(0xFFFF, 0, 0, 0));
|
||||
room.PortalPolygons.Add(Quad(0f, 0f, 0.5f, 0.5f, -4f));
|
||||
room.ClipPlanes.Add(new PortalClipPlane
|
||||
{
|
||||
Normal = new Vector3(0f, 0f, 1f),
|
||||
D = 3f,
|
||||
InsideSide = 1,
|
||||
});
|
||||
var reuse = new PortalVisibilityFrame();
|
||||
LoadedCell? Lookup(uint id) => id == room.CellId ? room : null;
|
||||
|
||||
var first = PortalVisibilityBuilder.BuildFromExterior(
|
||||
new[] { room }, Vector3.Zero, Lookup, ViewProj(), reuseFrame: reuse);
|
||||
Assert.Same(reuse, first);
|
||||
Assert.Equal(new[] { room.CellId }, first.OrderedVisibleCells);
|
||||
|
||||
var empty = PortalVisibilityBuilder.BuildFromExterior(
|
||||
Array.Empty<LoadedCell>(), Vector3.Zero, Lookup, ViewProj(), reuseFrame: reuse);
|
||||
Assert.Same(reuse, empty);
|
||||
Assert.Empty(empty.OrderedVisibleCells);
|
||||
Assert.Empty(empty.CellViews);
|
||||
Assert.True(empty.OutsideView.IsEmpty);
|
||||
|
||||
var second = PortalVisibilityBuilder.BuildFromExterior(
|
||||
new[] { room }, Vector3.Zero, Lookup, ViewProj(), reuseFrame: reuse);
|
||||
Assert.Same(reuse, second);
|
||||
Assert.Equal(new[] { room.CellId }, second.OrderedVisibleCells);
|
||||
Assert.True(second.CellViews.TryGetValue(room.CellId, out CellView? view));
|
||||
Assert.False(view!.IsEmpty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildFromExterior_DoesNotSeedWhenCameraIsOnInteriorSide()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue