Refactor core systems and improve resource management
- Updated dependencies, including `Misaki.HighPerformance` and `TerraFX.Interop`. - Refactored `Result` struct for better error handling and chaining. - Removed `Ptr<T>` struct as it was no longer necessary. - Enhanced `Win32Utility` with `Attach` and `Dispose` methods. - Improved `ProjectService` and `AppStateMachine` with `Result` integration. - Refactored `IShaderCompiler` to support SPIR-V cross-compilation and pass-level compilation. - Standardized Direct3D12 resource management with `UniquePtr` and added `D3D12Object` base class. - Improved shader reflection validation and pipeline creation in `D3D12PipelineLibrary`. - Updated `SDLCompiler` for better error handling during shader generation. - Enhanced logging, debugging, and code readability across the codebase. - Performed general code cleanup, including unused namespace removal and naming consistency.
This commit is contained in:
@@ -20,10 +20,10 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.0" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.1.0" />
|
||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.1.2" />
|
||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.2.0" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.2.6" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="9.0.10" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.2" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.0" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Core;
|
||||
|
||||
public unsafe readonly struct Ptr<T>
|
||||
where T : unmanaged
|
||||
{
|
||||
public readonly T* value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Ptr(T* value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator T*(Ptr<T> ptr)
|
||||
{
|
||||
return ptr.value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Ptr<T>(T* value)
|
||||
{
|
||||
return new Ptr<T>(value);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,22 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Core;
|
||||
|
||||
public readonly struct Result
|
||||
{
|
||||
public readonly bool success;
|
||||
private readonly bool _isSuccess;
|
||||
private readonly string? _message;
|
||||
|
||||
public readonly string? message;
|
||||
public readonly bool IsSuccess => _isSuccess;
|
||||
public readonly bool IsFailure => !_isSuccess;
|
||||
|
||||
public readonly string? Message => _message;
|
||||
|
||||
public Result(bool success, string? message = null)
|
||||
{
|
||||
this.success = success;
|
||||
this.message = message;
|
||||
_isSuccess = success;
|
||||
_message = message;
|
||||
}
|
||||
|
||||
public static Result Success()
|
||||
@@ -17,7 +24,7 @@ public readonly struct Result
|
||||
return new Result(true);
|
||||
}
|
||||
|
||||
public static Result Fail(string? message)
|
||||
public static Result Failure(string? message = null)
|
||||
{
|
||||
return new Result(false, message);
|
||||
}
|
||||
@@ -27,23 +34,48 @@ public readonly struct Result
|
||||
return Result<T>.Success(value);
|
||||
}
|
||||
|
||||
public override string ToString() => success ? "OK" : $"Error: {message}";
|
||||
public static Result<T> Failure<T>(string? message = null)
|
||||
{
|
||||
return Result<T>.Failure(message);
|
||||
}
|
||||
|
||||
public static implicit operator bool(Result result) => result.success;
|
||||
public static Result<T, S> Create<T, S>(T value, S status)
|
||||
{
|
||||
return new Result<T, S>(value, status);
|
||||
}
|
||||
|
||||
public override string ToString() => IsSuccess ? "OK" : $"Error: {Message}";
|
||||
|
||||
public static implicit operator bool(Result result) => result.IsSuccess;
|
||||
}
|
||||
|
||||
public readonly struct Result<T>
|
||||
{
|
||||
public readonly bool success;
|
||||
public readonly T value;
|
||||
private readonly bool _isSuccess;
|
||||
private readonly T _value;
|
||||
private readonly string? _message;
|
||||
|
||||
public readonly string? message;
|
||||
public readonly bool IsSuccess => _isSuccess;
|
||||
public readonly bool IsFailure => !_isSuccess;
|
||||
|
||||
public readonly T Value => _value;
|
||||
public readonly string? Message => _message;
|
||||
|
||||
public Result(bool success, T value, string? message = null)
|
||||
{
|
||||
this.success = success;
|
||||
this.value = value;
|
||||
this.message = message;
|
||||
_isSuccess = success;
|
||||
_value = value;
|
||||
_message = message;
|
||||
}
|
||||
|
||||
public ref readonly T GetValueRef()
|
||||
{
|
||||
if (!IsSuccess)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get value from a failed Result.");
|
||||
}
|
||||
|
||||
return ref Unsafe.AsRef(in _value);
|
||||
}
|
||||
|
||||
public static Result<T> Success(T value)
|
||||
@@ -51,26 +83,52 @@ public readonly struct Result<T>
|
||||
return new Result<T>(true, value);
|
||||
}
|
||||
|
||||
public static Result<T> Fail(string? message)
|
||||
public static Result<T> Failure(string? message = null)
|
||||
{
|
||||
return new Result<T>(false, default!, message);
|
||||
}
|
||||
|
||||
public override string ToString() => success ? $"OK: {value}" : $"Error: {message}";
|
||||
public override string ToString() => IsSuccess ? $"OK: {Value}" : $"Error: {Message}";
|
||||
|
||||
public static implicit operator Result<T>(T? data) => data is not null ? Success(data) : Fail(null);
|
||||
public static implicit operator Result<T>(Result result) => result.success ? Success(default!) : Fail(result.message);
|
||||
public static implicit operator Result<T>(T? data) => data is not null ? Success(data) : Failure(null);
|
||||
public static implicit operator Result<T>(Result result) => result.IsSuccess ? Success(default!) : Failure(result.Message);
|
||||
|
||||
public static implicit operator bool(Result<T> result) => result.IsSuccess;
|
||||
public static implicit operator Result(Result<T> result) => result.IsSuccess ? Result.Success() : Result.Failure(result.Message);
|
||||
}
|
||||
|
||||
public enum ResultStatus : byte
|
||||
{
|
||||
Success,
|
||||
NotFound,
|
||||
InvalidArgument,
|
||||
InvalidState,
|
||||
InternalError,
|
||||
PermissionDenied,
|
||||
NotSupported,
|
||||
OutOfMemory,
|
||||
Timeout,
|
||||
Cancelled,
|
||||
UnknownError
|
||||
}
|
||||
|
||||
public readonly struct Result<T, S>
|
||||
{
|
||||
public readonly T value;
|
||||
public readonly S status;
|
||||
private readonly T _value;
|
||||
private readonly S _status;
|
||||
|
||||
public T Value => _value;
|
||||
public S Status => _status;
|
||||
|
||||
public Result(T value, S status)
|
||||
{
|
||||
this.value = value;
|
||||
this.status = status;
|
||||
_value = value;
|
||||
_status = status;
|
||||
}
|
||||
|
||||
public ref readonly T GetValueRef()
|
||||
{
|
||||
return ref Unsafe.AsRef(in _value);
|
||||
}
|
||||
|
||||
public static Result<T, S> Create(T value, S status)
|
||||
@@ -78,26 +136,71 @@ public readonly struct Result<T, S>
|
||||
return new Result<T, S>(value, status);
|
||||
}
|
||||
|
||||
public override string ToString() => $"Value: {value}, Status: {status}";
|
||||
public override string ToString() => $"Value: {_value}, Status: {_status}";
|
||||
}
|
||||
|
||||
public static class ResultExtensions
|
||||
{
|
||||
public static void ThrowIfFailed(this Result result)
|
||||
{
|
||||
if (!result.success)
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
throw new InvalidOperationException($"Operation failed: {result.message}");
|
||||
throw new InvalidOperationException($"Operation failed: {result.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static T GetValueOrThrow<T>(this Result<T> result)
|
||||
{
|
||||
if (!result.success)
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
throw new InvalidOperationException($"Operation failed: {result.message}");
|
||||
throw new InvalidOperationException($"Operation failed: {result.Message}");
|
||||
}
|
||||
|
||||
return result.value;
|
||||
return result.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public static T? GetValueOrDefault<T>(this Result<T> result, T? defaultValue = default)
|
||||
{
|
||||
return result.IsSuccess ? result.Value : defaultValue;
|
||||
}
|
||||
|
||||
public static Result OnSuccess(this Result result, Action action)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<T> OnSuccess<T>(this Result<T> result, Action<T> action)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
action(result.Value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result OnFailed(this Result result, Action<string?> action)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
{
|
||||
action(result.Message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<T> OnFailed<T>(this Result<T> result, Action<string?> action)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
{
|
||||
action(result.Message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,32 @@ internal static unsafe partial class Win32Utility
|
||||
return new IID_PPV(Windows.__uuidof<T>(), comPtr.PPV());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Attach<T>(ref this UniquePtr<T> uPtr, T* other)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
var ptr = uPtr.Get();
|
||||
if (ptr != null)
|
||||
{
|
||||
var refCount = ptr->Release();
|
||||
Debug.Assert((refCount != 0) || (ptr != other));
|
||||
}
|
||||
|
||||
uPtr = new UniquePtr<T>(other);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Dispose<T>(ref this UniquePtr<T> uPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
T* ptr = uPtr.Get();
|
||||
if (ptr != null)
|
||||
{
|
||||
uPtr = default;
|
||||
MemoryLeakException.ThrowIfRefCountNonZero(ptr->Release());
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Assert(this HRESULT hr)
|
||||
@@ -50,9 +76,14 @@ internal static unsafe partial class Win32Utility
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ThrowIfFailed(this HRESULT hr)
|
||||
public static Result ToResult(this HRESULT hr, [CallerArgumentExpression(nameof(hr))] string? op = null)
|
||||
{
|
||||
Windows.ThrowIfFailed(hr);
|
||||
if (hr.SUCCEEDED)
|
||||
{
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
return Result.Failure($"{op} failed with code {hr}");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -102,8 +133,6 @@ internal static unsafe partial class Win32Utility
|
||||
extension(MemoryLeakException)
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Conditional("DEBUG")]
|
||||
[Conditional("GHOST_EDITOR")]
|
||||
public static void ThrowIfRefCountNonZero(uint count)
|
||||
{
|
||||
if (count != 0)
|
||||
|
||||
Reference in New Issue
Block a user