perf(render): consume prepared mesh package at runtime

This commit is contained in:
Erik 2026-07-24 15:07:25 +02:00
parent c42f93b323
commit f05afc07c1
22 changed files with 328 additions and 370 deletions

View file

@ -36,6 +36,9 @@ public sealed class ContentEffectsAudioCompositionTests
Enum.GetValues<ContentEffectsAudioCompositionPoint>(),
fixture.Points);
Assert.Same(fixture.Publication.Dats, result.Dats);
Assert.Same(fixture.Publication.PreparedAssets, result.PreparedAssets);
Assert.Equal("test.pak", fixture.Factory.PreparedAssetPath);
Assert.Same(result.Dats, fixture.Factory.PreparedAssetDats);
Assert.Same(fixture.Publication.Magic, result.MagicCatalog);
Assert.Same(fixture.Publication.Animations, result.AnimationLoader);
Assert.Same(fixture.Publication.Particles, result.ParticleSystem);
@ -167,6 +170,70 @@ public sealed class ContentEffectsAudioCompositionTests
StringComparison.Ordinal);
}
[Fact]
public void ProductionRendererConsumesOnlyThePublishedPreparedAssetSource()
{
string root = FindRepoRoot();
string contentPhase = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Composition",
"ContentEffectsAudioComposition.cs"));
string worldPhase = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Composition",
"WorldRenderComposition.cs"));
string manager = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Rendering",
"Wb",
"ObjectMeshManager.cs"));
string adapter = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Rendering",
"Wb",
"WbMeshAdapter.cs"));
string lifetime = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Rendering",
"GameWindowLifetime.cs"));
Assert.Contains("new PakPreparedAssetSource(path, dats, diagnostic)",
contentPhase, StringComparison.Ordinal);
Assert.Contains("content.PreparedAssets", worldPhase,
StringComparison.Ordinal);
Assert.Contains("_preparedAssets.Read(request.Asset, ct)", manager,
StringComparison.Ordinal);
Assert.DoesNotContain("MeshExtractor", manager,
StringComparison.Ordinal);
Assert.DoesNotContain("IDatReaderWriter", manager,
StringComparison.Ordinal);
Assert.DoesNotContain("GfxObjMesh.Build", adapter,
StringComparison.Ordinal);
int meshStage = lifetime.IndexOf(
"new ResourceShutdownStage(\"mesh adapter\"",
StringComparison.Ordinal);
int preparedRelease = lifetime.IndexOf(
"Hard(\"prepared asset source\"",
StringComparison.Ordinal);
int datRelease = lifetime.IndexOf(
"Hard(\"DAT collection\"",
StringComparison.Ordinal);
Assert.True(meshStage >= 0);
Assert.True(preparedRelease > meshStage);
Assert.True(datRelease > preparedRelease);
}
private sealed class Fixture : IDisposable
{
private readonly ContentEffectsAudioCompositionPoint? _failurePoint;
@ -185,6 +252,7 @@ public sealed class ContentEffectsAudioCompositionTests
typeof(HostInputCameraResult));
Dependencies = new ContentEffectsAudioDependencies(
"test-dat",
"test.pak",
new PhysicsDataCache(),
false,
new Spellbook(),
@ -227,6 +295,7 @@ public sealed class ContentEffectsAudioCompositionTests
IDisposable
{
public IDatReaderWriter? Dats { get; private set; }
public IPreparedAssetSource? PreparedAssets { get; private set; }
public MagicCatalog? Magic { get; private set; }
public IAnimationLoader? Animations { get; private set; }
public LiveEntityCollisionBuilder? Collision { get; private set; }
@ -242,6 +311,8 @@ public sealed class ContentEffectsAudioCompositionTests
public ContentAudioGraph? Audio { get; private set; }
public void PublishDatCollection(IDatReaderWriter value) => Dats = Once(Dats, value);
public void PublishPreparedAssetSource(IPreparedAssetSource value) =>
PreparedAssets = Once(PreparedAssets, value);
public void PublishMagicCatalog(MagicCatalog value) => Magic = Once(Magic, value);
public void PublishAnimationLoader(IAnimationLoader value) =>
Animations = Once(Animations, value);
@ -273,6 +344,8 @@ public sealed class ContentEffectsAudioCompositionTests
Registrations = null;
Audio?.Engine.Dispose();
Audio = null;
PreparedAssets?.Dispose();
PreparedAssets = null;
Dats?.Dispose();
Dats = null;
}
@ -292,11 +365,24 @@ public sealed class ContentEffectsAudioCompositionTests
private readonly MagicCatalog _magic =
(MagicCatalog)RuntimeHelpers.GetUninitializedObject(typeof(MagicCatalog));
private readonly IAnimationLoader _animations = new NullAnimationLoader();
private readonly IPreparedAssetSource _preparedAssets =
new NullPreparedAssetSource();
public int AudioFactoryCalls { get; private set; }
public OpenAlAudioEngine? LastAudioEngine { get; private set; }
public string? PreparedAssetPath { get; private set; }
public IDatReaderWriter? PreparedAssetDats { get; private set; }
public IDatReaderWriter OpenDatCollection(string datDirectory) => _dats;
public IPreparedAssetSource OpenPreparedAssetSource(
string path,
IDatReaderWriter dats,
Action<string> diagnostic)
{
PreparedAssetPath = path;
PreparedAssetDats = dats;
return _preparedAssets;
}
public MagicCatalog LoadMagicCatalog(IDatReaderWriter dats) => _magic;
public void InstallSpellMetadata(Spellbook spellBook, MagicCatalog catalog) { }
public int GetSpellCount(MagicCatalog catalog) => 0;
@ -369,6 +455,26 @@ public sealed class ContentEffectsAudioCompositionTests
public Animation? LoadAnimation(uint id) => null;
}
private sealed class NullPreparedAssetSource : IPreparedAssetSource
{
public PreparedAssetSourceStats Stats => default;
public CacheStats DecodedTextureCacheStats => default;
public PreparedAssetPresence Probe(
AcDream.Content.Pak.PakAssetType type,
uint sourceFileId) =>
PreparedAssetPresence.Missing;
public PreparedAssetReadResult Read(
in PreparedAssetRequest request,
CancellationToken cancellationToken = default) =>
PreparedAssetReadResult.Missing;
public void Dispose()
{
}
}
private sealed class AudioApiFactory(IOpenAlResourceApi api)
: IOpenAlResourceApiFactory
{