feat(diagnostics): complete residency pressure ledger

Complete Slice D4 by adding aggregate lifecycle occupancy and traffic facts, validating physical source reports, and including decoded audio under the typed startup budget. Exercise every domain under forced pressure and retain the real cache/fence convergence gates.
This commit is contained in:
Erik 2026-07-24 16:40:17 +02:00
parent f2644d42c2
commit 1853a57c12
15 changed files with 297 additions and 15 deletions

View file

@ -8,6 +8,14 @@ using AcDream.Core.Content;
namespace AcDream.Core.Audio;
public readonly record struct DatSoundCacheDiagnostics(
int CachedWaveCount,
long ResidentWaveBytes,
long BudgetBytes,
long Hits,
long Misses,
long Evictions);
/// <summary>
/// DatCollection-backed cache of decoded waves + SoundTable lookups.
///
@ -74,6 +82,9 @@ public sealed class DatSoundCache
private readonly LinkedList<WaveEntry> _waveLru = new();
private readonly long _maxWaveBytes;
private long _residentWaveBytes;
private long _hits;
private long _misses;
private long _evictions;
private sealed record WaveEntry(uint WaveId, WaveData Wave, long Bytes);
@ -86,7 +97,7 @@ public sealed class DatSoundCache
/// Test seam for pinning a smaller byte budget so eviction can be
/// exercised deterministically without allocating tens of megabytes.
/// </summary>
internal DatSoundCache(IDatObjectSource dats, long maxWaveBytes)
public DatSoundCache(IDatObjectSource dats, long maxWaveBytes)
{
ArgumentNullException.ThrowIfNull(dats);
ArgumentOutOfRangeException.ThrowIfLessThan(maxWaveBytes, 1);
@ -105,13 +116,19 @@ public sealed class DatSoundCache
{
if (_waveEntries.TryGetValue(waveId, out var node))
{
Interlocked.Increment(ref _hits);
Touch(node);
return node.Value.Wave;
}
}
if (_negativeWaveIds.ContainsKey(waveId))
{
Interlocked.Increment(ref _hits);
return null;
}
Interlocked.Increment(ref _misses);
Lazy<WaveData?> lazy = _inflight.GetOrAdd(
waveId,
@ -171,6 +188,23 @@ public sealed class DatSoundCache
/// </summary>
public int CachedSoundTableCount => _tables.Count;
public DatSoundCacheDiagnostics Diagnostics
{
get
{
lock (_gate)
{
return new DatSoundCacheDiagnostics(
_waveEntries.Count,
_residentWaveBytes,
_maxWaveBytes,
Interlocked.Read(ref _hits),
Interlocked.Read(ref _misses),
Interlocked.Read(ref _evictions));
}
}
}
private WaveData? DecodeUncached(uint waveId)
{
var wave = _dats.Get<Wave>(waveId);
@ -225,5 +259,6 @@ public sealed class DatSoundCache
_waveLru.Remove(node);
_waveEntries.Remove(node.Value.WaveId);
_residentWaveBytes -= node.Value.Bytes;
Interlocked.Increment(ref _evictions);
}
}