acdream/tests/AcDream.App.Tests/Rendering/BuildingGroupScratchTests.cs
Erik 749e8ceeb1 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>
2026-07-18 21:35:16 +02:00

252 lines
8.7 KiB
C#

using System.Collections.Generic;
using System.Linq;
using AcDream.App.Rendering;
using Xunit;
namespace AcDream.App.Tests.Rendering;
public sealed class BuildingGroupScratchTests
{
[Fact]
public void Rebuild_DropsHistoricalKeys_BoundsRetention_AndPreservesEncounterOrder()
{
var scratch = new BuildingGroupScratch();
int pathologicalCount = BuildingGroupScratch.MaxRetainedGroups * 4;
var pathological = new List<LoadedCell>(pathologicalCount);
for (int i = 0; i < pathologicalCount; i++)
{
pathological.Add(new LoadedCell
{
CellId = (uint)i + 1u,
BuildingId = 0x1000_0000u + (uint)i,
});
}
scratch.Rebuild(pathological);
Assert.Equal(pathologicalCount, scratch.ActiveGroupCount);
Assert.True(scratch.MapCapacity > BuildingGroupScratch.MaxRetainedGroups);
var firstA = new LoadedCell { CellId = 0xAA01u, BuildingId = 0x20u };
var second = new LoadedCell { CellId = 0xBB01u, BuildingId = 0x10u };
var firstB = new LoadedCell { CellId = 0xAA02u, BuildingId = 0x20u };
var unstamped = new LoadedCell { CellId = 0xCC01u };
scratch.Rebuild([firstA, second, firstB, unstamped]);
Assert.Equal(3, scratch.ActiveGroupCount);
Assert.Equal(
new uint[] { 0x20u, 0x10u, unstamped.CellId },
scratch.Groups.Keys.ToArray());
Assert.Equal(new[] { firstA, firstB }, scratch.Groups[0x20u]);
Assert.Equal(new[] { second }, scratch.Groups[0x10u]);
Assert.Equal(new[] { unstamped }, scratch.Groups[unstamped.CellId]);
Assert.DoesNotContain(
scratch.Groups.Keys,
key => key >= 0x1000_0000u);
Assert.InRange(
scratch.RetainedListCount,
0,
BuildingGroupScratch.MaxRetainedGroups);
Assert.InRange(
scratch.MapCapacity,
0,
BuildingGroupScratch.MaxRetainedGroups);
scratch.Reset();
Assert.Equal(0, scratch.ActiveGroupCount);
Assert.Empty(scratch.Groups);
Assert.InRange(
scratch.RetainedListCount,
0,
BuildingGroupScratch.MaxRetainedGroups);
Assert.InRange(
scratch.MapCapacity,
0,
BuildingGroupScratch.MaxRetainedGroups);
}
[Fact]
public void Reset_DoesNotRetainPathologicalGroupBackingArray()
{
var scratch = new BuildingGroupScratch();
var oversizedGroup = new List<LoadedCell>(
BuildingGroupScratch.MaxRetainedCellsPerGroup * 2);
for (int i = 0;
i < BuildingGroupScratch.MaxRetainedCellsPerGroup * 2;
i++)
{
oversizedGroup.Add(new LoadedCell
{
CellId = (uint)i + 1u,
BuildingId = 0x42u,
});
}
scratch.Rebuild(oversizedGroup);
Assert.Single(scratch.Groups);
Assert.True(
scratch.Groups[0x42u].Capacity
> BuildingGroupScratch.MaxRetainedCellsPerGroup);
scratch.Reset();
Assert.Equal(0, scratch.RetainedListCount);
Assert.Empty(scratch.Groups);
}
}
public sealed class RetailPViewScratchRetentionTests
{
[Fact]
public void ClearFrameBuffers_DropsPathologicalHighWater_AfterIdleHysteresis()
{
var retention = new RetailPViewScratchRetention();
int pathologicalCellCount =
RetailPViewScratchRetention.MaxRetainedCellItems * 4;
int pathologicalFrameCount =
RetailPViewScratchRetention.MaxRetainedLookInFrames * 4;
var lookInFrames = new List<PortalVisibilityFrame>(pathologicalFrameCount);
var lookInPrepare = new HashSet<uint>();
var drawableCells = new HashSet<uint>();
var shellBatch = new HashSet<uint>();
var orderedTransparent = new List<uint>(pathologicalCellCount);
for (int i = 0; i < pathologicalFrameCount; i++)
lookInFrames.Add(new PortalVisibilityFrame());
for (int i = 0; i < pathologicalCellCount; i++)
{
uint id = (uint)i;
lookInPrepare.Add(id);
drawableCells.Add(id);
shellBatch.Add(id);
orderedTransparent.Add(id);
}
Assert.True(
lookInFrames.Capacity
> RetailPViewScratchRetention.MaxRetainedLookInFrames);
Assert.True(
drawableCells.EnsureCapacity(0)
> RetailPViewScratchRetention.MaxRetainedCellItems);
retention.ClearFrameBuffers(
lookInFrames,
lookInPrepare,
drawableCells,
shellBatch,
orderedTransparent);
Assert.Empty(lookInFrames);
Assert.Empty(lookInPrepare);
Assert.Empty(drawableCells);
Assert.Empty(shellBatch);
Assert.Empty(orderedTransparent);
Assert.True(
lookInFrames.Capacity
> RetailPViewScratchRetention.MaxRetainedLookInFrames);
for (int i = 0; i < RetailPViewScratchRetention.CapacityTrimIdleFrames; i++)
{
retention.ClearFrameBuffers(
lookInFrames,
lookInPrepare,
drawableCells,
shellBatch,
orderedTransparent);
}
Assert.InRange(
lookInFrames.Capacity,
0,
RetailPViewScratchRetention.MaxRetainedLookInFrames);
Assert.InRange(
lookInPrepare.EnsureCapacity(0),
0,
RetailPViewScratchRetention.MaxRetainedCellItems);
Assert.InRange(
drawableCells.EnsureCapacity(0),
0,
RetailPViewScratchRetention.MaxRetainedCellItems);
Assert.InRange(
shellBatch.EnsureCapacity(0),
0,
RetailPViewScratchRetention.MaxRetainedCellItems);
Assert.InRange(
orderedTransparent.Capacity,
0,
RetailPViewScratchRetention.MaxRetainedCellItems);
}
[Fact]
public void ClearFrameBuffers_PreservesNormalWarmCapacity()
{
var retention = new RetailPViewScratchRetention();
var lookInFrames = new List<PortalVisibilityFrame>(8);
var lookInPrepare = new HashSet<uint>();
var drawableCells = new HashSet<uint>();
var shellBatch = new HashSet<uint>();
var orderedTransparent = new List<uint>(64);
for (uint id = 0; id < 64; id++)
{
lookInPrepare.Add(id);
drawableCells.Add(id);
shellBatch.Add(id);
orderedTransparent.Add(id);
}
int lookInCapacity = lookInFrames.Capacity;
int prepareCapacity = lookInPrepare.EnsureCapacity(0);
int drawableCapacity = drawableCells.EnsureCapacity(0);
int shellCapacity = shellBatch.EnsureCapacity(0);
int transparentCapacity = orderedTransparent.Capacity;
retention.ClearFrameBuffers(
lookInFrames,
lookInPrepare,
drawableCells,
shellBatch,
orderedTransparent);
Assert.Equal(lookInCapacity, lookInFrames.Capacity);
Assert.Equal(prepareCapacity, lookInPrepare.EnsureCapacity(0));
Assert.Equal(drawableCapacity, drawableCells.EnsureCapacity(0));
Assert.Equal(shellCapacity, shellBatch.EnsureCapacity(0));
Assert.Equal(transparentCapacity, orderedTransparent.Capacity);
}
[Fact]
public void ClearFrameBuffers_RecurringLargeWorkingSet_PreservesWarmCapacity()
{
const int recurringCount =
RetailPViewScratchRetention.MaxRetainedCellItems + 163;
var retention = new RetailPViewScratchRetention();
var lookInFrames = new List<PortalVisibilityFrame>();
var lookInPrepare = new HashSet<uint>();
var drawableCells = new HashSet<uint>();
var shellBatch = new HashSet<uint>();
var orderedTransparent = new List<uint>();
for (int iteration = 0;
iteration < RetailPViewScratchRetention.CapacityTrimIdleFrames + 5;
iteration++)
{
for (int i = 0; i < recurringCount; i++)
{
uint id = (uint)i;
drawableCells.Add(id);
orderedTransparent.Add(id);
}
int drawableCapacity = drawableCells.EnsureCapacity(0);
int transparentCapacity = orderedTransparent.Capacity;
retention.ClearFrameBuffers(
lookInFrames,
lookInPrepare,
drawableCells,
shellBatch,
orderedTransparent);
Assert.Equal(drawableCapacity, drawableCells.EnsureCapacity(0));
Assert.Equal(transparentCapacity, orderedTransparent.Capacity);
}
}
}