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
|
|
@ -84,6 +84,7 @@ internal readonly record struct ResidencyCharges(
|
|||
long LogicalBytes = 0,
|
||||
long CpuPreparedBytes = 0,
|
||||
long DecodedBytes = 0,
|
||||
long ScratchBytes = 0,
|
||||
long PinnedBytes = 0,
|
||||
long StagingBytes = 0,
|
||||
long GpuRequestedBytes = 0,
|
||||
|
|
@ -97,6 +98,7 @@ internal readonly record struct ResidencyCharges(
|
|||
LogicalBytes
|
||||
+ CpuPreparedBytes
|
||||
+ DecodedBytes
|
||||
+ ScratchBytes
|
||||
+ StagingBytes);
|
||||
|
||||
public long PhysicalGpuBytes => checked(
|
||||
|
|
@ -111,6 +113,7 @@ internal readonly record struct ResidencyCharges(
|
|||
ArgumentOutOfRangeException.ThrowIfNegative(LogicalBytes);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(CpuPreparedBytes);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(DecodedBytes);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(ScratchBytes);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(PinnedBytes);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(StagingBytes);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(GpuRequestedBytes);
|
||||
|
|
@ -126,6 +129,7 @@ internal readonly record struct ResidencyCharges(
|
|||
checked(left.LogicalBytes + right.LogicalBytes),
|
||||
checked(left.CpuPreparedBytes + right.CpuPreparedBytes),
|
||||
checked(left.DecodedBytes + right.DecodedBytes),
|
||||
checked(left.ScratchBytes + right.ScratchBytes),
|
||||
checked(left.PinnedBytes + right.PinnedBytes),
|
||||
checked(left.StagingBytes + right.StagingBytes),
|
||||
checked(left.GpuRequestedBytes + right.GpuRequestedBytes),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
namespace AcDream.App.Rendering.Residency;
|
||||
|
||||
/// <summary>
|
||||
/// Splits the one deferred-alpha scratch ceiling between the three physical
|
||||
/// owners. The final share receives the division remainder so the aggregate
|
||||
/// remains exact for every valid startup budget.
|
||||
/// </summary>
|
||||
internal readonly record struct AlphaScratchBudgetProfile(
|
||||
long QueueBytes,
|
||||
long DispatcherBytes,
|
||||
long ParticleBytes)
|
||||
{
|
||||
public long TotalBytes => checked(
|
||||
QueueBytes + DispatcherBytes + ParticleBytes);
|
||||
|
||||
public static AlphaScratchBudgetProfile Create(long totalBytes)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalBytes);
|
||||
long queue = totalBytes / 4;
|
||||
long particles = totalBytes / 4;
|
||||
long dispatcher = checked(totalBytes - queue - particles);
|
||||
return new AlphaScratchBudgetProfile(queue, dispatcher, particles);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capacity retention policy for render-thread scratch storage. Growth is
|
||||
/// always immediate at the physical owner. This policy only recommends a
|
||||
/// smaller warmed capacity after several completed low-demand observations,
|
||||
/// preventing one dense alpha frame from pinning its high-water arrays for the
|
||||
/// rest of the process without introducing an elapsed-time workaround.
|
||||
/// </summary>
|
||||
internal sealed class RetainedScratchCapacityPolicy(
|
||||
long budgetBytes,
|
||||
int lowDemandSamplesBeforeShrink = 3)
|
||||
{
|
||||
private readonly long _budgetBytes =
|
||||
budgetBytes > 0
|
||||
? budgetBytes
|
||||
: throw new ArgumentOutOfRangeException(nameof(budgetBytes));
|
||||
private readonly int _lowDemandSamplesBeforeShrink =
|
||||
lowDemandSamplesBeforeShrink > 0
|
||||
? lowDemandSamplesBeforeShrink
|
||||
: throw new ArgumentOutOfRangeException(
|
||||
nameof(lowDemandSamplesBeforeShrink));
|
||||
private int _lowDemandSamples;
|
||||
|
||||
public long BudgetBytes => _budgetBytes;
|
||||
|
||||
public int ObserveAndSelectCapacity(
|
||||
int currentCapacity,
|
||||
int requiredCapacity,
|
||||
int bytesPerUnit,
|
||||
int minimumCapacity,
|
||||
int growthQuantum)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(currentCapacity);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(requiredCapacity);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(bytesPerUnit);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(minimumCapacity);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(growthQuantum);
|
||||
|
||||
if (requiredCapacity > currentCapacity)
|
||||
{
|
||||
_lowDemandSamples = 0;
|
||||
return RoundUp(requiredCapacity, growthQuantum);
|
||||
}
|
||||
|
||||
int budgetCapacity = checked((int)Math.Min(
|
||||
int.MaxValue,
|
||||
Math.Max(minimumCapacity, _budgetBytes / bytesPerUnit)));
|
||||
bool demandHasFallen =
|
||||
currentCapacity > budgetCapacity
|
||||
&& requiredCapacity <= budgetCapacity
|
||||
&& (requiredCapacity == 0
|
||||
|| (long)currentCapacity >= (long)requiredCapacity * 4);
|
||||
if (!demandHasFallen)
|
||||
{
|
||||
_lowDemandSamples = 0;
|
||||
return currentCapacity;
|
||||
}
|
||||
|
||||
_lowDemandSamples++;
|
||||
if (_lowDemandSamples < _lowDemandSamplesBeforeShrink)
|
||||
return currentCapacity;
|
||||
|
||||
_lowDemandSamples = 0;
|
||||
long warmedDemand = Math.Max(
|
||||
minimumCapacity,
|
||||
Math.Min(int.MaxValue, (long)requiredCapacity * 2));
|
||||
int target = RoundUp(
|
||||
checked((int)Math.Min(warmedDemand, budgetCapacity)),
|
||||
growthQuantum);
|
||||
target = Math.Max(requiredCapacity, Math.Min(target, budgetCapacity));
|
||||
return Math.Min(currentCapacity, target);
|
||||
}
|
||||
|
||||
private static int RoundUp(int value, int quantum)
|
||||
{
|
||||
if (value == 0)
|
||||
return 0;
|
||||
long rounded = ((long)value + quantum - 1) / quantum * quantum;
|
||||
return checked((int)Math.Min(int.MaxValue, rounded));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue