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:
2026-05-03 17:05:52 +09:00
parent e7fedfd35a
commit d2bf2f12a2
16 changed files with 346 additions and 40 deletions

View File

@@ -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

View File

@@ -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();
}