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:
Erik 2026-07-18 21:35:16 +02:00
parent 3971997689
commit 749e8ceeb1
225 changed files with 29107 additions and 3914 deletions

View file

@ -206,4 +206,97 @@ public class ClipFrameAssemblerTests
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);
}
}
}