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

@ -433,7 +433,15 @@ public sealed class ContentEffectsAudioCompositionTests
EntityEffectPoseRegistry poses) => new(lighting, poses);
public TranslucencyHookSink CreateTranslucencySink(
TranslucencyFadeManager fades) => new(fades);
public DatSoundCache CreateSoundCache(IDatReaderWriter dats) => new(dats);
public DatSoundCache CreateSoundCache(
IDatReaderWriter dats,
long maximumDecodedBytes)
{
Assert.Equal(
ResidencyBudgetOptions.Default.AudioBytes,
maximumDecodedBytes);
return new DatSoundCache(dats, maximumDecodedBytes);
}
public OpenAlAudioEngine CreateAudioEngine()
{

View file

@ -308,7 +308,8 @@ public sealed class WorldRenderCompositionTests
WbMeshAdapter meshes,
TextureCache textures,
IPreparedAssetSource preparedAssets,
IAnimationLoader animations)
IAnimationLoader animations,
AcDream.Core.Audio.DatSoundCache? audio)
{
RegisteredResidency = manager;
}

View file

@ -1,6 +1,7 @@
using System.Text.Json;
using AcDream.App.Diagnostics;
using AcDream.App.Rendering;
using AcDream.App.Rendering.Residency;
using AcDream.App.Streaming;
using AcDream.App.UI.Testing;
using SixLabors.ImageSharp;
@ -114,6 +115,20 @@ public sealed class WorldLifecycleAutomationControllerTests
LoadedLandblocks = 1,
WorldEntities = 42,
TrackedGpuBytes = 1234,
Residency = new ResidencySnapshot(
[
new ResidencyDomainSnapshot(
ResidencyDomain.Animations,
EntryCount: 2,
OwnerCount: 0,
Charges: new ResidencyCharges(
DecodedBytes: 1024),
BudgetBytes: 4096,
Hits: 8,
Misses: 3,
Evictions: 1),
],
new ResidencyCharges(DecodedBytes: 1024)),
};
var screenshots = new FrameScreenshotController(
(_, _) => [0, 0, 0, 255],
@ -151,6 +166,18 @@ public sealed class WorldLifecycleAutomationControllerTests
Assert.True(json.RootElement.GetProperty("render").GetProperty("presentation")
.GetProperty("screenshotCaptured").GetBoolean());
Assert.Equal(42, json.RootElement.GetProperty("resources").GetProperty("worldEntities").GetInt32());
JsonElement residency = json.RootElement
.GetProperty("resources")
.GetProperty("residency");
Assert.Equal(
4096,
residency.GetProperty("totalBudgetBytes").GetInt64());
Assert.Equal(8, residency.GetProperty("totalHits").GetInt64());
Assert.Equal(
1024,
residency.GetProperty("totalCharges")
.GetProperty("decodedBytes")
.GetInt64());
Assert.True(File.Exists(Path.Combine(directory, "checkpoint-dungeon.json")));
}
finally

View file

@ -393,6 +393,90 @@ public sealed class ResidencyManagerTests
16,
snapshot.Get(ResidencyDomain.StandaloneTextures)
.FragmentedFreeBytes);
Assert.Equal(256, snapshot.TotalBudgetBytes);
Assert.Equal(128, snapshot.TotalCapacityBytes);
Assert.Equal(96, snapshot.TotalUsedBytes);
Assert.Equal(16, snapshot.TotalFragmentedFreeBytes);
Assert.Equal(3, snapshot.TotalEntries);
Assert.Equal(2, snapshot.TotalOwners);
}
[Fact]
public void ForcedPressureAcrossEveryDomainConvergesToZero()
{
const long budget = 64;
var manager = new ResidencyManager();
var current = new Dictionary<
ResidencyDomain,
ResidencyDomainSnapshot>();
foreach (ResidencyDomain domain in Enum.GetValues<ResidencyDomain>())
{
current[domain] = new ResidencyDomainSnapshot(
domain,
EntryCount: 2,
OwnerCount: 1,
Charges: new ResidencyCharges(
CpuPreparedBytes: budget * 2),
BudgetBytes: budget,
Hits: 3,
Misses: 2,
Evictions: 1);
ResidencyDomain capturedDomain = domain;
manager.RegisterDomainSource(new DelegateResidencyDomainSource(
capturedDomain,
() => current[capturedDomain]));
}
ResidencySnapshot pressured = manager.CaptureSnapshot();
Assert.Equal(
Enum.GetValues<ResidencyDomain>().Length,
pressured.Domains.Count);
Assert.All(
pressured.Domains,
domain => Assert.True(
domain.Charges.CommittedCpuBytes > domain.BudgetBytes));
Assert.Equal(
budget * 2 * pressured.Domains.Count,
pressured.TotalCharges.CommittedCpuBytes);
foreach (ResidencyDomain domain in Enum.GetValues<ResidencyDomain>())
{
current[domain] = new ResidencyDomainSnapshot(
domain,
EntryCount: 0,
OwnerCount: 0,
Charges: ResidencyCharges.Zero,
BudgetBytes: budget,
Hits: 3,
Misses: 2,
Evictions: 3);
}
ResidencySnapshot drained = manager.CaptureSnapshot();
Assert.True(drained.TotalCharges.IsZero);
Assert.Equal(0, drained.TotalEntries);
Assert.Equal(0, drained.TotalOwners);
Assert.Equal(budget * drained.Domains.Count, drained.TotalBudgetBytes);
Assert.Equal(30, drained.TotalEvictions);
}
[Fact]
public void DomainSourceRejectsImpossibleAllocatorFacts()
{
var source = new DelegateResidencyDomainSource(
ResidencyDomain.GlobalMeshArena,
() => new ResidencyDomainSnapshot(
ResidencyDomain.GlobalMeshArena,
EntryCount: 1,
OwnerCount: 0,
Charges: ResidencyCharges.Zero,
CapacityBytes: 64,
UsedBytes: 65));
Assert.Throws<InvalidOperationException>(
() => source.CaptureResidency());
}
[Fact]

View file

@ -31,6 +31,7 @@ public sealed class RuntimeOptionsTests
["ACDREAM_RESIDENCY_MESH_UNOWNED_ENTRIES"] = "72",
["ACDREAM_RESIDENCY_ANIMATION_MIB"] = "48",
["ACDREAM_RESIDENCY_ANIMATION_ENTRIES"] = "300",
["ACDREAM_RESIDENCY_AUDIO_MIB"] = "24",
};
RuntimeOptions options = RuntimeOptions.Parse(
@ -47,6 +48,9 @@ public sealed class RuntimeOptionsTests
48 * ResidencyBudgetOptions.MiB,
options.ResidencyBudgets.AnimationBytes);
Assert.Equal(300, options.ResidencyBudgets.AnimationEntries);
Assert.Equal(
24 * ResidencyBudgetOptions.MiB,
options.ResidencyBudgets.AudioBytes);
}
[Theory]

View file

@ -110,6 +110,14 @@ public sealed class DatSoundCacheTests
Assert.NotNull(b2);
Assert.NotSame(b1, b2); // evicted entries decode fresh on next request
Assert.Equal(2, dats.GetCallCount(2));
DatSoundCacheDiagnostics diagnostics = cache.Diagnostics;
Assert.Equal(2, diagnostics.CachedWaveCount);
Assert.Equal(300, diagnostics.ResidentWaveBytes);
Assert.Equal(300, diagnostics.BudgetBytes);
Assert.True(diagnostics.Hits >= 3);
Assert.True(diagnostics.Misses >= 4);
Assert.True(diagnostics.Evictions >= 2);
}
[Fact]