fix #432: allocation-free oracle fingerprint sort — the ~6 MB/frame diagnostics tax
Second instance of the #429 (ad695589) boxing-comparer defect class, this
time in the measurement harness rather than production:
ACDREAM_AUTOMATION_ARTIFACT_DIR (with retained-UI screenshots) constructs
CurrentRenderSceneOracle, whose presence as partition observer re-enables
the G5-retired legacy InteriorEntityPartition every frame with per-entity
fingerprinting. Complete() then sorts one fingerprint per RESIDENT entity
(~60k across the streaming window), and the comparer's first key bound
x.ProjectionClass.CompareTo(y.ProjectionClass) to Enum.CompareTo(object),
boxing both operands. The 3-value enum almost always ties, so the boxing
ran on essentially every comparison: a measured ~6.2 MB and ~14 ms per
frame, everywhere — not town-specific and not view-triggered, which is
also why it appeared to "latch" (the resident set drives it, not the view).
Comparing the underlying integral value keeps the identical order. Hermetic
gate: one warmed observed partition of 20,000 entities allocated 15,876,088
bytes before, and passes a <64 KiB bound after
(OracleObservedPartitionAllocationTests).
Ordinary play never constructs the oracle, so no player-visible behavior
changes; what changes is that captures taken with the automation artifact
directory set are no longer taxed. The #429 acceptance data is unaffected
(owner drives and the deciding A/B arms ran with the artifact dir null).
The temporary [pview-alloc] attribution probe that localized this is
retired in the same commit; the gate test now guards the defect.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
b217a292bb
commit
92999b0101
3 changed files with 180 additions and 18 deletions
|
|
@ -0,0 +1,65 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Scene;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.Tests.Rendering;
|
||||
|
||||
public sealed class OracleObservedPartitionAllocationTests
|
||||
{
|
||||
// #432: the automation-artifact harness constructs CurrentRenderSceneOracle,
|
||||
// which re-enables the G5-retired legacy partition every frame WITH the
|
||||
// oracle observing every resident entity. A warmed observed partition must
|
||||
// not allocate per frame — the fingerprint walk and its sort run over the
|
||||
// whole streaming window's resident entity set (~60k live), so any
|
||||
// per-comparison or per-entity transient multiplies into MB/frame and
|
||||
// poisons every measurement taken with ACDREAM_AUTOMATION_ARTIFACT_DIR set.
|
||||
[Fact]
|
||||
internal void AWarmedObservedPartitionAllocatesNearZero()
|
||||
{
|
||||
const int EntityCount = 20_000;
|
||||
var entities = new List<WorldEntity>(EntityCount);
|
||||
for (int i = 0; i < EntityCount; i++)
|
||||
{
|
||||
// Scramble ids so the fingerprint sort does real work instead of
|
||||
// consuming pre-sorted input.
|
||||
uint id = unchecked((uint)i * 2654435761u);
|
||||
entities.Add(new WorldEntity
|
||||
{
|
||||
Id = id,
|
||||
SourceGfxObjOrSetupId = 0x01000000u + (id & 0xFFFu),
|
||||
Position = new Vector3(i % 192, i / 192f, 0f),
|
||||
Rotation = Quaternion.Identity,
|
||||
MeshRefs = new[] { new MeshRef(0x01000001u, Matrix4x4.Identity) },
|
||||
});
|
||||
}
|
||||
|
||||
var landblockEntries = new[]
|
||||
{
|
||||
(LandblockId: 0xA9B4FFFFu,
|
||||
AabbMin: Vector3.Zero,
|
||||
AabbMax: new Vector3(192f, 192f, 100f),
|
||||
Entities: (IReadOnlyList<WorldEntity>)entities,
|
||||
AnimatedById: (IReadOnlyDictionary<uint, WorldEntity>?)null),
|
||||
};
|
||||
var visibleCells = new HashSet<uint>();
|
||||
var result = new InteriorEntityPartition.Result();
|
||||
var oracle = new CurrentRenderSceneOracle();
|
||||
|
||||
for (int warm = 0; warm < 3; warm++)
|
||||
{
|
||||
InteriorEntityPartition.Partition(
|
||||
result, visibleCells, landblockEntries, oracle);
|
||||
}
|
||||
|
||||
long before = GC.GetAllocatedBytesForCurrentThread();
|
||||
InteriorEntityPartition.Partition(
|
||||
result, visibleCells, landblockEntries, oracle);
|
||||
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
|
||||
|
||||
Assert.True(
|
||||
allocated < 64 * 1024,
|
||||
$"warmed observed partition allocated {allocated} bytes for "
|
||||
+ $"{EntityCount} entities");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue