Refactor asset streaming, error handling, and unit tests
- Add new compile-time constants and update package versions - Refactor AssetEntry upload logic to return Result and propagate errors - Enhance error handling in ResourceStreamingProcessor uploads - Make ResourceStreamingContext a readonly struct - Implement IDisposable and finalizers for resource cleanup - Overhaul AssetManagerTest with async tests and improved mocks - Add mock implementations for graphics interfaces for testing - Refactor MockingCommandBuffer and MockingResourceDatabase for better simulation - Update internals visibility for unit testing
This commit is contained in:
@@ -8,26 +8,26 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DefineConstants>$(DefineConstants);MHP_ENABLE_SAFETY_CHECKS;MHP_ENABLE_MIMALLOC</DefineConstants>
|
||||
<DefineConstants>$(DefineConstants);MHP_ENABLE_SAFETY_CHECKS;MHP_ENABLE_STACKTRACE;MHP_ENABLE_MIMALLOC;MHP_FASTMATH</DefineConstants>
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DefineConstants>$(DefineConstants);MHP_ENABLE_MIMALLOC</DefineConstants>
|
||||
<DefineConstants>$(DefineConstants);MHP_ENABLE_MIMALLOC;MHP_FASTMATH</DefineConstants>
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.9" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="3.1.3" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="3.1.5" />
|
||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.6.17">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.3.3" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics.SPMD" Version="1.3.4" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics.SPMD" Version="1.3.5" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.7" />
|
||||
<PackageReference Include="TerraFX.Interop.Mimalloc" Version="1.6.7.2" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.6" />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Jobs;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
@@ -54,7 +53,7 @@ internal partial class AssetEntry
|
||||
{
|
||||
private static readonly Action<AssetEntry>[] s_onCreation = new Action<AssetEntry>[(int)AssetType.Unknown + 1];
|
||||
private static readonly Func<AssetEntry, Result>[] s_onParseRawData = new Func<AssetEntry, Result>[(int)AssetType.Unknown + 1];
|
||||
private static readonly Action<AssetEntry, ResourceStreamingContext>[] s_onRecordUpload = new Action<AssetEntry, ResourceStreamingContext>[(int)AssetType.Unknown + 1];
|
||||
private static readonly Func<AssetEntry, ResourceStreamingContext, Result>[] s_onRecordUpload = new Func<AssetEntry, ResourceStreamingContext, Result>[(int)AssetType.Unknown + 1];
|
||||
private static readonly Action<AssetEntry, ResourceStreamingContext>[] s_onUploadComplete = new Action<AssetEntry, ResourceStreamingContext>[(int)AssetType.Unknown + 1];
|
||||
private static readonly Action<AssetEntry>[] s_onReleaseResource = new Action<AssetEntry>[(int)AssetType.Unknown + 1];
|
||||
|
||||
@@ -184,9 +183,9 @@ internal unsafe partial class AssetEntry
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnRecordUploadCommands(ResourceStreamingContext context)
|
||||
public Result OnRecordUploadCommands(ResourceStreamingContext context)
|
||||
{
|
||||
s_onRecordUpload[(int)_assetType]?.Invoke(this, context);
|
||||
return s_onRecordUpload[(int)_assetType]?.Invoke(this, context) ?? Result.Failure("Unsupported asset type.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -417,10 +416,10 @@ internal partial class AssetManager : IDisposable
|
||||
|
||||
if (Interlocked.CompareExchange(ref entry.StateValue, (int)AssetState.Scheduled, (int)AssetState.Ready) == (int)AssetState.Ready)
|
||||
{
|
||||
// Entry is in Ready state — the old texture is valid and will remain visible.
|
||||
// Go directly to Scheduled → Loading → Loaded → Uploading → Ready again.
|
||||
// Entry is in Ready state - the old texture is valid and will remain visible.
|
||||
// Go directly to Scheduled -> Loading -> Loaded -> Uploading -> Ready again.
|
||||
// The swap cycle in RecordTextureUpload/OnTextureUploadComplete handles the
|
||||
// v1 → v2 transition exactly like the fallback → v1 transition.
|
||||
// v1 to v2 transition exactly like the fallback to v1 transition.
|
||||
EnsureScheduled(entry);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -62,17 +62,26 @@ internal class ResourceStreamingProcessor : IResourceStreamingProcessor
|
||||
}
|
||||
|
||||
// Record copy commands into cmdCopy
|
||||
entry.OnRecordUploadCommands(context);
|
||||
if (entry.OnRecordUploadCommands(context).IsFailure)
|
||||
{
|
||||
Logger.Error($"Failed to record upload commands for asset {entry.AssetId}. Skipping upload.");
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.State = AssetState.Uploading;
|
||||
|
||||
_pendingFinalize.Enqueue(entry);
|
||||
uploadCount++;
|
||||
}
|
||||
|
||||
var result = context.CopyPipeline.End();
|
||||
|
||||
// 3. Submit the batch
|
||||
if (uploadCount > 0 && result.IsSuccess)
|
||||
if (context.CopyPipeline.End().IsFailure)
|
||||
{
|
||||
Logger.Error("Failed to submit copy command list for resource streaming.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (uploadCount > 0)
|
||||
{
|
||||
_pendingCopyFenceValue = context.CopyPipeline.SignaledFenceValue();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,6 @@ using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
||||
[assembly: InternalsVisibleTo("Ghost.Editor.Core")]
|
||||
[assembly: InternalsVisibleTo("Ghost.Graphics.Test")]
|
||||
[assembly: InternalsVisibleTo("Ghost.Graphics.Test-Winui")]
|
||||
[assembly: InternalsVisibleTo("Ghost.UnitTest")]
|
||||
|
||||
[assembly: EngineAssembly]
|
||||
@@ -3,7 +3,7 @@ using Ghost.Graphics.Services;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
|
||||
internal ref struct ResourceStreamingContext
|
||||
internal readonly struct ResourceStreamingContext
|
||||
{
|
||||
public required AsyncCopyPipeline CopyPipeline
|
||||
{
|
||||
|
||||
@@ -475,8 +475,12 @@ public class RenderSystem : IDisposable
|
||||
|
||||
_renderPipeline.Dispose();
|
||||
|
||||
_fence.Dispose();
|
||||
|
||||
_shaderLibrary.Dispose();
|
||||
_resourceManager.Dispose();
|
||||
_swapChainManager.Dispose();
|
||||
_asyncCopyPipeline.Dispose();
|
||||
|
||||
_graphicsEngine.Dispose();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.Services;
|
||||
|
||||
public class AsyncCopyPipeline
|
||||
public class AsyncCopyPipeline : IDisposable
|
||||
{
|
||||
private readonly IRenderDevice _device;
|
||||
|
||||
@@ -26,6 +26,11 @@ public class AsyncCopyPipeline
|
||||
_fence.Name = "AsyncCopyPipeline_Fence";
|
||||
}
|
||||
|
||||
~AsyncCopyPipeline()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
internal void Begin()
|
||||
{
|
||||
_commandAllocator.Reset();
|
||||
@@ -74,4 +79,13 @@ public class AsyncCopyPipeline
|
||||
{
|
||||
return _fence.WaitForValueAsync(_fenceValue);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_commandAllocator.Dispose();
|
||||
_commandBuffer.Dispose();
|
||||
_fence.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,6 +592,7 @@ public sealed partial class ResourceManager : IDisposable
|
||||
_meshes.Dispose();
|
||||
_materials.Dispose();
|
||||
_shaders.Dispose();
|
||||
_computeShaders.Dispose();
|
||||
_materialPalettes.Dispose();
|
||||
|
||||
_resourceDatabase.ReleaseResource(_paletteOffsetBuffer.AsResource());
|
||||
|
||||
Reference in New Issue
Block a user