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:
parent
f2644d42c2
commit
1853a57c12
15 changed files with 297 additions and 15 deletions
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue