perf(render): consume prepared mesh package at runtime
This commit is contained in:
parent
c42f93b323
commit
f05afc07c1
22 changed files with 328 additions and 370 deletions
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@ public sealed class WorldRenderCompositionTests
|
|||
public WbMeshAdapter CreateMeshAdapter(
|
||||
GL gl,
|
||||
IDatReaderWriter dats,
|
||||
IPreparedAssetSource preparedAssets,
|
||||
IGpuResourceRetirementQueue retirement) =>
|
||||
Resource<WbMeshAdapter>("WB mesh adapter");
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ public static class ObjectMeshDataEquality {
|
|||
Assert.True(expected.Indices.SequenceEqual(actual.Indices),
|
||||
$"{path}.Indices: expected [{string.Join(",", expected.Indices)}], got [{string.Join(",", actual.Indices)}]");
|
||||
Assert.True(expected.CullMode == actual.CullMode, $"{path}.CullMode: expected {expected.CullMode}, got {actual.CullMode}");
|
||||
Assert.True(expected.Translucency == actual.Translucency,
|
||||
$"{path}.Translucency: expected {expected.Translucency}, got {actual.Translucency}");
|
||||
Assert.True(expected.IsTransparent == actual.IsTransparent, $"{path}.IsTransparent: expected {expected.IsTransparent}, got {actual.IsTransparent}");
|
||||
Assert.True(expected.IsAdditive == actual.IsAdditive, $"{path}.IsAdditive: expected {expected.IsAdditive}, got {actual.IsAdditive}");
|
||||
Assert.True(expected.HasWrappingUVs == actual.HasWrappingUVs, $"{path}.HasWrappingUVs: expected {expected.HasWrappingUVs}, got {actual.HasWrappingUVs}");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Numerics;
|
||||
using AcDream.Content.Pak;
|
||||
using AcDream.Core.Meshing;
|
||||
using Chorizite.Core.Lib;
|
||||
using Chorizite.Core.Render.Enums;
|
||||
using DatReaderWriter.DBObjs;
|
||||
|
|
@ -59,6 +60,7 @@ public class ObjectMeshDataSerializerTests {
|
|||
UploadPixelType = AcDream.Content.UploadPixelType.UnsignedByte,
|
||||
Indices = new List<ushort> { 0, 1, 2, 2, 3, 0 },
|
||||
CullMode = CullMode.CounterClockwise,
|
||||
Translucency = TranslucencyKind.InvAlpha,
|
||||
IsTransparent = true,
|
||||
IsAdditive = false,
|
||||
HasWrappingUVs = true,
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.Core.Meshing;
|
||||
|
||||
namespace AcDream.Core.Tests.Rendering.Wb;
|
||||
|
||||
public sealed class AcSurfaceMetadataTableTests
|
||||
{
|
||||
[Fact]
|
||||
public void Add_ThenLookup_RoundTripsSameMetadata()
|
||||
{
|
||||
var table = new AcSurfaceMetadataTable();
|
||||
var meta = new AcSurfaceMetadata(
|
||||
Translucency: TranslucencyKind.AlphaBlend,
|
||||
Luminosity: 0.5f,
|
||||
Diffuse: 0.8f,
|
||||
SurfOpacity: 0.7f,
|
||||
NeedsUvRepeat: true,
|
||||
DisableFog: false);
|
||||
|
||||
table.Add(gfxObjId: 0x01000123ul, surfaceIdx: 2, meta);
|
||||
|
||||
Assert.True(table.TryLookup(0x01000123ul, 2, out var got));
|
||||
Assert.Equal(meta, got);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lookup_MissingKey_ReturnsFalse()
|
||||
{
|
||||
var table = new AcSurfaceMetadataTable();
|
||||
Assert.False(table.TryLookup(0xDEADBEEFul, 0, out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_OverwritesPreviousMetadata()
|
||||
{
|
||||
var table = new AcSurfaceMetadataTable();
|
||||
var first = new AcSurfaceMetadata(TranslucencyKind.Opaque, 0f, 1f, 1f, false, false);
|
||||
var second = new AcSurfaceMetadata(TranslucencyKind.Additive, 1f, 1f, 1f, false, true);
|
||||
|
||||
table.Add(0xAAAA, 0, first);
|
||||
table.Add(0xAAAA, 0, second);
|
||||
|
||||
Assert.True(table.TryLookup(0xAAAA, 0, out var got));
|
||||
Assert.Equal(second, got);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_FromMultipleThreads_IsThreadSafe()
|
||||
{
|
||||
var table = new AcSurfaceMetadataTable();
|
||||
var threads = new System.Threading.Tasks.Task[8];
|
||||
for (int t = 0; t < 8; t++)
|
||||
{
|
||||
int threadIdx = t;
|
||||
threads[t] = System.Threading.Tasks.Task.Run(() =>
|
||||
{
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
ulong key = (ulong)(threadIdx * 1000 + i);
|
||||
table.Add(key, 0, new AcSurfaceMetadata(
|
||||
TranslucencyKind.Opaque, 0f, 1f, 1f, false, false));
|
||||
}
|
||||
});
|
||||
}
|
||||
System.Threading.Tasks.Task.WaitAll(threads);
|
||||
|
||||
// 8000 entries should be present.
|
||||
for (int t = 0; t < 8; t++)
|
||||
for (int i = 0; i < 1000; i++)
|
||||
Assert.True(table.TryLookup((ulong)(t * 1000 + i), 0, out _));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue