perf(render): bound animation and alpha scratch residency
Complete Slice D3 by replacing the unbounded animation dictionary with a concurrent byte/count LRU and by putting the three retail alpha scratch owners behind one typed aggregate budget. Preserve immediate growth and draw order while reclaiming one-frame density spikes after sustained under-use. Close stale bounds-cache issue evidence without inventing a cache.
This commit is contained in:
parent
3e18fc2730
commit
f2644d42c2
18 changed files with 857 additions and 34 deletions
|
|
@ -202,6 +202,81 @@ public sealed class RetailDatLoaderTests
|
|||
Assert.Throws<InvalidDataException>(() => animations.LoadAnimation(wrongAnimationKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnimationCache_CountPressureEvictsLruWithoutInvalidatingLiveReferences()
|
||||
{
|
||||
const uint firstDid = 0x03010020u;
|
||||
const uint secondDid = 0x03010021u;
|
||||
const uint thirdDid = 0x03010022u;
|
||||
var portal = new RawDatabase();
|
||||
portal.Add(firstDid, AnimationBytes(firstDid));
|
||||
portal.Add(secondDid, AnimationBytes(secondDid));
|
||||
portal.Add(thirdDid, AnimationBytes(thirdDid));
|
||||
var loader = new RetailAnimationLoader(
|
||||
portal,
|
||||
maximumEstimatedBytes: long.MaxValue,
|
||||
maximumEntries: 2);
|
||||
|
||||
DatAnimation first = Assert.IsType<DatAnimation>(
|
||||
loader.LoadAnimation(firstDid));
|
||||
_ = Assert.IsType<DatAnimation>(loader.LoadAnimation(secondDid));
|
||||
_ = Assert.IsType<DatAnimation>(loader.LoadAnimation(thirdDid));
|
||||
|
||||
AnimationCacheDiagnostics pressured = loader.Diagnostics;
|
||||
Assert.Equal(2, pressured.Count);
|
||||
Assert.Equal(1, pressured.Stats.Evictions);
|
||||
Assert.Equal(firstDid, first.Id);
|
||||
|
||||
DatAnimation reloaded = Assert.IsType<DatAnimation>(
|
||||
loader.LoadAnimation(firstDid));
|
||||
Assert.NotSame(first, reloaded);
|
||||
Assert.Equal(4, portal.TotalReads);
|
||||
Assert.Equal(2, loader.Diagnostics.Stats.Evictions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnimationCache_OversizeEntryIsServedButNeverRetained()
|
||||
{
|
||||
const uint animationDid = 0x03010023u;
|
||||
var portal = new RawDatabase();
|
||||
portal.Add(animationDid, AnimationBytes(animationDid));
|
||||
var loader = new RetailAnimationLoader(
|
||||
portal,
|
||||
maximumEstimatedBytes: 1,
|
||||
maximumEntries: 2);
|
||||
|
||||
Assert.NotNull(loader.LoadAnimation(animationDid));
|
||||
Assert.NotNull(loader.LoadAnimation(animationDid));
|
||||
|
||||
Assert.Equal(0, loader.Diagnostics.Count);
|
||||
Assert.Equal(0, loader.Diagnostics.EstimatedBytes);
|
||||
Assert.Equal(2, portal.TotalReads);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnimationCache_CoalescesSameDidAndAllowsUnrelatedReadsInParallel()
|
||||
{
|
||||
const uint firstDid = 0x03010024u;
|
||||
const uint secondDid = 0x03010025u;
|
||||
var portal = new RawDatabase { ReadDelayMilliseconds = 40 };
|
||||
portal.Add(firstDid, AnimationBytes(firstDid));
|
||||
portal.Add(secondDid, AnimationBytes(secondDid));
|
||||
var loader = new RetailAnimationLoader(portal);
|
||||
|
||||
DatAnimation?[] firstPair = await Task.WhenAll(
|
||||
Task.Run(() => loader.LoadAnimation(firstDid)),
|
||||
Task.Run(() => loader.LoadAnimation(firstDid)));
|
||||
Assert.Same(firstPair[0], firstPair[1]);
|
||||
Assert.Equal(1, portal.TotalReads);
|
||||
|
||||
await Task.WhenAll(
|
||||
Task.Run(() => loader.LoadAnimation(secondDid)),
|
||||
Task.Run(() => loader.LoadAnimation(0x03010026u)));
|
||||
|
||||
Assert.True(portal.MaxConcurrentReads >= 2);
|
||||
Assert.Equal(3, portal.TotalReads);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PhysicsScriptLoader_AllowsConcurrentFirstReads()
|
||||
{
|
||||
|
|
@ -224,6 +299,16 @@ public sealed class RetailDatLoaderTests
|
|||
Assert.Equal(2, portal.TotalReads);
|
||||
}
|
||||
|
||||
private static byte[] AnimationBytes(uint id)
|
||||
{
|
||||
byte[] bytes =
|
||||
ProjectileVfxDatFixtures.OrdinaryAnimation.ToArray();
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(
|
||||
bytes,
|
||||
id);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PhysicsScriptLoader_CoalescesConcurrentReadsForTheSameDid()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue