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>
|
<ItemGroup>
|
||||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.0" />
|
<PackageReference Include="Misaki.HighPerformance" Version="1.0.0" />
|
||||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.1.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="Misaki.HighPerformance.Mathematics" Version="1.2.6" />
|
||||||
<PackageReference Include="System.IO.Hashing" Version="9.0.10" />
|
<PackageReference Include="System.IO.Hashing" Version="10.0.0" />
|
||||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.2" />
|
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</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;
|
namespace Ghost.Core;
|
||||||
|
|
||||||
public readonly struct Result
|
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)
|
public Result(bool success, string? message = null)
|
||||||
{
|
{
|
||||||
this.success = success;
|
_isSuccess = success;
|
||||||
this.message = message;
|
_message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result Success()
|
public static Result Success()
|
||||||
@@ -17,7 +24,7 @@ public readonly struct Result
|
|||||||
return new Result(true);
|
return new Result(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result Fail(string? message)
|
public static Result Failure(string? message = null)
|
||||||
{
|
{
|
||||||
return new Result(false, message);
|
return new Result(false, message);
|
||||||
}
|
}
|
||||||
@@ -27,23 +34,48 @@ public readonly struct Result
|
|||||||
return Result<T>.Success(value);
|
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 struct Result<T>
|
||||||
{
|
{
|
||||||
public readonly bool success;
|
private readonly bool _isSuccess;
|
||||||
public readonly T value;
|
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)
|
public Result(bool success, T value, string? message = null)
|
||||||
{
|
{
|
||||||
this.success = success;
|
_isSuccess = success;
|
||||||
this.value = value;
|
_value = value;
|
||||||
this.message = message;
|
_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)
|
public static Result<T> Success(T value)
|
||||||
@@ -51,26 +83,52 @@ public readonly struct Result<T>
|
|||||||
return new Result<T>(true, value);
|
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);
|
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>(T? data) => data is not null ? Success(data) : Failure(null);
|
||||||
public static implicit operator Result<T>(Result result) => result.success ? Success(default!) : Fail(result.message);
|
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 struct Result<T, S>
|
||||||
{
|
{
|
||||||
public readonly T value;
|
private readonly T _value;
|
||||||
public readonly S status;
|
private readonly S _status;
|
||||||
|
|
||||||
|
public T Value => _value;
|
||||||
|
public S Status => _status;
|
||||||
|
|
||||||
public Result(T value, S status)
|
public Result(T value, S status)
|
||||||
{
|
{
|
||||||
this.value = value;
|
_value = value;
|
||||||
this.status = status;
|
_status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref readonly T GetValueRef()
|
||||||
|
{
|
||||||
|
return ref Unsafe.AsRef(in _value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result<T, S> Create(T value, S status)
|
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);
|
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 class ResultExtensions
|
||||||
{
|
{
|
||||||
public static void ThrowIfFailed(this Result result)
|
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)
|
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());
|
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")]
|
[Conditional("DEBUG")]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Assert(this HRESULT hr)
|
public static void Assert(this HRESULT hr)
|
||||||
@@ -50,9 +76,14 @@ internal static unsafe partial class Win32Utility
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -102,8 +133,6 @@ internal static unsafe partial class Win32Utility
|
|||||||
extension(MemoryLeakException)
|
extension(MemoryLeakException)
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[Conditional("DEBUG")]
|
|
||||||
[Conditional("GHOST_EDITOR")]
|
|
||||||
public static void ThrowIfRefCountNonZero(uint count)
|
public static void ThrowIfRefCountNonZero(uint count)
|
||||||
{
|
{
|
||||||
if (count != 0)
|
if (count != 0)
|
||||||
|
|||||||
@@ -75,26 +75,26 @@ internal partial class ProjectService
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(projectDirectory) || !Directory.Exists(projectDirectory))
|
if (string.IsNullOrWhiteSpace(projectDirectory) || !Directory.Exists(projectDirectory))
|
||||||
{
|
{
|
||||||
return Result<ProjectMetadataInfo>.Fail("Project directory is invalid or does not exist.");
|
return Result<ProjectMetadataInfo>.Failure("Project directory is invalid or does not exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var projectAssetsPath = Path.Combine(projectDirectory, ASSETS_FOLDER);
|
var projectAssetsPath = Path.Combine(projectDirectory, ASSETS_FOLDER);
|
||||||
var projectConfigPath = Path.Combine(projectDirectory, CONFIG_FOLDER);
|
var projectConfigPath = Path.Combine(projectDirectory, CONFIG_FOLDER);
|
||||||
if (!Directory.Exists(projectAssetsPath) || !Directory.Exists(projectConfigPath))
|
if (!Directory.Exists(projectAssetsPath) || !Directory.Exists(projectConfigPath))
|
||||||
{
|
{
|
||||||
return Result<ProjectMetadataInfo>.Fail("Project folder structure is invalid.");
|
return Result<ProjectMetadataInfo>.Failure("Project folder structure is invalid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var metadataPath = Directory.GetFiles(projectDirectory, $"*.{ProjectMetadata.PROJECT_EXTENSION}", SearchOption.TopDirectoryOnly).FirstOrDefault();
|
var metadataPath = Directory.GetFiles(projectDirectory, $"*.{ProjectMetadata.PROJECT_EXTENSION}", SearchOption.TopDirectoryOnly).FirstOrDefault();
|
||||||
if (string.IsNullOrWhiteSpace(metadataPath) || !File.Exists(metadataPath))
|
if (string.IsNullOrWhiteSpace(metadataPath) || !File.Exists(metadataPath))
|
||||||
{
|
{
|
||||||
return Result<ProjectMetadataInfo>.Fail("Project metadata file not found.");
|
return Result<ProjectMetadataInfo>.Failure("Project metadata file not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var metadata = await LoadMetadataAsync(metadataPath);
|
var metadata = await LoadMetadataAsync(metadataPath);
|
||||||
if (metadata == null)
|
if (metadata == null)
|
||||||
{
|
{
|
||||||
return Result<ProjectMetadataInfo>.Fail("Project metadata file is corrupted or invalid.");
|
return Result<ProjectMetadataInfo>.Failure("Project metadata file is corrupted or invalid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ProjectMetadataInfo(metadataPath, metadata);
|
return new ProjectMetadataInfo(metadataPath, metadata);
|
||||||
@@ -187,7 +187,7 @@ internal partial class ProjectService
|
|||||||
// Check if folder is empty
|
// Check if folder is empty
|
||||||
if (Directory.EnumerateFiles(projectPath, "*", SearchOption.AllDirectories).Any())
|
if (Directory.EnumerateFiles(projectPath, "*", SearchOption.AllDirectories).Any())
|
||||||
{
|
{
|
||||||
return new(false, default, "Directory is not empty");
|
return Result.Failure("Directory is not empty");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,28 +197,28 @@ internal partial class ProjectService
|
|||||||
await SetupRequestFolderAsync(projectPath, templatePath);
|
await SetupRequestFolderAsync(projectPath, templatePath);
|
||||||
|
|
||||||
var info = await AddProjectAsync(projectName, metadataPath);
|
var info = await AddProjectAsync(projectName, metadataPath);
|
||||||
return new(true, new(metadataPath, metadata));
|
return Result.Success(new ProjectMetadataInfo(metadataPath, metadata));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
return Result<ProjectMetadataInfo>.Fail($"Failed to create project: {e.Message}");
|
return Result.Failure($"Failed to create project: {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<ProjectMetadataInfo>> AddProjectFromDirectoryAsync(string projectDirectory)
|
public async Task<Result<ProjectMetadataInfo>> AddProjectFromDirectoryAsync(string projectDirectory)
|
||||||
{
|
{
|
||||||
var result = await ValidateProjectDirectoryAsync(projectDirectory);
|
var result = await ValidateProjectDirectoryAsync(projectDirectory);
|
||||||
if (!result.success)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await HasProjectAsync(result.value.Path))
|
if (await HasProjectAsync(result.Value.Path))
|
||||||
{
|
{
|
||||||
return Result<ProjectMetadataInfo>.Fail("Project already exists.");
|
return Result.Failure("Project already exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
await AddProjectAsync(result.value.Metadata.Name, result.value.Path);
|
await AddProjectAsync(result.Value.Metadata.Name, result.Value.Path);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.AppState;
|
namespace Ghost.Editor.Core.AppState;
|
||||||
|
|
||||||
internal partial class AppStateMachine : IDisposable, IAsyncDisposable
|
internal partial class AppStateMachine : IDisposable, IAsyncDisposable
|
||||||
@@ -5,52 +7,91 @@ internal partial class AppStateMachine : IDisposable, IAsyncDisposable
|
|||||||
private Dictionary<StateKey, Lazy<IAppState>> _states = new();
|
private Dictionary<StateKey, Lazy<IAppState>> _states = new();
|
||||||
private IAppState? _current;
|
private IAppState? _current;
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
public void RegisterState(StateKey key, Func<IAppState> stateFactory)
|
public void RegisterState(StateKey key, Func<IAppState> stateFactory)
|
||||||
{
|
{
|
||||||
_states[key] = new(stateFactory);
|
_states[key] = new(stateFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task TransitionToAsync(StateKey stateKey, object? parameter = null)
|
public async Task<Result> TransitionToAsync(StateKey stateKey, object? parameter = null)
|
||||||
{
|
{
|
||||||
var previous = _current;
|
var previous = _current;
|
||||||
if (!_states.TryGetValue(stateKey, out var next))
|
if (!_states.TryGetValue(stateKey, out var next))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"State '{stateKey}' is not registered.");
|
return Result.Failure($"State '{stateKey}' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Result result;
|
||||||
|
if (previous != null)
|
||||||
|
{
|
||||||
|
result = await previous.OnExitingAsync();
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = await next.Value.OnEnteringAsync(parameter);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
if (previous != null)
|
||||||
|
{
|
||||||
|
await previous.OnEnteredAsync(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previous != null)
|
if (previous != null)
|
||||||
{
|
{
|
||||||
await previous.OnExitingAsync();
|
result = await previous.OnExitedAsync();
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
await next.Value.OnExitedAsync();
|
||||||
|
await previous.OnEnteredAsync(parameter);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await next.Value.OnEnteringAsync(parameter);
|
result = await next.Value.OnEnteredAsync(parameter);
|
||||||
|
if (result.IsFailure)
|
||||||
if (previous != null)
|
|
||||||
{
|
{
|
||||||
await previous.OnExitedAsync();
|
await next.Value.OnExitedAsync();
|
||||||
}
|
|
||||||
|
|
||||||
await next.Value.OnEnteredAsync(parameter);
|
if (previous != null)
|
||||||
|
{
|
||||||
|
await previous.OnEnteredAsync(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
_current = next.Value;
|
_current = next.Value;
|
||||||
|
|
||||||
|
return Result.Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_states.Clear();
|
DisposeAsync().AsTask().Wait();
|
||||||
|
|
||||||
_current?.OnExitingAsync().GetAwaiter().GetResult();
|
|
||||||
_current?.OnExitedAsync().GetAwaiter().GetResult();
|
|
||||||
_current = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_states.Clear();
|
_states.Clear();
|
||||||
if (_current != null)
|
if (_current != null)
|
||||||
{
|
{
|
||||||
await _current.OnExitingAsync();
|
await _current.OnExitingAsync();
|
||||||
await _current.OnExitedAsync();
|
await _current.OnExitedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_current = null;
|
||||||
|
_disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.AppState;
|
namespace Ghost.Editor.Core.AppState;
|
||||||
|
|
||||||
internal interface IAppState
|
internal interface IAppState
|
||||||
@@ -5,22 +7,22 @@ internal interface IAppState
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when exiting the state.
|
/// Called when exiting the state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Task OnExitingAsync();
|
public Task<Result> OnExitingAsync();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when entering the state, right after OnEnteringAsync.
|
/// Called when entering the state, right after OnEnteringAsync.
|
||||||
/// <paramref name="parameter">can be used to pass data into the state, such as a project to load.</summary>
|
/// <paramref name="parameter">can be used to pass data into the state, such as a project to load.</summary>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Task OnEnteringAsync(object? parameter);
|
public Task<Result> OnEnteringAsync(object? parameter);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when exiting the state, specifically for pose transitions.
|
/// Called when exiting the state, specifically for pose transitions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Task OnExitedAsync();
|
public Task<Result> OnExitedAsync();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when entered the state, specifically after the state has been fully initialized and is ready for interaction.
|
/// Called when entered the state, specifically after the state has been fully initialized and is ready for interaction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parameter">can be used to pass data into the state, such as a project to load.</param>
|
/// <param name="parameter">can be used to pass data into the state, such as a project to load.</param>
|
||||||
public Task OnEnteredAsync(object? parameter);
|
public Task<Result> OnEnteredAsync(object? parameter);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Editor.Core.Utilities;
|
using Ghost.Editor.Core.Utilities;
|
||||||
using Ghost.Engine.Services;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
@@ -8,7 +7,7 @@ namespace Ghost.Editor.Core.AssetHandle;
|
|||||||
|
|
||||||
public static partial class AssetDatabase
|
public static partial class AssetDatabase
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, Type> _importerTypeLookup = new();
|
private static readonly Dictionary<string, Type> s_importerTypeLookup = new();
|
||||||
|
|
||||||
private static void InitializeMetaData()
|
private static void InitializeMetaData()
|
||||||
{
|
{
|
||||||
@@ -23,7 +22,7 @@ public static partial class AssetDatabase
|
|||||||
var attribute = type.GetCustomAttribute<AssetImporterAttribute>()!;
|
var attribute = type.GetCustomAttribute<AssetImporterAttribute>()!;
|
||||||
foreach (var extension in attribute.SupportedExtensions)
|
foreach (var extension in attribute.SupportedExtensions)
|
||||||
{
|
{
|
||||||
_importerTypeLookup[extension] = type;
|
s_importerTypeLookup[extension] = type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,12 +35,12 @@ public static partial class AssetDatabase
|
|||||||
{
|
{
|
||||||
if (Directory.Exists(assetPath))
|
if (Directory.Exists(assetPath))
|
||||||
{
|
{
|
||||||
return Result<string>.Fail("Folder does not have meta data");
|
return Result<string>.Failure("Folder does not have meta data");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Path.GetExtension(assetPath).Equals(".meta", StringComparison.OrdinalIgnoreCase))
|
if (Path.GetExtension(assetPath).Equals(".meta", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return Result<string>.Fail("Asset path cannot be a meta file");
|
return Result<string>.Failure("Asset path cannot be a meta file");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result<string>.Success(assetPath + ".meta");
|
return Result<string>.Success(assetPath + ".meta");
|
||||||
@@ -51,7 +50,7 @@ public static partial class AssetDatabase
|
|||||||
{
|
{
|
||||||
var extension = Path.GetExtension(assetPath);
|
var extension = Path.GetExtension(assetPath);
|
||||||
|
|
||||||
if (_importerTypeLookup.TryGetValue(extension, out var importerType))
|
if (s_importerTypeLookup.TryGetValue(extension, out var importerType))
|
||||||
{
|
{
|
||||||
var settingsType = importerType.BaseType?.GetGenericArguments()[0];
|
var settingsType = importerType.BaseType?.GetGenericArguments()[0];
|
||||||
if (settingsType == null || !typeof(ImporterSettings).IsAssignableFrom(settingsType))
|
if (settingsType == null || !typeof(ImporterSettings).IsAssignableFrom(settingsType))
|
||||||
@@ -79,28 +78,27 @@ public static partial class AssetDatabase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void GenerateMetaFile(string assetPath)
|
internal static Result GenerateMetaFile(string assetPath)
|
||||||
{
|
{
|
||||||
var metaFileResult = GetMetaFilePath(assetPath);
|
var metaFileResult = GetMetaFilePath(assetPath);
|
||||||
if (!metaFileResult.success)
|
if (!metaFileResult.IsSuccess)
|
||||||
{
|
{
|
||||||
Logger.LogError(metaFileResult.message ?? string.Empty);
|
return metaFileResult;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(metaFileResult.value))
|
if (File.Exists(metaFileResult.Value))
|
||||||
{
|
{
|
||||||
var existingMeta = JsonSerializer.Deserialize<AssetMeta>(File.ReadAllText(metaFileResult.value));
|
var existingMeta = JsonSerializer.Deserialize<AssetMeta>(File.ReadAllText(metaFileResult.Value));
|
||||||
if (existingMeta != null && _assetPathLookup.TryGetValue(existingMeta.Guid, out var path))
|
if (existingMeta != null && s_assetPathLookup.TryGetValue(existingMeta.Guid, out var path))
|
||||||
{
|
{
|
||||||
if (assetPath != path)
|
if (assetPath != path)
|
||||||
{
|
{
|
||||||
existingMeta.Guid = Guid.NewGuid();
|
existingMeta.Guid = Guid.NewGuid();
|
||||||
WriteMetaFile(metaFileResult.value, existingMeta);
|
WriteMetaFile(metaFileResult.Value, existingMeta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return Result.Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultSettings = GetDefaultSettingsForAsset(assetPath);
|
var defaultSettings = GetDefaultSettingsForAsset(assetPath);
|
||||||
@@ -110,7 +108,9 @@ public static partial class AssetDatabase
|
|||||||
Settings = defaultSettings
|
Settings = defaultSettings
|
||||||
};
|
};
|
||||||
|
|
||||||
WriteMetaFile(metaFileResult.value, metaData);
|
WriteMetaFile(metaFileResult.Value, metaData);
|
||||||
|
|
||||||
|
return Result.Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnAssetCreated(object sender, FileSystemEventArgs e)
|
private static void OnAssetCreated(object sender, FileSystemEventArgs e)
|
||||||
@@ -121,19 +121,19 @@ public static partial class AssetDatabase
|
|||||||
private static void OnAssetDeleted(object sender, FileSystemEventArgs e)
|
private static void OnAssetDeleted(object sender, FileSystemEventArgs e)
|
||||||
{
|
{
|
||||||
var metaFileResult = GetMetaFilePath(e.FullPath);
|
var metaFileResult = GetMetaFilePath(e.FullPath);
|
||||||
if (metaFileResult.success && File.Exists(metaFileResult.value))
|
if (metaFileResult.IsSuccess && File.Exists(metaFileResult.Value))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var meta = JsonSerializer.Deserialize<AssetMeta>(File.ReadAllText(metaFileResult.value));
|
var meta = JsonSerializer.Deserialize<AssetMeta>(File.ReadAllText(metaFileResult.Value));
|
||||||
if (meta != null
|
if (meta != null
|
||||||
&& _assetPathLookup.TryGetValue(meta.Guid, out var path)
|
&& s_assetPathLookup.TryGetValue(meta.Guid, out var path)
|
||||||
&& path == e.FullPath)
|
&& path == e.FullPath)
|
||||||
{
|
{
|
||||||
_assetPathLookup.Remove(meta.Guid);
|
s_assetPathLookup.Remove(meta.Guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
File.Delete(metaFileResult.value);
|
File.Delete(metaFileResult.Value);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ public static partial class AssetDatabase
|
|||||||
{
|
{
|
||||||
private static FileSystemWatcher? _watcher;
|
private static FileSystemWatcher? _watcher;
|
||||||
|
|
||||||
private static readonly Dictionary<Guid, string> _assetPathLookup = new();
|
private static readonly Dictionary<Guid, string> s_assetPathLookup = new();
|
||||||
|
|
||||||
public static DirectoryInfo? AssetsDirectory
|
public static DirectoryInfo? AssetsDirectory
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ public partial class ValueControl<T> : Control
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the value without notifying the change event.
|
/// Sets the _value without notifying the change event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The new value to set.</param>
|
/// <param name="value">The new _value to set.</param>
|
||||||
/// <remarks>This method only suppresses the change event notification, not the <see cref="ValueChanged(T, T)"/> method.
|
/// <remarks>This method only suppresses the change event notification, not the <see cref="ValueChanged(T, T)"/> method.
|
||||||
/// Useful when you need to change the value programmatically without triggering the change event.</remarks>
|
/// Useful when you need to change the _value programmatically without triggering the change event.</remarks>
|
||||||
public void SetValueWithoutNotifying(T value)
|
public void SetValueWithoutNotifying(T value)
|
||||||
{
|
{
|
||||||
_suppressChangedEvent = true;
|
_suppressChangedEvent = true;
|
||||||
|
|||||||
@@ -73,17 +73,17 @@ internal partial class CreateProjectViewModel(INotificationService notificationS
|
|||||||
}
|
}
|
||||||
|
|
||||||
var result = await projectService.CreateProjectAsync(ProjectName, ProjectLocation, EngineData.s_engineVersion, SelectedTemplate.Value.directory);
|
var result = await projectService.CreateProjectAsync(ProjectName, ProjectLocation, EngineData.s_engineVersion, SelectedTemplate.Value.directory);
|
||||||
if (!result.success)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
notificationService.ShowNotification(result.message, MessageType.Error);
|
notificationService.ShowNotification(result.Message, MessageType.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await stateService.TransitionToAsync(StateKey.EngineEditor, result.value);
|
await stateService.TransitionToAsync(StateKey.EngineEditor, result.Value);
|
||||||
}
|
}
|
||||||
catch (System.Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ internal partial class OpenProjectViewModel(ProjectService projectService, INoti
|
|||||||
if (rootFolder != null)
|
if (rootFolder != null)
|
||||||
{
|
{
|
||||||
var result = await projectService.AddProjectFromDirectoryAsync(rootFolder.Path);
|
var result = await projectService.AddProjectFromDirectoryAsync(rootFolder.Path);
|
||||||
if (result.success)
|
if (result.IsSuccess)
|
||||||
{
|
{
|
||||||
projects.Add(result.value);
|
projects.Add(result.Value);
|
||||||
goto CloseDropPanel;
|
goto CloseDropPanel;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
errorMessage = result.message;
|
errorMessage = result.Message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public abstract class ScriptComponent : IComponentData
|
|||||||
internal World _world = null!;
|
internal World _world = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this script component is enabled.
|
/// Gets or sets a Value indicating whether this script component is enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Enable
|
public bool Enable
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public readonly struct EntityManager : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of the component to set.</typeparam>
|
/// <typeparam name="T">The type of the component to set.</typeparam>
|
||||||
/// <param name="entity">The entity for which the component is to be add.</param>
|
/// <param name="entity">The entity for which the component is to be add.</param>
|
||||||
/// <param name="component">The component value to add.</param>
|
/// <param name="component">The component Value to add.</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly void AddComponent<T>(Entity entity, T component)
|
public readonly void AddComponent<T>(Entity entity, T component)
|
||||||
where T : unmanaged, IComponentData
|
where T : unmanaged, IComponentData
|
||||||
@@ -145,7 +145,7 @@ public readonly struct EntityManager : IDisposable
|
|||||||
/// Sets a component of the specified type for the given <see cref="Entity"/>.
|
/// Sets a component of the specified type for the given <see cref="Entity"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entity">The entity for which the component is to be set.</param>
|
/// <param name="entity">The entity for which the component is to be set.</param>
|
||||||
/// <param name="component">The component value to set.</param>
|
/// <param name="component">The component Value to set.</param>
|
||||||
/// <param name="type">The type of the component to set.</param>
|
/// <param name="type">The type of the component to set.</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly void SetComponent(Entity entity, IComponentData component, Type type)
|
public readonly void SetComponent(Entity entity, IComponentData component, Type type)
|
||||||
@@ -169,7 +169,7 @@ public readonly struct EntityManager : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of the component to set.</typeparam>
|
/// <typeparam name="T">The type of the component to set.</typeparam>
|
||||||
/// <param name="entity">The entity for which the component is to be set.</param>
|
/// <param name="entity">The entity for which the component is to be set.</param>
|
||||||
/// <param name="component">The component value to set.</param>
|
/// <param name="component">The component Value to set.</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly void SetComponent<T>(Entity entity, in T component)
|
public readonly void SetComponent<T>(Entity entity, in T component)
|
||||||
where T : unmanaged, IComponentData
|
where T : unmanaged, IComponentData
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ public struct QueryFilter : IDisposable
|
|||||||
|
|
||||||
if (!allMask.IsCreated)
|
if (!allMask.IsCreated)
|
||||||
{
|
{
|
||||||
allMask = new UnsafeBitSet(mask.Length, Allocator.Stack, AllocationOption.None);
|
allMask = new UnsafeBitSet(mask.Count, Allocator.Stack, AllocationOption.None);
|
||||||
allMask.SetAll();
|
allMask.SetAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
allMask.AndOperation(mask);
|
allMask.And(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var typeHandle in _any)
|
foreach (var typeHandle in _any)
|
||||||
@@ -53,10 +53,10 @@ public struct QueryFilter : IDisposable
|
|||||||
|
|
||||||
if (!anyMask.IsCreated)
|
if (!anyMask.IsCreated)
|
||||||
{
|
{
|
||||||
anyMask = new UnsafeBitSet(mask.Length, Allocator.Stack);
|
anyMask = new UnsafeBitSet(mask.Count, Allocator.Stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
anyMask.OrOperation(mask);
|
anyMask.And(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var typeHandle in _absent)
|
foreach (var typeHandle in _absent)
|
||||||
@@ -65,25 +65,25 @@ public struct QueryFilter : IDisposable
|
|||||||
|
|
||||||
if (!absentMask.IsCreated)
|
if (!absentMask.IsCreated)
|
||||||
{
|
{
|
||||||
absentMask = new UnsafeBitSet(mask.Length, Allocator.Stack);
|
absentMask = new UnsafeBitSet(mask.Count, Allocator.Stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
absentMask.OrOperation(mask);
|
absentMask.Or(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allMask.IsCreated)
|
if (allMask.IsCreated)
|
||||||
{
|
{
|
||||||
result.AndOperation(allMask);
|
result.And(allMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anyMask.IsCreated)
|
if (anyMask.IsCreated)
|
||||||
{
|
{
|
||||||
result.AndOperation(anyMask);
|
result.And(anyMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (absentMask.IsCreated)
|
if (absentMask.IsCreated)
|
||||||
{
|
{
|
||||||
result.AndOperation(~absentMask);
|
result.And(~absentMask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
|
using Ghost.Core.Graphics;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
namespace Ghost.Graphics.RHI;
|
namespace Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
public struct CompileResult : IDisposable
|
public struct CompileResult : IDisposable
|
||||||
{
|
{
|
||||||
public UnsafeArray<byte> bytecode;
|
public UnsafeArray<byte> bytecode;
|
||||||
|
public ShaderReflectionData reflectionData;
|
||||||
|
|
||||||
public readonly bool IsCreated => bytecode.IsCreated;
|
public readonly bool IsCreated => bytecode.IsCreated;
|
||||||
|
|
||||||
@@ -16,6 +19,20 @@ public struct CompileResult : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct GraphicsCompiledResult : IDisposable
|
||||||
|
{
|
||||||
|
public CompileResult tsResult;
|
||||||
|
public CompileResult msResult;
|
||||||
|
public CompileResult psResult;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
tsResult.Dispose();
|
||||||
|
msResult.Dispose();
|
||||||
|
psResult.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ref struct CompilerConfig
|
public ref struct CompilerConfig
|
||||||
{
|
{
|
||||||
public ReadOnlySpan<string> defines;
|
public ReadOnlySpan<string> defines;
|
||||||
@@ -49,7 +66,8 @@ public enum CompilerOption
|
|||||||
None = 0,
|
None = 0,
|
||||||
KeepDebugInfo = 1 << 0,
|
KeepDebugInfo = 1 << 0,
|
||||||
KeepReflections = 1 << 1,
|
KeepReflections = 1 << 1,
|
||||||
WarnAsError = 1 << 2
|
WarnAsError = 1 << 2,
|
||||||
|
SpirvCrossCompile = 1 << 3
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ShaderStage
|
public enum ShaderStage
|
||||||
@@ -125,6 +143,7 @@ public readonly struct ShaderReflectionData
|
|||||||
|
|
||||||
public unsafe interface IShaderCompiler
|
public unsafe interface IShaderCompiler
|
||||||
{
|
{
|
||||||
Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator, void** ppReflection);
|
Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator);
|
||||||
Result<ShaderReflectionData> PerformDXCReflection<T>(T* pReflectionBlob) where T : unmanaged;
|
Result<GraphicsCompiledResult> CompilePass(IPassDescriptor descriptor);
|
||||||
|
Result<GraphicsCompiledResult> LoadCompiledCache(ShaderPassKey key);
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ public ref struct MaterialAccessor
|
|||||||
/// Sets a float property in the material's constant buffer.
|
/// Sets a float property in the material's constant buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyName">The name of the property to set.</param>
|
/// <param name="propertyName">The name of the property to set.</param>
|
||||||
/// <param name="value">The value to set for the property.</param>
|
/// <param name="value">The Value to set for the property.</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly void SetFloat(string propertyName, in float value)
|
public readonly void SetFloat(string propertyName, in float value)
|
||||||
{
|
{
|
||||||
@@ -137,7 +137,7 @@ public ref struct MaterialAccessor
|
|||||||
/// Sets a uint property in the material's constant buffer (useful for texture indices).
|
/// Sets a uint property in the material's constant buffer (useful for texture indices).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyName">The name of the property to set.</param>
|
/// <param name="propertyName">The name of the property to set.</param>
|
||||||
/// <param name="value">The value to set for the property.</param>
|
/// <param name="value">The Value to set for the property.</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly void SetUInt(string propertyName, in uint value)
|
public readonly void SetUInt(string propertyName, in uint value)
|
||||||
{
|
{
|
||||||
@@ -148,7 +148,7 @@ public ref struct MaterialAccessor
|
|||||||
/// Sets a Vector property in the material's constant buffer.
|
/// Sets a Vector property in the material's constant buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyName">The name of the property to set.</param>
|
/// <param name="propertyName">The name of the property to set.</param>
|
||||||
/// <param name="value">The value to set for the property.</param>
|
/// <param name="value">The Value to set for the property.</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly void SetVector(string propertyName, in float4 value)
|
public readonly void SetVector(string propertyName, in float4 value)
|
||||||
{
|
{
|
||||||
@@ -159,7 +159,7 @@ public ref struct MaterialAccessor
|
|||||||
/// Sets a Matrix property in the material's constant buffer.
|
/// Sets a Matrix property in the material's constant buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyName">The name of the property to set.</param>
|
/// <param name="propertyName">The name of the property to set.</param>
|
||||||
/// <param name="value">The value to set for the property.</param>
|
/// <param name="value">The Value to set for the property.</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly void SetMatrix(string propertyName, in float4x4 value)
|
public readonly void SetMatrix(string propertyName, in float4x4 value)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
|
using Ghost.Graphics.Contracts;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
@@ -17,6 +18,7 @@ public unsafe readonly ref struct RenderingContext
|
|||||||
public ICommandBuffer CopyCommandBuffer => _copyCmd;
|
public ICommandBuffer CopyCommandBuffer => _copyCmd;
|
||||||
public ICommandBuffer ComputeCommandBuffer => _computeCmd;
|
public ICommandBuffer ComputeCommandBuffer => _computeCmd;
|
||||||
|
|
||||||
|
public IShaderCompiler ShaderCompiler => _engine.ShaderCompiler;
|
||||||
public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator;
|
public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator;
|
||||||
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
|
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
|
||||||
public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary;
|
public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary;
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ using static TerraFX.Aliases.DXGI_Alias;
|
|||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
internal unsafe class D3D12CommandBuffer : D3D12RHIObject<ID3D12GraphicsCommandList10>, ICommandBuffer
|
internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList10>, ICommandBuffer
|
||||||
{
|
{
|
||||||
private ComPtr<ID3D12CommandAllocator> _allocator;
|
private UniquePtr<ID3D12CommandAllocator> _allocator;
|
||||||
|
|
||||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||||
@@ -282,8 +282,15 @@ internal unsafe class D3D12CommandBuffer : D3D12RHIObject<ID3D12GraphicsCommandL
|
|||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var shaderPipeline = _pipelineLibrary.GetGraphicsPSO(pipelineKey).GetValueOrThrow();
|
var psor = _pipelineLibrary.GetGraphicsPSO(pipelineKey);
|
||||||
nativeObject.Get()->SetPipelineState(shaderPipeline.value);
|
if (psor.Status != ResultStatus.Success)
|
||||||
|
{
|
||||||
|
#if DEBUG || GHOST_EDITOR
|
||||||
|
Logger.LogError($"Failed to get graphics pipeline state object for key {pipelineKey}: {psor.Status}");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeObject.Get()->SetPipelineState(psor.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer)
|
public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer)
|
||||||
@@ -479,7 +486,7 @@ internal unsafe class D3D12CommandBuffer : D3D12RHIObject<ID3D12GraphicsCommandL
|
|||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (IsDisposed)
|
if (Disposed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -489,7 +496,7 @@ internal unsafe class D3D12CommandBuffer : D3D12RHIObject<ID3D12GraphicsCommandL
|
|||||||
throw new InvalidOperationException("Command buffer is still recording");
|
throw new InvalidOperationException("Command buffer is still recording");
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryLeakException.ThrowIfRefCountNonZero(_allocator.Reset());
|
_allocator.Dispose();
|
||||||
_commandCount = 0;
|
_commandCount = 0;
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Ghost.Core.Utilities;
|
using Ghost.Core.Utilities;
|
||||||
using Ghost.Graphics.D3D12.Utilities;
|
using Ghost.Graphics.D3D12.Utilities;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
|
using Misaki.HighPerformance.LowLevel;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
@@ -9,19 +10,18 @@ namespace Ghost.Graphics.D3D12;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// D3D12 implementation of command queue interface
|
/// D3D12 implementation of command queue interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal unsafe class D3D12CommandQueue : ICommandQueue
|
internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, ICommandQueue
|
||||||
{
|
{
|
||||||
private ComPtr<ID3D12CommandQueue> _queue;
|
private UniquePtr<ID3D12Fence1> _fence;
|
||||||
private ComPtr<ID3D12Fence1> _fence;
|
|
||||||
private readonly AutoResetEvent _fenceEvent;
|
private readonly AutoResetEvent _fenceEvent;
|
||||||
private ulong _fenceValue;
|
private ulong _fenceValue;
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public CommandQueueType Type
|
public CommandQueueType Type
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
public ID3D12CommandQueue* NativeQueue => _queue.Get();
|
|
||||||
|
public ID3D12CommandQueue* NativeQueue => nativeObject.Get();
|
||||||
|
|
||||||
public D3D12CommandQueue(ID3D12Device14* pDevice, CommandQueueType type)
|
public D3D12CommandQueue(ID3D12Device14* pDevice, CommandQueueType type)
|
||||||
{
|
{
|
||||||
@@ -41,17 +41,25 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
|||||||
ThrowIfFailed(pDevice->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue));
|
ThrowIfFailed(pDevice->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue));
|
||||||
ThrowIfFailed(pDevice->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
|
ThrowIfFailed(pDevice->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
|
||||||
|
|
||||||
_queue.Attach(pQueue);
|
nativeObject.Attach(pQueue);
|
||||||
_fence.Attach(pFence);
|
_fence.Attach(pFence);
|
||||||
}
|
}
|
||||||
|
|
||||||
~D3D12CommandQueue()
|
private static D3D12_COMMAND_LIST_TYPE ConvertCommandQueueType(CommandQueueType type)
|
||||||
{
|
{
|
||||||
Dispose();
|
return type switch
|
||||||
|
{
|
||||||
|
CommandQueueType.Graphics => D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||||
|
CommandQueueType.Compute => D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_COMPUTE,
|
||||||
|
CommandQueueType.Copy => D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_COPY,
|
||||||
|
_ => throw new ArgumentException($"Unknown command queue type: {type}")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Submit(ICommandBuffer commandBuffer)
|
public void Submit(ICommandBuffer commandBuffer)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
if (commandBuffer.IsEmpty)
|
if (commandBuffer.IsEmpty)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -61,7 +69,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
|||||||
{
|
{
|
||||||
var commandList = d3d12CommandBuffer.NativeCommandList;
|
var commandList = d3d12CommandBuffer.NativeCommandList;
|
||||||
var commandListPtr = (ID3D12CommandList*)commandList;
|
var commandListPtr = (ID3D12CommandList*)commandList;
|
||||||
_queue.Get()->ExecuteCommandLists(1, &commandListPtr);
|
nativeObject.Get()->ExecuteCommandLists(1, &commandListPtr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -71,6 +79,8 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
|||||||
|
|
||||||
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
|
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
Span<int> executableIndices = stackalloc int[commandBuffers.Length];
|
Span<int> executableIndices = stackalloc int[commandBuffers.Length];
|
||||||
executableIndices.Fill(-1);
|
executableIndices.Fill(-1);
|
||||||
|
|
||||||
@@ -107,18 +117,22 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
|||||||
currentIndex++;
|
currentIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_queue.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
|
nativeObject.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong Signal(ulong value)
|
public ulong Signal(ulong value)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
_fenceValue = value;
|
_fenceValue = value;
|
||||||
_queue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue);
|
nativeObject.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue);
|
||||||
return _fenceValue;
|
return _fenceValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WaitForValue(ulong value)
|
public void WaitForValue(ulong value)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
if (_fence.Get()->GetCompletedValue() < value)
|
if (_fence.Get()->GetCompletedValue() < value)
|
||||||
{
|
{
|
||||||
var handle = new HANDLE((void*)_fenceEvent.SafeWaitHandle.DangerousGetHandle());
|
var handle = new HANDLE((void*)_fenceEvent.SafeWaitHandle.DangerousGetHandle());
|
||||||
@@ -131,39 +145,28 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
|||||||
|
|
||||||
public ulong GetCompletedValue()
|
public ulong GetCompletedValue()
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
return _fence.Get()->GetCompletedValue();
|
return _fence.Get()->GetCompletedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WaitIdle()
|
public void WaitIdle()
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
|
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
|
||||||
WaitForValue(fenceValue);
|
WaitForValue(fenceValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static D3D12_COMMAND_LIST_TYPE ConvertCommandQueueType(CommandQueueType type)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
return type switch
|
if (Disposed)
|
||||||
{
|
|
||||||
CommandQueueType.Graphics => D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
||||||
CommandQueueType.Compute => D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_COMPUTE,
|
|
||||||
CommandQueueType.Copy => D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_COPY,
|
|
||||||
_ => throw new ArgumentException($"Unknown command queue type: {type}")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_fenceEvent?.Dispose();
|
_fenceEvent?.Dispose();
|
||||||
_fence.Dispose();
|
_fence.Dispose();
|
||||||
_queue.Dispose();
|
|
||||||
|
|
||||||
_disposed = true;
|
base.Dispose(disposing);
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Ghost.Core.Utilities;
|
||||||
|
using Misaki.HighPerformance.LowLevel;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
@@ -7,9 +9,9 @@ namespace Ghost.Graphics.D3D12;
|
|||||||
|
|
||||||
internal unsafe class D3D12DebugLayer
|
internal unsafe class D3D12DebugLayer
|
||||||
{
|
{
|
||||||
private readonly ComPtr<ID3D12Debug6> _d3d12Debug;
|
private UniquePtr<ID3D12Debug6> _d3d12Debug;
|
||||||
private readonly ComPtr<IDXGIDebug1> _dxgiDebug;
|
private UniquePtr<IDXGIDebug1> _dxgiDebug;
|
||||||
private readonly ComPtr<IDXGIInfoQueue> _dxgiInfoQueue;
|
private UniquePtr<IDXGIInfoQueue> _dxgiInfoQueue;
|
||||||
|
|
||||||
public D3D12DebugLayer()
|
public D3D12DebugLayer()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
|
using Ghost.Graphics.Utilities;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
using Ghost.Graphics.RHI;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||||
@@ -12,6 +13,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private readonly D3D12RenderDevice _device;
|
private readonly D3D12RenderDevice _device;
|
||||||
|
private readonly DxcShaderCompiler _shaderCompiler;
|
||||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||||
@@ -23,6 +25,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public IRenderDevice Device => _device;
|
public IRenderDevice Device => _device;
|
||||||
|
public IShaderCompiler ShaderCompiler => _shaderCompiler;
|
||||||
public IPipelineLibrary PipelineLibrary => _pipelineLibrary;
|
public IPipelineLibrary PipelineLibrary => _pipelineLibrary;
|
||||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||||
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
||||||
@@ -34,6 +37,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
_debugLayer = new();
|
_debugLayer = new();
|
||||||
#endif
|
#endif
|
||||||
_device = new();
|
_device = new();
|
||||||
|
_shaderCompiler = new();
|
||||||
_descriptorAllocator = new(_device);
|
_descriptorAllocator = new(_device);
|
||||||
|
|
||||||
_resourceDatabase = new(_descriptorAllocator);
|
_resourceDatabase = new(_descriptorAllocator);
|
||||||
|
|||||||
132
Ghost.Graphics/D3D12/D3D12Object.cs
Normal file
132
Ghost.Graphics/D3D12/D3D12Object.cs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
using Ghost.Core.Utilities;
|
||||||
|
using Ghost.Graphics.D3D12.Utilities;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
|
using Misaki.HighPerformance.LowLevel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using TerraFX.Interop.DirectX;
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
|
internal abstract class D3D12RHIObject : IRHIObject
|
||||||
|
{
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
} = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal abstract unsafe class D3D12Object<T> : IRHIObject, IDisposable
|
||||||
|
where T : unmanaged, ID3D12Object.Interface
|
||||||
|
{
|
||||||
|
private bool _disposed;
|
||||||
|
private string _name = string.Empty;
|
||||||
|
|
||||||
|
protected UniquePtr<T> nativeObject;
|
||||||
|
|
||||||
|
protected bool Disposed => _disposed;
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => _name;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_name == value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_name = value;
|
||||||
|
if (nativeObject.Get() != null)
|
||||||
|
{
|
||||||
|
nativeObject.Get()->SetName(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~D3D12Object()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected void ThrowIfDisposed()
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeObject.Dispose();
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal abstract class IUnknownObject<T> : IRHIObject, IDisposable
|
||||||
|
where T : unmanaged, IUnknown.Interface
|
||||||
|
{
|
||||||
|
private bool _disposed;
|
||||||
|
private string _name = string.Empty;
|
||||||
|
|
||||||
|
protected UniquePtr<T> nativeObject;
|
||||||
|
|
||||||
|
protected bool Disposed => _disposed;
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => _name;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_name == value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_name = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~IUnknownObject()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected void ThrowIfDisposed()
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeObject.Dispose();
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Core.Graphics;
|
using Ghost.Core.Graphics;
|
||||||
using Ghost.Core.Utilities;
|
using Ghost.Core.Utilities;
|
||||||
|
using Ghost.Graphics.Contracts;
|
||||||
using Ghost.Graphics.Core;
|
using Ghost.Graphics.Core;
|
||||||
using Ghost.Graphics.D3D12.Utilities;
|
using Ghost.Graphics.D3D12.Utilities;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
|
using Misaki.HighPerformance.LowLevel;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
@@ -17,25 +19,10 @@ using static TerraFX.Aliases.D3D12_Alias;
|
|||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
internal struct D3D12GraphicsCompiledResult : IDisposable
|
|
||||||
{
|
|
||||||
public CompileResult tsResult;
|
|
||||||
public CompileResult msResult;
|
|
||||||
public CompileResult psResult;
|
|
||||||
public CBufferInfo cbufferInfo;
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
tsResult.Dispose();
|
|
||||||
msResult.Dispose();
|
|
||||||
psResult.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal struct D3D12PipelineState : IDisposable
|
internal struct D3D12PipelineState : IDisposable
|
||||||
{
|
{
|
||||||
public D3DX12_MESH_SHADER_PIPELINE_STATE_DESC psoDesc;
|
public D3DX12_MESH_SHADER_PIPELINE_STATE_DESC psoDesc;
|
||||||
public ComPtr<ID3D12PipelineState> pso;
|
public UniquePtr<ID3D12PipelineState> pso;
|
||||||
public ShaderPassKey shaderPass;
|
public ShaderPassKey shaderPass;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -49,12 +36,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
|||||||
private readonly D3D12RenderDevice _device;
|
private readonly D3D12RenderDevice _device;
|
||||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||||
|
|
||||||
private ComPtr<ID3D12PipelineLibrary1> _library;
|
private UniquePtr<ID3D12PipelineLibrary1> _library;
|
||||||
private ComPtr<ID3D12RootSignature> _defaultRootSignature;
|
private UniquePtr<ID3D12RootSignature> _defaultRootSignature;
|
||||||
|
|
||||||
private readonly Dictionary<GraphicsPipelineKey, D3D12PipelineState> _pipelineCache;
|
private readonly Dictionary<GraphicsPipelineKey, D3D12PipelineState> _pipelineCache;
|
||||||
// NOTE: This is just a temporary cache for compiled shader code. We will implement a proper disk cache later.
|
private readonly Dictionary<ShaderPassKey, CBufferInfo> _cbufferInfoCache;
|
||||||
private readonly Dictionary<ShaderPassKey, D3D12GraphicsCompiledResult> _compiledResults;
|
|
||||||
|
|
||||||
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
|
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
|
||||||
|
|
||||||
@@ -63,13 +49,13 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
|||||||
_device = device;
|
_device = device;
|
||||||
_resourceDatabase = resourceDatabase;
|
_resourceDatabase = resourceDatabase;
|
||||||
|
|
||||||
_pipelineCache = new();
|
_pipelineCache = new Dictionary<GraphicsPipelineKey, D3D12PipelineState>();
|
||||||
_compiledResults = new();
|
_cbufferInfoCache = new Dictionary<ShaderPassKey, CBufferInfo>();
|
||||||
|
|
||||||
CreateDefaultRootSignature();
|
CreateDefaultRootSignature();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateDefaultRootSignature()
|
private Result CreateDefaultRootSignature()
|
||||||
{
|
{
|
||||||
_defaultRootSignature = default;
|
_defaultRootSignature = default;
|
||||||
|
|
||||||
@@ -154,21 +140,38 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
|||||||
Desc_1_1 = rootSignatureDesc
|
Desc_1_1 = rootSignatureDesc
|
||||||
};
|
};
|
||||||
|
|
||||||
using ComPtr<ID3DBlob> signature = default;
|
ID3DBlob* pSignature = default;
|
||||||
using ComPtr<ID3DBlob> error = default;
|
ID3DBlob* pError = default;
|
||||||
|
|
||||||
var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, signature.GetAddressOf(), error.GetAddressOf());
|
try
|
||||||
if (serializeResult.FAILED)
|
|
||||||
{
|
{
|
||||||
var errorMsg = error.Get() != null ? Marshal.PtrToStringUTF8((nint)error.Get()->GetBufferPointer()) : "Unknown error";
|
var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, &pSignature, &pError);
|
||||||
throw new InvalidOperationException($"Failed to serialize default root signature: {errorMsg}");
|
if (serializeResult.FAILED)
|
||||||
|
{
|
||||||
|
var errorMsg = pError != null ? Marshal.PtrToStringUTF8((nint)pError->GetBufferPointer()) : "Unknown error";
|
||||||
|
return Result.Failure($"Failed to serialize default root signature: {errorMsg}");
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D12RootSignature* pRootSignature = default;
|
||||||
|
ThrowIfFailed(_device.NativeDevice->CreateRootSignature(0, pSignature->GetBufferPointer(), pSignature->GetBufferSize(),
|
||||||
|
__uuidof(pRootSignature), (void**)&pRootSignature));
|
||||||
|
|
||||||
|
_defaultRootSignature.Attach(pRootSignature);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (pSignature != null)
|
||||||
|
{
|
||||||
|
pSignature->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pError != null)
|
||||||
|
{
|
||||||
|
pError->Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ID3D12RootSignature* pRootSignature = default;
|
return Result.Success();
|
||||||
ThrowIfFailed(_device.NativeDevice->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(),
|
|
||||||
__uuidof(pRootSignature), (void**)&pRootSignature));
|
|
||||||
|
|
||||||
_defaultRootSignature.Attach(pRootSignature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitializeLibrary(string? filePath)
|
public void InitializeLibrary(string? filePath)
|
||||||
@@ -208,20 +211,20 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
|||||||
fs.Write(buffer.AsSpan());
|
fs.Write(buffer.AsSpan());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Result<CBufferInfo> ValidateReflectionData(FullPassDescriptor descriptor, ShaderReflectionData reflectionData)
|
private static Result<CBufferInfo> ValidateReflectionData(ShaderReflectionData reflectionData)
|
||||||
{
|
{
|
||||||
CBufferInfo cbufferInfo = default;
|
CBufferInfo cbufferInfo;
|
||||||
|
|
||||||
foreach (var info in reflectionData.ResourcesBindings)
|
foreach (var info in reflectionData.ResourcesBindings)
|
||||||
{
|
{
|
||||||
if (info.BindPoint > 3)
|
if (info.BindPoint > 3)
|
||||||
{
|
{
|
||||||
return Result.Fail($"Resource binding point {info.BindPoint} is out of range. Only binding points 0-3 are supported in the current root signature.");
|
return Result.Failure($"Resource binding point {info.BindPoint} is out of range. Only binding points 0-3 are supported in the current root signature.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.Type != D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER)
|
if (info.Type != ShaderInputType.ConstantBuffer)
|
||||||
{
|
{
|
||||||
return Result.Fail($"Resource binding type {info.Type} is not supported. Only constant buffers are supported in the current root signature.");
|
return Result.Failure($"Resource binding type {info.Type} is not supported. Only constant buffers are supported in the current root signature.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.BindPoint == RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT)
|
if (info.BindPoint == RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT)
|
||||||
@@ -239,112 +242,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Fail("Per-material constant buffer not found in shader reflection data.");
|
return Result.Failure("Per-material constant buffer not found in shader reflection data.");
|
||||||
|
|
||||||
// TODO: Validate Cbuffer sizes and bindings.
|
// TODO: Validate Cbuffer sizes and bindings.
|
||||||
}
|
}
|
||||||
|
|
||||||
private static D3D12GraphicsCompiledResult CompileAndValidateFullPass(FullPassDescriptor descriptor)
|
|
||||||
{
|
|
||||||
static CompileResult CompileAndValidate(ref CompilerConfig config, FullPassDescriptor descriptor)
|
|
||||||
{
|
|
||||||
IDxcBlob* reflectionBlob = default;
|
|
||||||
CBufferInfo cbufferInfo = default;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// TODO: This does not include generated code. This will cause a root signature mismatch.
|
|
||||||
var result = D3D12ShaderCompiler.Compile(ref config, Allocator.Persistent, (void**)&reflectionBlob).GetValueOrThrow();
|
|
||||||
if (reflectionBlob != null)
|
|
||||||
{
|
|
||||||
var reflection = D3D12ShaderCompiler.PerformDXCReflection(reflectionBlob).GetValueOrThrow();
|
|
||||||
cbufferInfo = ValidateReflectionData(descriptor, reflection).GetValueOrThrow();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (reflectionBlob != null)
|
|
||||||
{
|
|
||||||
reflectionBlob->Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CompileResult tsResult = default;
|
|
||||||
var tsEntry = descriptor.taskShader;
|
|
||||||
if (tsEntry.IsCreated)
|
|
||||||
{
|
|
||||||
var config = new CompilerConfig
|
|
||||||
{
|
|
||||||
defines = descriptor.defines.AsSpan(),
|
|
||||||
include = descriptor.generatedCodePath,
|
|
||||||
shaderPath = tsEntry.shader,
|
|
||||||
entryPoint = tsEntry.entry,
|
|
||||||
stage = ShaderStage.TaskShader,
|
|
||||||
tier = CompilerTier.Tier0,
|
|
||||||
optimizeLevel = CompilerOptimizeLevel.O3,
|
|
||||||
options = CompilerOption.KeepReflections,
|
|
||||||
};
|
|
||||||
|
|
||||||
tsResult = CompileAndValidate(ref config, descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompileResult msResult;
|
|
||||||
var msEntry = descriptor.meshShader;
|
|
||||||
if (msEntry.IsCreated)
|
|
||||||
{
|
|
||||||
var config = new CompilerConfig
|
|
||||||
{
|
|
||||||
defines = descriptor.defines.AsSpan(),
|
|
||||||
include = descriptor.generatedCodePath,
|
|
||||||
shaderPath = msEntry.shader,
|
|
||||||
entryPoint = msEntry.entry,
|
|
||||||
stage = ShaderStage.MeshShader,
|
|
||||||
tier = CompilerTier.Tier0,
|
|
||||||
optimizeLevel = CompilerOptimizeLevel.O3,
|
|
||||||
options = CompilerOption.KeepReflections,
|
|
||||||
};
|
|
||||||
|
|
||||||
msResult = CompileAndValidate(ref config, descriptor);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Mesh shader expected.");
|
|
||||||
}
|
|
||||||
|
|
||||||
CompileResult psResult;
|
|
||||||
var psEntry = descriptor.pixelShader;
|
|
||||||
if (psEntry.IsCreated)
|
|
||||||
{
|
|
||||||
var config = new CompilerConfig
|
|
||||||
{
|
|
||||||
defines = descriptor.defines.AsSpan(),
|
|
||||||
include = descriptor.generatedCodePath,
|
|
||||||
shaderPath = psEntry.shader,
|
|
||||||
entryPoint = psEntry.entry,
|
|
||||||
stage = ShaderStage.PixelShader,
|
|
||||||
tier = CompilerTier.Tier0,
|
|
||||||
optimizeLevel = CompilerOptimizeLevel.O3,
|
|
||||||
options = CompilerOption.KeepReflections,
|
|
||||||
};
|
|
||||||
|
|
||||||
psResult = CompileAndValidate(ref config, descriptor);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Pixel shader expected.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new D3D12GraphicsCompiledResult
|
|
||||||
{
|
|
||||||
tsResult = tsResult,
|
|
||||||
msResult = msResult,
|
|
||||||
psResult = psResult
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static D3D12_COMPARISON_FUNC ToD3DCompare(ZTestOptions z) => z switch
|
private static D3D12_COMPARISON_FUNC ToD3DCompare(ZTestOptions z) => z switch
|
||||||
{
|
{
|
||||||
ZTestOptions.Disabled => D3D12_COMPARISON_FUNC_ALWAYS,
|
ZTestOptions.Disabled => D3D12_COMPARISON_FUNC_ALWAYS,
|
||||||
@@ -366,50 +268,42 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
|||||||
return D3D12Utility.D3D12_DEPTH_STENCIL_DESC_CREATE(depthEnabled, writeEnabled, cmp);
|
return D3D12Utility.D3D12_DEPTH_STENCIL_DESC_CREATE(depthEnabled, writeEnabled, cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetCompiledCache(ShaderPassKey passKey, out D3D12GraphicsCompiledResult compiled)
|
public Result<GraphicsPipelineKey> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled)
|
||||||
{
|
{
|
||||||
return _compiledResults.TryGetValue(passKey, out compiled);
|
static Result<CBufferInfo> ValidatePassReflectionData(ref readonly GraphicsCompiledResult compiled)
|
||||||
}
|
|
||||||
|
|
||||||
private GraphicsPipelineKey CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly D3D12GraphicsCompiledResult compiled)
|
|
||||||
{
|
|
||||||
var rtvCount = (uint)Math.Min(descriptor.RtvFormats.Length, D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT);
|
|
||||||
|
|
||||||
var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC
|
|
||||||
{
|
{
|
||||||
pRootSignature = _defaultRootSignature.Get(),
|
var msr = ValidateReflectionData(compiled.msResult.reflectionData);
|
||||||
MS = new D3D12_SHADER_BYTECODE(compiled.msResult.bytecode.GetUnsafePtr(), (nuint)compiled.msResult.bytecode.Count),
|
if (msr.IsFailure)
|
||||||
PS = new D3D12_SHADER_BYTECODE(compiled.psResult.bytecode.GetUnsafePtr(), (nuint)compiled.psResult.bytecode.Count),
|
|
||||||
PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
|
||||||
SampleMask = UINT32_MAX,
|
|
||||||
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
|
|
||||||
NumRenderTargets = rtvCount,
|
|
||||||
DSVFormat = descriptor.DsvFormat.ToDXGIFormat(),
|
|
||||||
DepthStencilState = BuildDepthStencil(descriptor.ZTest, descriptor.ZWrite),
|
|
||||||
NodeMask = 0,
|
|
||||||
Flags = D3D12_PIPELINE_STATE_FLAG_NONE,
|
|
||||||
|
|
||||||
BlendState = descriptor.Blend switch
|
|
||||||
{
|
{
|
||||||
BlendOptions.Opaque => D3D12Utility.D3D12_BLEND_DESC_OPAQUE,
|
return Result.Failure("Validation of mesh shader reflection data failed: " + msr.Message);
|
||||||
BlendOptions.Alpha => D3D12Utility.D3D12_BLEND_DESC_ALPHA_BLEND,
|
}
|
||||||
BlendOptions.Additive => D3D12Utility.D3D12_BLEND_DESC_ADDITIVE,
|
|
||||||
BlendOptions.Multiply => D3D12Utility.D3D12_BLEND_DESC_MULTIPLY,
|
|
||||||
BlendOptions.PremultipliedAlpha => D3D12Utility.D3D12_BLEND_DESC_PREMULTIPLIED,
|
|
||||||
_ => D3D12Utility.D3D12_BLEND_DESC_OPAQUE
|
|
||||||
},
|
|
||||||
RasterizerState = descriptor.Cull switch
|
|
||||||
{
|
|
||||||
CullOptions.Off => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE,
|
|
||||||
CullOptions.Front => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_CLOCKWISE,
|
|
||||||
CullOptions.Back => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_COUNTER_CLOCKWISE,
|
|
||||||
_ => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (compiled.tsResult.IsCreated)
|
var psr = ValidateReflectionData(compiled.psResult.reflectionData);
|
||||||
{
|
if (psr.IsFailure)
|
||||||
desc.AS = new D3D12_SHADER_BYTECODE(compiled.tsResult.bytecode.GetUnsafePtr(), (nuint)compiled.tsResult.bytecode.Count);
|
{
|
||||||
|
return Result.Failure("Validation of pixel shader reflection data failed: " + psr.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msr.Value != psr.Value)
|
||||||
|
{
|
||||||
|
return Result.Failure("Mesh shader and pixel shader constant buffer layouts do not match.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compiled.tsResult.IsCreated)
|
||||||
|
{
|
||||||
|
var tsr = ValidateReflectionData(compiled.tsResult.reflectionData);
|
||||||
|
if (tsr.IsFailure)
|
||||||
|
{
|
||||||
|
return Result.Failure("Validation of task shader reflection data failed: " + tsr.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tsr.Value != msr.Value)
|
||||||
|
{
|
||||||
|
return Result.Failure("Task shader and mesh shader constant buffer layouts do not match.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return psr.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = new GraphicsPipelineHash
|
var hash = new GraphicsPipelineHash
|
||||||
@@ -419,18 +313,67 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
|||||||
DsvFormat = descriptor.DsvFormat,
|
DsvFormat = descriptor.DsvFormat,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var rtvCount = (uint)Math.Min(descriptor.RtvFormats.Length, D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT);
|
||||||
|
|
||||||
for (var i = 0; i < rtvCount && i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
|
for (var i = 0; i < rtvCount && i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
|
||||||
{
|
{
|
||||||
desc.RTVFormats[i] = descriptor.RtvFormats[i].ToDXGIFormat();
|
|
||||||
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(descriptor.ColorMask & 0x0F);
|
|
||||||
hash.RtvFormats[i] = descriptor.RtvFormats[i];
|
hash.RtvFormats[i] = descriptor.RtvFormats[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = hash.GetKey();
|
var key = hash.GetKey();
|
||||||
ref var existing = ref CollectionsMarshal.GetValueRefOrAddDefault(_pipelineCache, key, out var exists);
|
|
||||||
if (!exists)
|
if (!_pipelineCache.ContainsKey(key))
|
||||||
{
|
{
|
||||||
existing.psoDesc = desc;
|
var result = ValidatePassReflectionData(in compiled);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return Result.Failure(result.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
_cbufferInfoCache[descriptor.PassId] = result.Value;
|
||||||
|
|
||||||
|
var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC
|
||||||
|
{
|
||||||
|
pRootSignature = _defaultRootSignature.Get(),
|
||||||
|
MS = new D3D12_SHADER_BYTECODE(compiled.msResult.bytecode.GetUnsafePtr(), (nuint)compiled.msResult.bytecode.Count),
|
||||||
|
PS = new D3D12_SHADER_BYTECODE(compiled.psResult.bytecode.GetUnsafePtr(), (nuint)compiled.psResult.bytecode.Count),
|
||||||
|
PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
||||||
|
SampleMask = UINT32_MAX,
|
||||||
|
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
|
||||||
|
NumRenderTargets = rtvCount,
|
||||||
|
DSVFormat = descriptor.DsvFormat.ToDXGIFormat(),
|
||||||
|
DepthStencilState = BuildDepthStencil(descriptor.ZTest, descriptor.ZWrite),
|
||||||
|
NodeMask = 0,
|
||||||
|
Flags = D3D12_PIPELINE_STATE_FLAG_NONE,
|
||||||
|
|
||||||
|
BlendState = descriptor.Blend switch
|
||||||
|
{
|
||||||
|
BlendOptions.Opaque => D3D12Utility.D3D12_BLEND_DESC_OPAQUE,
|
||||||
|
BlendOptions.Alpha => D3D12Utility.D3D12_BLEND_DESC_ALPHA_BLEND,
|
||||||
|
BlendOptions.Additive => D3D12Utility.D3D12_BLEND_DESC_ADDITIVE,
|
||||||
|
BlendOptions.Multiply => D3D12Utility.D3D12_BLEND_DESC_MULTIPLY,
|
||||||
|
BlendOptions.PremultipliedAlpha => D3D12Utility.D3D12_BLEND_DESC_PREMULTIPLIED,
|
||||||
|
_ => D3D12Utility.D3D12_BLEND_DESC_OPAQUE
|
||||||
|
},
|
||||||
|
RasterizerState = descriptor.Cull switch
|
||||||
|
{
|
||||||
|
CullOptions.Off => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE,
|
||||||
|
CullOptions.Front => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_CLOCKWISE,
|
||||||
|
CullOptions.Back => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_COUNTER_CLOCKWISE,
|
||||||
|
_ => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (compiled.tsResult.IsCreated)
|
||||||
|
{
|
||||||
|
desc.AS = new D3D12_SHADER_BYTECODE(compiled.tsResult.bytecode.GetUnsafePtr(), (nuint)compiled.tsResult.bytecode.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < rtvCount && i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
|
||||||
|
{
|
||||||
|
desc.RTVFormats[i] = descriptor.RtvFormats[i].ToDXGIFormat();
|
||||||
|
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(descriptor.ColorMask & 0x0F);
|
||||||
|
}
|
||||||
|
|
||||||
var meshStream = new CD3DX12_PIPELINE_MESH_STATE_STREAM(in desc);
|
var meshStream = new CD3DX12_PIPELINE_MESH_STATE_STREAM(in desc);
|
||||||
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
|
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
|
||||||
@@ -443,7 +386,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
|||||||
|
|
||||||
var pKeyStr = stackalloc char[GraphicsPipelineKey.KEY_STRING_LENGTH];
|
var pKeyStr = stackalloc char[GraphicsPipelineKey.KEY_STRING_LENGTH];
|
||||||
var keySpan = new Span<char>(pKeyStr, GraphicsPipelineKey.KEY_STRING_LENGTH);
|
var keySpan = new Span<char>(pKeyStr, GraphicsPipelineKey.KEY_STRING_LENGTH);
|
||||||
key.GetString(keySpan).ThrowIfFailed();
|
var kr = key.GetString(keySpan);
|
||||||
|
if (kr.IsFailure)
|
||||||
|
{
|
||||||
|
return kr;
|
||||||
|
}
|
||||||
|
|
||||||
var hr = _library.Get()->LoadPipeline(pKeyStr, &streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState);
|
var hr = _library.Get()->LoadPipeline(pKeyStr, &streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState);
|
||||||
if (hr == E.E_INVALIDARG)
|
if (hr == E.E_INVALIDARG)
|
||||||
@@ -457,77 +404,35 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
|||||||
ThrowIfFailed(hr);
|
ThrowIfFailed(hr);
|
||||||
}
|
}
|
||||||
|
|
||||||
existing.pso.Attach(pPipelineState);
|
D3D12PipelineState pso = default;
|
||||||
|
pso.shaderPass = descriptor.PassId;
|
||||||
|
pso.psoDesc = desc;
|
||||||
|
pso.pso.Attach(pPipelineState);
|
||||||
|
|
||||||
|
_pipelineCache[key] = pso;
|
||||||
}
|
}
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GraphicsPipelineKey CompilePassPSO(IPassDescriptor descriptor, ReadOnlySpan<TextureFormat> rtvs, TextureFormat dsv)
|
public Result<CBufferInfo, ResultStatus> GetCBufferInfo(ShaderPassKey passId)
|
||||||
{
|
{
|
||||||
GraphicsPipelineKey key = default;
|
if (_cbufferInfoCache.TryGetValue(passId, out var cbufferInfo))
|
||||||
|
|
||||||
var passKey = new ShaderPassKey(descriptor.Identifier);
|
|
||||||
var hasCompiledCache = TryGetCompiledCache(passKey, out var compiled);
|
|
||||||
|
|
||||||
switch (descriptor)
|
|
||||||
{
|
{
|
||||||
case FullPassDescriptor fullPass:
|
return Result.Create(cbufferInfo, ResultStatus.Success);
|
||||||
if (!hasCompiledCache)
|
|
||||||
{
|
|
||||||
compiled = CompileAndValidateFullPass(fullPass);
|
|
||||||
}
|
|
||||||
|
|
||||||
var psoDes = new GraphicsPSODescriptor
|
|
||||||
{
|
|
||||||
PassId = new ShaderPassKey(fullPass.Identifier),
|
|
||||||
ZTest = fullPass.localPipeline.zTest,
|
|
||||||
ZWrite = fullPass.localPipeline.zWrite,
|
|
||||||
Cull = fullPass.localPipeline.cull,
|
|
||||||
Blend = fullPass.localPipeline.blend,
|
|
||||||
ColorMask = fullPass.localPipeline.colorMask,
|
|
||||||
|
|
||||||
RtvFormats = rtvs,
|
|
||||||
DsvFormat = dsv,
|
|
||||||
};
|
|
||||||
|
|
||||||
key = CompilePSO(in psoDes, in compiled);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Do we need to support other pass types?
|
|
||||||
case FallbackPassDescriptor:
|
|
||||||
if (!hasCompiledCache)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("FallbackPassDescriptor is not supported for PSO compilation. There may be some inheritance dependency issues.");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return key;
|
return Result.Create(default(CBufferInfo), ResultStatus.NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<Ptr<ID3D12PipelineState>> GetGraphicsPSO(GraphicsPipelineKey key)
|
public Result<SharedPtr<ID3D12PipelineState>, ResultStatus> GetGraphicsPSO(GraphicsPipelineKey key)
|
||||||
{
|
{
|
||||||
if (_pipelineCache.TryGetValue(key, out var cacheEntry))
|
if (_pipelineCache.TryGetValue(key, out var cacheEntry))
|
||||||
{
|
{
|
||||||
return new Ptr<ID3D12PipelineState>(cacheEntry.pso.Get());
|
return Result.Create(new SharedPtr<ID3D12PipelineState>(cacheEntry.pso.Get()), ResultStatus.Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Fail("Pipeline state not found in cache.");
|
return Result.Create(default(SharedPtr<ID3D12PipelineState>), ResultStatus.NotFound);
|
||||||
}
|
|
||||||
|
|
||||||
public Result<CBufferInfo> GetCBufferInfo(ShaderPassKey key)
|
|
||||||
{
|
|
||||||
if (_compiledResults.TryGetValue(key, out var compiled))
|
|
||||||
{
|
|
||||||
return compiled.cbufferInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Fail("Compiled shader not found in cache.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
using Ghost.Graphics.D3D12.Utilities;
|
|
||||||
using Ghost.Graphics.RHI;
|
|
||||||
using Misaki.HighPerformance.LowLevel;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using TerraFX.Interop.DirectX;
|
|
||||||
using TerraFX.Interop.Windows;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
|
||||||
|
|
||||||
internal abstract unsafe class D3D12RHIObject<T> : IRHIObject, IDisposable
|
|
||||||
where T : unmanaged, ID3D12Object.Interface
|
|
||||||
{
|
|
||||||
private bool _disposed;
|
|
||||||
private string _name = string.Empty;
|
|
||||||
|
|
||||||
protected ComPtr<T> nativeObject;
|
|
||||||
|
|
||||||
protected bool IsDisposed => _disposed;
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get => _name;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_name == value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_name = value;
|
|
||||||
if (nativeObject.Get() != null)
|
|
||||||
{
|
|
||||||
nativeObject.Get()->SetName(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~D3D12RHIObject()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
protected void ThrowIfDisposed()
|
|
||||||
{
|
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryLeakException.ThrowIfRefCountNonZero(nativeObject.Reset());
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
using Ghost.Core.Utilities;
|
using Ghost.Core.Utilities;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using System.Runtime.Versioning;
|
using Misaki.HighPerformance.LowLevel;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
using static TerraFX.Aliases.D3D_Alias;
|
using static TerraFX.Aliases.D3D_Alias;
|
||||||
|
using static TerraFX.Aliases.D3D12_Alias;
|
||||||
using static TerraFX.Aliases.DXGI_Alias;
|
using static TerraFX.Aliases.DXGI_Alias;
|
||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
@@ -11,33 +12,30 @@ namespace Ghost.Graphics.D3D12;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// D3D12 implementation of the render device interface
|
/// D3D12 implementation of the render device interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal unsafe class D3D12RenderDevice : IRenderDevice
|
internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDevice
|
||||||
{
|
{
|
||||||
private ComPtr<IDXGIFactory7> _dxgiFactory;
|
private UniquePtr<IDXGIFactory7> _dxgiFactory;
|
||||||
private ComPtr<ID3D12Device14> _device;
|
private UniquePtr<IDXGIAdapter1> _adapter;
|
||||||
private ComPtr<IDXGIAdapter1> _adapter;
|
|
||||||
|
|
||||||
private readonly D3D12CommandQueue _graphicsQueue;
|
private readonly D3D12CommandQueue _graphicsQueue;
|
||||||
private readonly D3D12CommandQueue _computeQueue;
|
private readonly D3D12CommandQueue _computeQueue;
|
||||||
private readonly D3D12CommandQueue _copyQueue;
|
private readonly D3D12CommandQueue _copyQueue;
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public ICommandQueue GraphicsQueue => _graphicsQueue;
|
public ICommandQueue GraphicsQueue => _graphicsQueue;
|
||||||
public ICommandQueue ComputeQueue => _computeQueue;
|
public ICommandQueue ComputeQueue => _computeQueue;
|
||||||
public ICommandQueue CopyQueue => _copyQueue;
|
public ICommandQueue CopyQueue => _copyQueue;
|
||||||
|
|
||||||
public ID3D12Device14* NativeDevice => _device.Get();
|
|
||||||
public IDXGIFactory7* DXGIFactory => _dxgiFactory.Get();
|
public IDXGIFactory7* DXGIFactory => _dxgiFactory.Get();
|
||||||
|
public ID3D12Device14* NativeDevice => nativeObject.Get();
|
||||||
public IDXGIAdapter1* Adapter => _adapter.Get();
|
public IDXGIAdapter1* Adapter => _adapter.Get();
|
||||||
|
|
||||||
public D3D12RenderDevice()
|
public D3D12RenderDevice()
|
||||||
{
|
{
|
||||||
InitializeDevice();
|
InitializeDevice();
|
||||||
|
|
||||||
_graphicsQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Graphics);
|
_graphicsQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Graphics);
|
||||||
_computeQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Compute);
|
_computeQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Compute);
|
||||||
_copyQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Copy);
|
_copyQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
~D3D12RenderDevice()
|
~D3D12RenderDevice()
|
||||||
@@ -60,7 +58,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
|||||||
IDXGIAdapter1* pAdapter = default;
|
IDXGIAdapter1* pAdapter = default;
|
||||||
|
|
||||||
for (uint adapterIndex = 0;
|
for (uint adapterIndex = 0;
|
||||||
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, __uuidof<IDXGIAdapter1>(), (void**)&pAdapter).SUCCEEDED;
|
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, __uuidof(pAdapter), (void**)&pAdapter).SUCCEEDED;
|
||||||
adapterIndex++)
|
adapterIndex++)
|
||||||
{
|
{
|
||||||
DXGI_ADAPTER_DESC1 desc = default;
|
DXGI_ADAPTER_DESC1 desc = default;
|
||||||
@@ -84,30 +82,77 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
|||||||
|
|
||||||
if (pDevice == null)
|
if (pDevice == null)
|
||||||
{
|
{
|
||||||
pAdapter->Release(); // Dispose the last adapter we tried. If the operation succeeded, we would have moved it.
|
pAdapter->Release(); // Dispose the last adapter we tried.
|
||||||
throw new PlatformNotSupportedException("Cannot create ID3D12Device with feature level 12.0");
|
throw new PlatformNotSupportedException("Cannot create ID3D12Device with feature level 12.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
_device.Attach(pDevice);
|
nativeObject.Attach(pDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public FeatureSupport GetFeatureSupport()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
FeatureSupport support = FeatureSupport.None;
|
||||||
|
|
||||||
|
D3D12_FEATURE_DATA_D3D12_OPTIONS options = default;
|
||||||
|
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)).SUCCEEDED)
|
||||||
|
{
|
||||||
|
if (options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_3)
|
||||||
|
{
|
||||||
|
support |= FeatureSupport.BindlessResources;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_FEATURE_DATA_D3D12_OPTIONS5 options5 = default;
|
||||||
|
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options5, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS5)).SUCCEEDED)
|
||||||
|
{
|
||||||
|
if (options5.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED)
|
||||||
|
{
|
||||||
|
support |= FeatureSupport.RayTracing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_FEATURE_DATA_D3D12_OPTIONS6 options6 = default;
|
||||||
|
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS6)).SUCCEEDED)
|
||||||
|
{
|
||||||
|
if (options6.VariableShadingRateTier != D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED)
|
||||||
|
{
|
||||||
|
support |= FeatureSupport.VariableRateShading;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = default;
|
||||||
|
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS7)).SUCCEEDED)
|
||||||
|
{
|
||||||
|
if (options7.MeshShaderTier != D3D12_MESH_SHADER_TIER_NOT_SUPPORTED)
|
||||||
|
{
|
||||||
|
support |= FeatureSupport.MeshShaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options7.SamplerFeedbackTier != D3D12_SAMPLER_FEEDBACK_TIER_NOT_SUPPORTED)
|
||||||
|
{
|
||||||
|
support |= FeatureSupport.SamplerFeedback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return support;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (Disposed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_graphicsQueue?.Dispose();
|
_graphicsQueue.Dispose();
|
||||||
_computeQueue?.Dispose();
|
_computeQueue.Dispose();
|
||||||
_copyQueue?.Dispose();
|
_copyQueue.Dispose();
|
||||||
|
|
||||||
_device.Reset();
|
|
||||||
_dxgiFactory.Dispose();
|
_dxgiFactory.Dispose();
|
||||||
_adapter.Dispose();
|
_adapter.Dispose();
|
||||||
|
|
||||||
_disposed = true;
|
base.Dispose(disposing);
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -589,9 +589,9 @@ internal sealed unsafe partial class D3D12ResourceAllocator
|
|||||||
// TODO: Dedicated pool for copy, render graph, and persistent resources
|
// TODO: Dedicated pool for copy, render graph, and persistent resources
|
||||||
|
|
||||||
// TODO: Thread safety for resource allocator
|
// TODO: Thread safety for resource allocator
|
||||||
// A common solution is to use ticket. Each allocation request create a ticket and put it into a thread-safe queue. A dedicated thread process the queue and fulfill the requests.
|
// A common solution is to use ticket. Each pAllocation request create a ticket and put it into a thread-safe queue. A dedicated thread process the queue and fulfill the requests.
|
||||||
|
|
||||||
internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D12MA_Allocator>, IResourceAllocator
|
||||||
{
|
{
|
||||||
private readonly IFenceSynchronizer _fenceSynchronizer;
|
private readonly IFenceSynchronizer _fenceSynchronizer;
|
||||||
private readonly D3D12RenderDevice _device;
|
private readonly D3D12RenderDevice _device;
|
||||||
@@ -599,11 +599,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||||
|
|
||||||
private ComPtr<D3D12MA_Allocator> _allocator;
|
|
||||||
private UnsafeQueue<Handle<GPUResource>> _temResources;
|
private UnsafeQueue<Handle<GPUResource>> _temResources;
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public D3D12ResourceAllocator(
|
public D3D12ResourceAllocator(
|
||||||
IFenceSynchronizer fenceSynchronizer,
|
IFenceSynchronizer fenceSynchronizer,
|
||||||
D3D12RenderDevice device,
|
D3D12RenderDevice device,
|
||||||
@@ -620,7 +617,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
D3D12MA_Allocator* pAllocator = default;
|
D3D12MA_Allocator* pAllocator = default;
|
||||||
ThrowIfFailed(D3D12MA_CreateAllocator(&desc, &pAllocator));
|
ThrowIfFailed(D3D12MA_CreateAllocator(&desc, &pAllocator));
|
||||||
_allocator.Attach(pAllocator);
|
nativeObject.Attach(pAllocator);
|
||||||
|
|
||||||
_fenceSynchronizer = fenceSynchronizer;
|
_fenceSynchronizer = fenceSynchronizer;
|
||||||
_device = device;
|
_device = device;
|
||||||
@@ -637,7 +634,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private Handle<GPUResource> TrackResource(ComPtr<D3D12MA_Allocation> allocation, D3D12_RESOURCE_STATES state, ResourceViewGroup resourceDescriptor, ResourceDesc desc, bool isTemp)
|
private Handle<GPUResource> TrackResource(D3D12MA_Allocation* allocation, D3D12_RESOURCE_STATES state, ResourceViewGroup resourceDescriptor, ResourceDesc desc, bool isTemp)
|
||||||
{
|
{
|
||||||
var handle = _resourceDatabase.AddResource(allocation, _fenceSynchronizer.CPUFenceValue, D3D12StatesToRHIState(state), resourceDescriptor, desc);
|
var handle = _resourceDatabase.AddResource(allocation, _fenceSynchronizer.CPUFenceValue, D3D12StatesToRHIState(state), resourceDescriptor, desc);
|
||||||
|
|
||||||
@@ -651,6 +648,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool isTemp = false)
|
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool isTemp = false)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
CheckTexture2DSize(desc.Width, desc.Height);
|
CheckTexture2DSize(desc.Width, desc.Height);
|
||||||
|
|
||||||
var d3d12Format = ConvertTextureFormat(desc.Format);
|
var d3d12Format = ConvertTextureFormat(desc.Format);
|
||||||
@@ -706,8 +705,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
var initialState = DetermineInitialTextureState(desc.Usage);
|
var initialState = DetermineInitialTextureState(desc.Usage);
|
||||||
|
|
||||||
ComPtr<D3D12MA_Allocation> allocation = default;
|
D3D12MA_Allocation* pAllocation = default;
|
||||||
ThrowIfFailed(_allocator.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, allocation.GetAddressOf(), Win32Utility.IID_NULL, null));
|
ThrowIfFailed(nativeObject.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, &pAllocation, Win32Utility.IID_NULL, null));
|
||||||
|
|
||||||
var resourceDescriptor = ResourceViewGroup.Invalid;
|
var resourceDescriptor = ResourceViewGroup.Invalid;
|
||||||
if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
|
if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
|
||||||
@@ -716,51 +715,54 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
|
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
|
||||||
|
|
||||||
var isCubeMap = desc.Dimension == TextureDimension.TextureCube || desc.Dimension == TextureDimension.TextureCubeArray;
|
var isCubeMap = desc.Dimension == TextureDimension.TextureCube || desc.Dimension == TextureDimension.TextureCubeArray;
|
||||||
var srvDesc = CreateTextureSrvDesc(allocation.Get()->GetResource(), mipLevels, desc.Slice, isCubeMap);
|
var srvDesc = CreateTextureSrvDesc(pAllocation->GetResource(), mipLevels, desc.Slice, isCubeMap);
|
||||||
|
|
||||||
_device.NativeDevice->CreateShaderResourceView(allocation.Get()->GetResource(), &srvDesc, cpuHandle);
|
_device.NativeDevice->CreateShaderResourceView(pAllocation->GetResource(), &srvDesc, cpuHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc.Usage.HasFlag(TextureUsage.RenderTarget))
|
if (desc.Usage.HasFlag(TextureUsage.RenderTarget))
|
||||||
{
|
{
|
||||||
resourceDescriptor.rtv = _descriptorAllocator.AllocateRTV(isTemp);
|
resourceDescriptor.rtv = _descriptorAllocator.AllocateRTV(isTemp);
|
||||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.rtv);
|
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.rtv);
|
||||||
var rtvDesc = CreateRtvDesc(allocation.Get()->GetResource());
|
var rtvDesc = CreateRtvDesc(pAllocation->GetResource());
|
||||||
|
|
||||||
_device.NativeDevice->CreateRenderTargetView(allocation.Get()->GetResource(), &rtvDesc, cpuHandle);
|
_device.NativeDevice->CreateRenderTargetView(pAllocation->GetResource(), &rtvDesc, cpuHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc.Usage.HasFlag(TextureUsage.DepthStencil))
|
if (desc.Usage.HasFlag(TextureUsage.DepthStencil))
|
||||||
{
|
{
|
||||||
resourceDescriptor.dsv = _descriptorAllocator.AllocateDSV(isTemp);
|
resourceDescriptor.dsv = _descriptorAllocator.AllocateDSV(isTemp);
|
||||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.dsv);
|
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.dsv);
|
||||||
var dsvDesc = CreateDsvDesc(allocation.Get()->GetResource());
|
var dsvDesc = CreateDsvDesc(pAllocation->GetResource());
|
||||||
|
|
||||||
_device.NativeDevice->CreateDepthStencilView(allocation.Get()->GetResource(), &dsvDesc, cpuHandle);
|
_device.NativeDevice->CreateDepthStencilView(pAllocation->GetResource(), &dsvDesc, cpuHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess))
|
if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess))
|
||||||
{
|
{
|
||||||
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
|
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
|
||||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
|
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
|
||||||
var uavDesc = CreateTextureUavDesc(allocation.Get()->GetResource());
|
var uavDesc = CreateTextureUavDesc(pAllocation->GetResource());
|
||||||
|
|
||||||
_device.NativeDevice->CreateUnorderedAccessView(allocation.Get()->GetResource(), null, &uavDesc, cpuHandle);
|
_device.NativeDevice->CreateUnorderedAccessView(pAllocation->GetResource(), null, &uavDesc, cpuHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
var handle = TrackResource(allocation, initialState, resourceDescriptor, ResourceDesc.Texture(desc), isTemp);
|
var handle = TrackResource(pAllocation, initialState, resourceDescriptor, ResourceDesc.Texture(desc), isTemp);
|
||||||
|
|
||||||
return handle.AsTexture();
|
return handle.AsTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, bool isTemp = false)
|
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, bool isTemp = false)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var textureDesc = desc.ToTextureDescripton();
|
var textureDesc = desc.ToTextureDescripton();
|
||||||
return CreateTexture(ref textureDesc, isTemp);
|
return CreateTexture(ref textureDesc, isTemp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, bool isTemp = false)
|
public Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, bool isTemp = false)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
CheckBufferSize(desc.Size);
|
CheckBufferSize(desc.Size);
|
||||||
|
|
||||||
var resourceDescription = D3D12_RESOURCE_DESC.Buffer(desc.Size, ConvertBufferUsage(desc.Usage));
|
var resourceDescription = D3D12_RESOURCE_DESC.Buffer(desc.Size, ConvertBufferUsage(desc.Usage));
|
||||||
@@ -778,11 +780,11 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
var initialState = DetermineInitialBufferState(desc.Usage, desc.MemoryType);
|
var initialState = DetermineInitialBufferState(desc.Usage, desc.MemoryType);
|
||||||
|
|
||||||
ComPtr<D3D12MA_Allocation> allocation = default;
|
D3D12MA_Allocation* pAllocation = default;
|
||||||
ThrowIfFailed(_allocator.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, allocation.GetAddressOf(), Win32Utility.IID_NULL, null));
|
ThrowIfFailed(nativeObject.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, &pAllocation, Win32Utility.IID_NULL, null));
|
||||||
|
|
||||||
var resourceDescriptor = ResourceViewGroup.Invalid;
|
var resourceDescriptor = ResourceViewGroup.Invalid;
|
||||||
var pResource = allocation.Get()->GetResource();
|
var pResource = pAllocation->GetResource();
|
||||||
|
|
||||||
if (desc.Usage.HasFlag(BufferUsage.Constant))
|
if (desc.Usage.HasFlag(BufferUsage.Constant))
|
||||||
{
|
{
|
||||||
@@ -803,7 +805,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
{
|
{
|
||||||
resourceDescriptor.srv = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
|
resourceDescriptor.srv = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
|
||||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
|
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
|
||||||
var srvDesc = CreateBufferSrvDesc(allocation.Get()->GetResource(), desc.Stride, isRaw);
|
var srvDesc = CreateBufferSrvDesc(pAllocation->GetResource(), desc.Stride, isRaw);
|
||||||
|
|
||||||
_device.NativeDevice->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
|
_device.NativeDevice->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
|
||||||
}
|
}
|
||||||
@@ -812,17 +814,19 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
{
|
{
|
||||||
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
|
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
|
||||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
|
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
|
||||||
var uavDesc = CreateBufferUavDesc(allocation.Get()->GetResource(), desc.Stride, isRaw);
|
var uavDesc = CreateBufferUavDesc(pAllocation->GetResource(), desc.Stride, isRaw);
|
||||||
|
|
||||||
_device.NativeDevice->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
|
_device.NativeDevice->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
var handle = TrackResource(allocation, initialState, resourceDescriptor, ResourceDesc.Buffer(desc), isTemp);
|
var handle = TrackResource(pAllocation, initialState, resourceDescriptor, ResourceDesc.Buffer(desc), isTemp);
|
||||||
return handle.AsGraphicsBuffer();
|
return handle.AsGraphicsBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<GraphicsBuffer> CreateUploadBuffer(ulong size, bool isTemp = true)
|
public Handle<GraphicsBuffer> CreateUploadBuffer(ulong size, bool isTemp = true)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var desc = new BufferDesc
|
var desc = new BufferDesc
|
||||||
{
|
{
|
||||||
Size = size,
|
Size = size,
|
||||||
@@ -835,6 +839,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
|
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var vertexBufferDesc = new BufferDesc
|
var vertexBufferDesc = new BufferDesc
|
||||||
{
|
{
|
||||||
Size = (uint)(vertices.Count * Unsafe.SizeOf<Vertex>()),
|
Size = (uint)(vertices.Count * Unsafe.SizeOf<Vertex>()),
|
||||||
@@ -867,14 +873,18 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
|
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var material = new Material();
|
var material = new Material();
|
||||||
material.SetShader(shader, this, _resourceDatabase);
|
material.SetShader(shader, this, _resourceDatabase);
|
||||||
|
|
||||||
return _resourceDatabase.AddMaterial(ref material);
|
return _resourceDatabase.AddMaterial(ref material);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Identifier<Shader> CreateShader(ShaderDescriptor descriptor)
|
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var shader = new Shader(descriptor);
|
var shader = new Shader(descriptor);
|
||||||
foreach (var pass in descriptor.passes)
|
foreach (var pass in descriptor.passes)
|
||||||
{
|
{
|
||||||
@@ -883,9 +893,14 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var passKey = new ShaderPassKey(fullPass.uniqueIdentifier);
|
var passKey = new ShaderPassKey(fullPass.Identifier);
|
||||||
var cbufferInfo = _pipelineLibrary.GetCBufferInfo(passKey).GetValueOrThrow();
|
var cbr = _pipelineLibrary.GetCBufferInfo(passKey);
|
||||||
_resourceDatabase.AddShaderPass(new ShaderPassKey(fullPass.uniqueIdentifier), new ShaderPass(cbufferInfo));
|
if (cbr.Status != ResultStatus.Success)
|
||||||
|
{
|
||||||
|
return Identifier<Shader>.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
_resourceDatabase.AddShaderPass(passKey, new ShaderPass(cbr.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return _resourceDatabase.AddShader(shader);
|
return _resourceDatabase.AddShader(shader);
|
||||||
@@ -893,6 +908,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
public void ReleaseTempResources()
|
public void ReleaseTempResources()
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
while (_temResources.Count > 0)
|
while (_temResources.Count > 0)
|
||||||
{
|
{
|
||||||
var handle = _temResources.Peek();
|
var handle = _temResources.Peek();
|
||||||
@@ -916,9 +933,9 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (Disposed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -936,9 +953,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
}
|
}
|
||||||
|
|
||||||
_temResources.Dispose();
|
_temResources.Dispose();
|
||||||
_allocator.Dispose();
|
|
||||||
|
|
||||||
_disposed = true;
|
base.Dispose(disposing);
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Ghost.Core;
|
|||||||
using Ghost.Graphics.Core;
|
using Ghost.Graphics.Core;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Misaki.HighPerformance.Collections;
|
using Misaki.HighPerformance.Collections;
|
||||||
|
using Misaki.HighPerformance.LowLevel;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -19,16 +20,16 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
|||||||
public struct ResourceUnion
|
public struct ResourceUnion
|
||||||
{
|
{
|
||||||
[FieldOffset(0)]
|
[FieldOffset(0)]
|
||||||
public ComPtr<D3D12MA_Allocation> allocation;
|
public UniquePtr<D3D12MA_Allocation> allocation;
|
||||||
[FieldOffset(0)]
|
[FieldOffset(0)]
|
||||||
public ComPtr<ID3D12Resource> resource;
|
public UniquePtr<ID3D12Resource> resource;
|
||||||
|
|
||||||
public ResourceUnion(ComPtr<D3D12MA_Allocation> allocation)
|
public ResourceUnion(D3D12MA_Allocation* allocation)
|
||||||
{
|
{
|
||||||
this.allocation = allocation;
|
this.allocation = allocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceUnion(ComPtr<ID3D12Resource> resource)
|
public ResourceUnion(ID3D12Resource* resource)
|
||||||
{
|
{
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
}
|
}
|
||||||
@@ -44,7 +45,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
|||||||
public readonly bool Allocated => isExternal ? resourceUnion.resource.Get() != null : resourceUnion.allocation.Get() != null;
|
public readonly bool Allocated => isExternal ? resourceUnion.resource.Get() != null : resourceUnion.allocation.Get() != null;
|
||||||
public readonly ID3D12Resource* ResourcePtr => isExternal ? resourceUnion.resource.Get() : resourceUnion.allocation.Get()->GetResource();
|
public readonly ID3D12Resource* ResourcePtr => isExternal ? resourceUnion.resource.Get() : resourceUnion.allocation.Get()->GetResource();
|
||||||
|
|
||||||
public ResourceRecord(ComPtr<D3D12MA_Allocation> allocation, uint cpuFenceValue, ResourceState state, ResourceViewGroup resourceDescriptor, ResourceDesc desc)
|
public ResourceRecord(D3D12MA_Allocation* allocation, uint cpuFenceValue, ResourceState state, ResourceViewGroup resourceDescriptor, ResourceDesc desc)
|
||||||
{
|
{
|
||||||
this.resourceUnion = new ResourceUnion(allocation);
|
this.resourceUnion = new ResourceUnion(allocation);
|
||||||
this.isExternal = false;
|
this.isExternal = false;
|
||||||
@@ -55,7 +56,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
|||||||
this.desc = desc;
|
this.desc = desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceRecord(ComPtr<ID3D12Resource> resource, ResourceState state)
|
public ResourceRecord(ID3D12Resource* resource, ResourceState state)
|
||||||
{
|
{
|
||||||
this.resourceUnion = new ResourceUnion(resource);
|
this.resourceUnion = new ResourceUnion(resource);
|
||||||
this.isExternal = true;
|
this.isExternal = true;
|
||||||
@@ -63,7 +64,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
|||||||
this.viewGroup = default;
|
this.viewGroup = default;
|
||||||
this.cpuFenceValue = ~0u;
|
this.cpuFenceValue = ~0u;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.desc = ResourceDesc.FromD3D12(resource.Get()->GetDesc());
|
this.desc = ResourceDesc.FromD3D12(resource->GetDesc());
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Release(D3D12DescriptorAllocator descriptorAllocator)
|
public uint Release(D3D12DescriptorAllocator descriptorAllocator)
|
||||||
@@ -73,11 +74,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
|||||||
{
|
{
|
||||||
if (isExternal)
|
if (isExternal)
|
||||||
{
|
{
|
||||||
refCount = resourceUnion.resource.Reset();
|
refCount = resourceUnion.resource.Get()->Release();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
refCount = resourceUnion.allocation.Reset();
|
refCount = resourceUnion.allocation.Get()->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceUnion = default;
|
resourceUnion = default;
|
||||||
@@ -131,11 +132,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
|||||||
resource = default!;
|
resource = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<GPUResource> ImportExternalResource(ComPtr<ID3D12Resource> resource, ResourceState initialState, string? name = null)
|
public unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceState initialState, string? name = null)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
var id = _resources.Add(new ResourceRecord(resource, initialState), out var generation);
|
var id = _resources.Add(new ResourceRecord(pResource, initialState), out var generation);
|
||||||
var handle = new Handle<GPUResource>(id, generation);
|
var handle = new Handle<GPUResource>(id, generation);
|
||||||
|
|
||||||
#if DEBUG || GHOST_EDITOR
|
#if DEBUG || GHOST_EDITOR
|
||||||
@@ -148,7 +149,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
|||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handle<GPUResource> AddResource(ComPtr<D3D12MA_Allocation> allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
|
public unsafe Handle<GPUResource> AddResource(D3D12MA_Allocation* allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,11 @@ namespace Ghost.Graphics.D3D12;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// D3D12 implementation of swap chain interface
|
/// D3D12 implementation of swap chain interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal unsafe class D3D12SwapChain : ISwapChain
|
internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapChain
|
||||||
{
|
{
|
||||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||||
|
|
||||||
private ComPtr<IDXGISwapChain4> _swapChain;
|
|
||||||
private UnsafeArray<Handle<Texture>> _backBuffers;
|
private UnsafeArray<Handle<Texture>> _backBuffers;
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public uint Width
|
public uint Width
|
||||||
{
|
{
|
||||||
@@ -109,37 +107,42 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
pTempSwapChain->QueryInterface(__uuidof(pSwapChain), (void**)&pSwapChain);
|
pTempSwapChain->QueryInterface(__uuidof(pSwapChain), (void**)&pSwapChain);
|
||||||
pTempSwapChain->Release();
|
pTempSwapChain->Release();
|
||||||
|
|
||||||
_swapChain.Attach(pSwapChain);
|
nativeObject.Attach(pSwapChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateBackBuffers()
|
private void CreateBackBuffers()
|
||||||
{
|
{
|
||||||
for (uint i = 0; i < BufferCount; i++)
|
for (uint i = 0; i < BufferCount; i++)
|
||||||
{
|
{
|
||||||
ComPtr<ID3D12Resource> backBuffer = default;
|
ID3D12Resource* pBackBuffer = default;
|
||||||
_swapChain.Get()->GetBuffer(i, backBuffer.IID(), backBuffer.PPV());
|
nativeObject.Get()->GetBuffer(i, __uuidof(pBackBuffer), (void**)&pBackBuffer);
|
||||||
backBuffer.Get()->SetName($"SwapChain_BackBuffer_{i}");
|
pBackBuffer->SetName($"SwapChain_BackBuffer_{i}");
|
||||||
|
|
||||||
_backBuffers[i] = _resourceDatabase.ImportExternalResource(backBuffer.Move(), ResourceState.Present).AsTexture();
|
_backBuffers[i] = _resourceDatabase.ImportExternalResource(pBackBuffer, ResourceState.Present).AsTexture();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Handle<Texture> GetCurrentBackBuffer()
|
public Handle<Texture> GetCurrentBackBuffer()
|
||||||
{
|
{
|
||||||
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
|
ThrowIfDisposed();
|
||||||
|
return _backBuffers[nativeObject.Get()->GetCurrentBackBufferIndex()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Present(bool vsync = true)
|
public void Present(bool vsync = true)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var presentFlags = 0u;
|
var presentFlags = 0u;
|
||||||
var syncInterval = vsync ? 1u : 0u;
|
var syncInterval = vsync ? 1u : 0u;
|
||||||
|
|
||||||
ThrowIfFailed(_swapChain.Get()->Present(syncInterval, presentFlags));
|
ThrowIfFailed(nativeObject.Get()->Present(syncInterval, presentFlags));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resize(uint width, uint height)
|
public void Resize(uint width, uint height)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
if (Width == width && Height == height)
|
if (Width == width && Height == height)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -152,7 +155,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resize the swap chain
|
// Resize the swap chain
|
||||||
if (_swapChain.Get()->ResizeBuffers(BufferCount, width, height, DXGI_FORMAT_B8G8R8A8_UNORM, (uint)DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING).FAILED)
|
if (nativeObject.Get()->ResizeBuffers(BufferCount, width, height, DXGI_FORMAT_B8G8R8A8_UNORM, (uint)DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING).FAILED)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Failed to resize swap chain buffers.");
|
throw new InvalidOperationException("Failed to resize swap chain buffers.");
|
||||||
}
|
}
|
||||||
@@ -164,9 +167,9 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
CreateBackBuffers();
|
CreateBackBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (Disposed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -176,8 +179,8 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
_resourceDatabase.ReleaseResource(_backBuffers[i].AsResource());
|
_resourceDatabase.ReleaseResource(_backBuffers[i].AsResource());
|
||||||
}
|
}
|
||||||
|
|
||||||
_swapChain.Dispose();
|
|
||||||
_backBuffers.Dispose();
|
_backBuffers.Dispose();
|
||||||
_disposed = true;
|
|
||||||
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using Ghost.Core.Utilities;
|
using Ghost.Core.Utilities;
|
||||||
|
using Misaki.HighPerformance.LowLevel;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
|
||||||
|
|
||||||
using static TerraFX.Aliases.D3D12_Alias;
|
using static TerraFX.Aliases.D3D12_Alias;
|
||||||
|
|
||||||
@@ -16,8 +16,8 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
|
|||||||
|
|
||||||
private readonly D3D12RenderDevice _device;
|
private readonly D3D12RenderDevice _device;
|
||||||
|
|
||||||
private ComPtr<ID3D12DescriptorHeap> _heap;
|
private UniquePtr<ID3D12DescriptorHeap> _heap;
|
||||||
private ComPtr<ID3D12DescriptorHeap> _shaderVisibleHeap;
|
private UniquePtr<ID3D12DescriptorHeap> _shaderVisibleHeap;
|
||||||
private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandle;
|
private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandle;
|
||||||
private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandleShaderVisible;
|
private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandleShaderVisible;
|
||||||
private D3D12_GPU_DESCRIPTOR_HANDLE _startGpuHandleShaderVisible;
|
private D3D12_GPU_DESCRIPTOR_HANDLE _startGpuHandleShaderVisible;
|
||||||
@@ -323,18 +323,25 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
|
|||||||
var oldSize = NumDescriptors;
|
var oldSize = NumDescriptors;
|
||||||
var newSize = (int)BitOperations.RoundUpToPowerOf2((uint)minRequiredSize);
|
var newSize = (int)BitOperations.RoundUpToPowerOf2((uint)minRequiredSize);
|
||||||
|
|
||||||
using var oldHeap = _heap;
|
var oldHeap = _heap.Detach();
|
||||||
|
|
||||||
if (!AllocateResources(newSize))
|
try
|
||||||
{
|
{
|
||||||
return false;
|
if (!AllocateResources(newSize))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_device.NativeDevice->CopyDescriptorsSimple((uint)oldSize, _startCpuHandle, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||||
|
|
||||||
|
if (_shaderVisibleHeap.Get() != null)
|
||||||
|
{
|
||||||
|
_device.NativeDevice->CopyDescriptorsSimple((uint)oldSize, _startCpuHandleShaderVisible, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
_device.NativeDevice->CopyDescriptorsSimple((uint)oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
|
||||||
|
|
||||||
if (_shaderVisibleHeap.Get() != null)
|
|
||||||
{
|
{
|
||||||
_device.NativeDevice->CopyDescriptorsSimple((uint)oldSize, _startCpuHandleShaderVisible, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
oldHeap->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -2784,7 +2784,6 @@ public static partial class D3D12_Alias
|
|||||||
public const D3D12_BARRIER_LAYOUT D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE = D3D12_BARRIER_LAYOUT.D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE;
|
public const D3D12_BARRIER_LAYOUT D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE = D3D12_BARRIER_LAYOUT.D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE;
|
||||||
public const D3D12_BARRIER_LAYOUT D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE = D3D12_BARRIER_LAYOUT.D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE;
|
public const D3D12_BARRIER_LAYOUT D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE = D3D12_BARRIER_LAYOUT.D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE;
|
||||||
public const D3D12_BARRIER_LAYOUT D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST = D3D12_BARRIER_LAYOUT.D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST;
|
public const D3D12_BARRIER_LAYOUT D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST = D3D12_BARRIER_LAYOUT.D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST;
|
||||||
public const D3D12_BARRIER_LAYOUT D3D12_BARRIER_LAYOUT_VIDEO_QUEUE_COMMON = D3D12_BARRIER_LAYOUT.D3D12_BARRIER_LAYOUT_VIDEO_QUEUE_COMMON;
|
|
||||||
public const D3D12_BARRIER_LAYOUT D3D12_BARRIER_LAYOUT_UNDEFINED = D3D12_BARRIER_LAYOUT.D3D12_BARRIER_LAYOUT_UNDEFINED;
|
public const D3D12_BARRIER_LAYOUT D3D12_BARRIER_LAYOUT_UNDEFINED = D3D12_BARRIER_LAYOUT.D3D12_BARRIER_LAYOUT_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,12 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Misaki.HighPerformance.Analyzer" Version="1.0.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Misaki.HighPerformance.Image" Version="1.0.0" />
|
<PackageReference Include="Misaki.HighPerformance.Image" Version="1.0.0" />
|
||||||
<PackageReference Include="TerraFX.Interop.D3D12MemoryAllocator" Version="2.0.1.5" />
|
<PackageReference Include="TerraFX.Interop.D3D12MemoryAllocator" Version="3.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public readonly struct GraphicsPipelineKey
|
|||||||
{
|
{
|
||||||
if (!value.TryFormat(destination, out _, "X16"))
|
if (!value.TryFormat(destination, out _, "X16"))
|
||||||
{
|
{
|
||||||
return Result.Fail("Failed to format GraphicsPipelineKey to string.");
|
return Result.Failure("Failed to format GraphicsPipelineKey to string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
destination[16] = '\0';
|
destination[16] = '\0';
|
||||||
@@ -166,7 +166,7 @@ public ref struct GraphicsPSODescriptor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct CBufferPropertyInfo
|
public readonly record struct CBufferPropertyInfo
|
||||||
{
|
{
|
||||||
public string Name
|
public string Name
|
||||||
{
|
{
|
||||||
@@ -184,7 +184,7 @@ public readonly struct CBufferPropertyInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct CBufferInfo
|
public readonly record struct CBufferInfo
|
||||||
{
|
{
|
||||||
public string Name
|
public string Name
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ public interface ICommandBuffer : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uploads the specified data to the buffer represented by the given handle.
|
/// Uploads the specified data to the buffer represented by the given handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The unmanaged value type of the elements to upload to the buffer.</typeparam>
|
/// <typeparam name="T">The unmanaged Value type of the elements to upload to the buffer.</typeparam>
|
||||||
/// <param name="buffer">A handle to the buffer that will receive the uploaded data.</param>
|
/// <param name="buffer">A handle to the buffer that will receive the uploaded data.</param>
|
||||||
/// <param name="data">A read-only span containing the data to upload to the buffer. The span must contain elements of type
|
/// <param name="data">A read-only span containing the data to upload to the buffer. The span must contain elements of type
|
||||||
/// <typeparamref name="T"/>.</param>
|
/// <typeparamref name="T"/>.</param>
|
||||||
|
|||||||
@@ -26,22 +26,22 @@ public interface ICommandQueue : IDisposable
|
|||||||
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers);
|
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signals a fence with the specified value
|
/// Signals a fence with the specified Value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">Value to signal</param>
|
/// <param name="value">Value to signal</param>
|
||||||
/// <returns>The fence value that was signaled</returns>
|
/// <returns>The fence Value that was signaled</returns>
|
||||||
public ulong Signal(ulong value);
|
public ulong Signal(ulong value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Waits for the fence to reach the specified value
|
/// Waits for the fence to reach the specified Value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">Value to wait for</param>
|
/// <param name="value">Value to wait for</param>
|
||||||
public void WaitForValue(ulong value);
|
public void WaitForValue(ulong value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the last completed fence value
|
/// Gets the last completed fence Value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Last completed fence value</returns>
|
/// <returns>Last completed fence Value</returns>
|
||||||
public ulong GetCompletedValue();
|
public ulong GetCompletedValue();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
namespace Ghost.Graphics.RHI;
|
namespace Ghost.Graphics.RHI;
|
||||||
|
|
||||||
public interface IGraphicsEngine : IDisposable
|
public interface IGraphicsEngine : IDisposable
|
||||||
@@ -7,6 +9,11 @@ public interface IGraphicsEngine : IDisposable
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IShaderCompiler ShaderCompiler
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
IPipelineLibrary PipelineLibrary
|
IPipelineLibrary PipelineLibrary
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Ghost.Core.Graphics;
|
using Ghost.Core;
|
||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
namespace Ghost.Graphics.RHI;
|
namespace Ghost.Graphics.RHI;
|
||||||
|
|
||||||
@@ -21,5 +22,6 @@ public interface IPipelineLibrary
|
|||||||
/// <param name="filePath">File path. If null, load default library.</param>
|
/// <param name="filePath">File path. If null, load default library.</param>
|
||||||
void InitializeLibrary(string? filePath);
|
void InitializeLibrary(string? filePath);
|
||||||
void SaveLibraryToDisk(string filePath);
|
void SaveLibraryToDisk(string filePath);
|
||||||
GraphicsPipelineKey CompilePassPSO(IPassDescriptor descriptor, ReadOnlySpan<TextureFormat> rtvs, TextureFormat dsv);
|
Result<GraphicsPipelineKey> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);
|
||||||
|
Result<CBufferInfo, ResultStatus> GetCBufferInfo(ShaderPassKey passId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
namespace Ghost.Graphics.RHI;
|
namespace Ghost.Graphics.RHI;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum FeatureSupport
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
RayTracing = 1 << 0,
|
||||||
|
VariableRateShading = 1 << 1,
|
||||||
|
MeshShaders = 1 << 2,
|
||||||
|
SamplerFeedback = 1 << 3,
|
||||||
|
BindlessResources = 1 << 4,
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// D3D12-native render device interface for creating graphics resources
|
/// D3D12-native render device interface for creating graphics resources
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -28,4 +39,6 @@ public interface IRenderDevice : IDisposable
|
|||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FeatureSupport GetFeatureSupport();
|
||||||
}
|
}
|
||||||
@@ -48,5 +48,5 @@ public interface IResourceAllocator
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
|
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
|
||||||
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
|
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
|
||||||
public Identifier<Shader> CreateShader(ShaderDescriptor descriptor);
|
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ public interface IResourceDatabase
|
|||||||
/// Retrieves the current state of the specified resource.
|
/// Retrieves the current state of the specified resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="handle">The handle that uniquely identifies the resource whose state is to be retrieved.</param>
|
/// <param name="handle">The handle that uniquely identifies the resource whose state is to be retrieved.</param>
|
||||||
/// <returns>A ResourceState value representing the current state of the resource associated with the specified handle.</returns>
|
/// <returns>A ResourceState Value representing the current state of the resource associated with the specified handle.</returns>
|
||||||
ResourceState GetResourceState(Handle<GPUResource> handle);
|
ResourceState GetResourceState(Handle<GPUResource> handle);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the state of the specified resource handle to the given value.
|
/// Sets the state of the specified resource handle to the given Value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="handle">The handle that identifies the resource whose state will be updated.</param>
|
/// <param name="handle">The handle that identifies the resource whose state will be updated.</param>
|
||||||
/// <param name="state">The new state to assign to the resource represented by <paramref name="handle"/>.</param>
|
/// <param name="state">The new state to assign to the resource represented by <paramref name="handle"/>.</param>
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.Core;
|
using Ghost.Core.Graphics;
|
||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Ghost.Graphics.Core;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Ghost.Graphics.Utilities;
|
using Ghost.Graphics.Utilities;
|
||||||
using Ghost.SDL.Compiler;
|
using Ghost.SDL.Compiler;
|
||||||
using Misaki.HighPerformance.Image;
|
using Misaki.HighPerformance.Image;
|
||||||
|
using Misaki.HighPerformance.Utilities;
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
namespace Ghost.Graphics.RenderPasses;
|
namespace Ghost.Graphics.RenderPasses;
|
||||||
|
|
||||||
@@ -30,14 +33,36 @@ internal unsafe class MeshRenderPass : IRenderPass
|
|||||||
{
|
{
|
||||||
var shaderDescriptor = SDLCompiler.CompileShader("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
|
var shaderDescriptor = SDLCompiler.CompileShader("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
|
||||||
|
|
||||||
var key = ctx.PipelineLibrary.CompilePassPSO(shaderDescriptor.passes[0], [TextureFormat.B8G8R8A8_UNorm], TextureFormat.Unknown);
|
foreach (var pass in shaderDescriptor.passes)
|
||||||
|
{
|
||||||
|
var compileResult = ctx.ShaderCompiler.CompilePass(pass);
|
||||||
|
if (compileResult.IsFailure || pass is not FullPassDescriptor fullPass)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var psoDes = new GraphicsPSODescriptor
|
||||||
|
{
|
||||||
|
PassId = new ShaderPassKey(fullPass.Identifier),
|
||||||
|
ZTest = fullPass.localPipeline.zTest,
|
||||||
|
ZWrite = fullPass.localPipeline.zWrite,
|
||||||
|
Cull = fullPass.localPipeline.cull,
|
||||||
|
Blend = fullPass.localPipeline.blend,
|
||||||
|
ColorMask = fullPass.localPipeline.colorMask,
|
||||||
|
|
||||||
|
RtvFormats = [TextureFormat.B8G8R8A8_UNorm],
|
||||||
|
DsvFormat = TextureFormat.Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.PipelineLibrary.CompilePSO(in psoDes, in compileResult.GetValueRef()).GetValueOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
MeshBuilder.CreateCube(0.75f, default, out var vertices, out var indices);
|
MeshBuilder.CreateCube(0.75f, default, out var vertices, out var indices);
|
||||||
|
|
||||||
_mesh = ctx.CreateMesh(vertices, indices);
|
_mesh = ctx.CreateMesh(vertices, indices);
|
||||||
ctx.UploadMesh(_mesh, true);
|
ctx.UploadMesh(_mesh, true);
|
||||||
|
|
||||||
_shader = ctx.ResourceAllocator.CreateShader(shaderDescriptor);
|
_shader = ctx.ResourceAllocator.CreateGraphicsShader(shaderDescriptor);
|
||||||
_material = ctx.ResourceAllocator.CreateMaterial(_shader);
|
_material = ctx.ResourceAllocator.CreateMaterial(_shader);
|
||||||
|
|
||||||
var imageResults = new ImageResult[_textureFiles.Length];
|
var imageResults = new ImageResult[_textureFiles.Length];
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
|
using Ghost.Core.Graphics;
|
||||||
|
using Ghost.Core.Utilities;
|
||||||
|
using Ghost.Graphics.Contracts;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
|
using Misaki.HighPerformance.LowLevel;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
using Misaki.HighPerformance.Utilities;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
using static TerraFX.Interop.DirectX.DXC;
|
using static TerraFX.Interop.DirectX.DXC;
|
||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.Utilities;
|
||||||
|
|
||||||
internal partial class D3D12ShaderCompiler
|
internal sealed partial class DxcShaderCompiler
|
||||||
{
|
{
|
||||||
private static string GetProfileString(ShaderStage stage, CompilerTier version)
|
private static string GetProfileString(ShaderStage stage, CompilerTier version)
|
||||||
{
|
{
|
||||||
@@ -84,6 +89,11 @@ internal partial class D3D12ShaderCompiler
|
|||||||
argsArray.Add(DXC_ARG_WARNINGS_ARE_ERRORS);
|
argsArray.Add(DXC_ARG_WARNINGS_ARE_ERRORS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.options.HasFlag(CompilerOption.SpirvCrossCompile))
|
||||||
|
{
|
||||||
|
argsArray.Add("-spirv");
|
||||||
|
}
|
||||||
|
|
||||||
return argsArray;
|
return argsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,12 +115,16 @@ internal partial class D3D12ShaderCompiler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe partial class D3D12ShaderCompiler : IShaderCompiler
|
internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler, IDisposable
|
||||||
{
|
{
|
||||||
private ComPtr<IDxcCompiler3> _compiler;
|
private UniquePtr<IDxcCompiler3> _compiler;
|
||||||
private ComPtr<IDxcUtils> _utils;
|
private UniquePtr<IDxcUtils> _utils;
|
||||||
|
// NOTE: This is just a temporary cache for compiled shader code. We will implement a proper disk cache later.
|
||||||
|
private readonly Dictionary<ShaderPassKey, GraphicsCompiledResult> _compiledResults;
|
||||||
|
|
||||||
public D3D12ShaderCompiler()
|
private bool _disposed;
|
||||||
|
|
||||||
|
public DxcShaderCompiler()
|
||||||
{
|
{
|
||||||
// Initialize DXC _compiler.Get() and _utils.Get()
|
// Initialize DXC _compiler.Get() and _utils.Get()
|
||||||
var dxccID = CLSID.CLSID_DxcCompiler;
|
var dxccID = CLSID.CLSID_DxcCompiler;
|
||||||
@@ -123,121 +137,19 @@ internal unsafe partial class D3D12ShaderCompiler : IShaderCompiler
|
|||||||
|
|
||||||
_compiler.Attach(pCompiler);
|
_compiler.Attach(pCompiler);
|
||||||
_utils.Attach(pUtils);
|
_utils.Attach(pUtils);
|
||||||
|
|
||||||
|
_compiledResults = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator, void** ppReflection)
|
~DxcShaderCompiler()
|
||||||
{
|
{
|
||||||
// NOTE: Should we cache the _compiler.Get() and _utils.Get() instances for better performance?
|
Dispose();
|
||||||
IDxcIncludeHandler* pIncludeHandler = default;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Create DXC _compiler.Get() and _utils.Get()
|
|
||||||
var dxccID = CLSID.CLSID_DxcCompiler;
|
|
||||||
var dxcuID = CLSID.CLSID_DxcUtils;
|
|
||||||
|
|
||||||
|
|
||||||
ThrowIfFailed(_utils.Get()->CreateDefaultIncludeHandler(&pIncludeHandler));
|
|
||||||
|
|
||||||
// Create source blob
|
|
||||||
using ComPtr<IDxcBlobEncoding> sourceBlob = default;
|
|
||||||
fixed (char* pPath = config.shaderPath)
|
|
||||||
{
|
|
||||||
if (_utils.Get()->LoadFile(pPath, null, sourceBlob.GetAddressOf()).FAILED)
|
|
||||||
{
|
|
||||||
return Result.Fail($"Failed to load shader file: {config.shaderPath}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var argsArray = GetCompilerArguments(in config);
|
|
||||||
var argPtrs = stackalloc char*[argsArray.Count];
|
|
||||||
for (var i = 0; i < argsArray.Count; i++)
|
|
||||||
{
|
|
||||||
argPtrs[i] = (char*)Marshal.StringToHGlobalUni(argsArray[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
IDxcResult* pResult = default;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Compile shader
|
|
||||||
var buffer = new DxcBuffer
|
|
||||||
{
|
|
||||||
Ptr = sourceBlob.Get()->GetBufferPointer(),
|
|
||||||
Size = sourceBlob.Get()->GetBufferSize(),
|
|
||||||
Encoding = DXC_CP_UTF8
|
|
||||||
};
|
|
||||||
|
|
||||||
ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, pIncludeHandler, __uuidof(pResult), (void**)&pResult));
|
|
||||||
|
|
||||||
// Check compilation pResult
|
|
||||||
HRESULT hrStatus;
|
|
||||||
pResult->GetStatus(&hrStatus);
|
|
||||||
if (hrStatus.FAILED)
|
|
||||||
{
|
|
||||||
// Get error messages
|
|
||||||
using ComPtr<IDxcBlobEncoding> errorBlob = default;
|
|
||||||
pResult->GetErrorBuffer(errorBlob.GetAddressOf());
|
|
||||||
|
|
||||||
if (errorBlob.Get() != null)
|
|
||||||
{
|
|
||||||
var errorMessage = Marshal.PtrToStringUTF8((IntPtr)errorBlob.Get()->GetBufferPointer());
|
|
||||||
return Result.Fail($"DXC shader compilation failed:\n{errorMessage}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Result.Fail("DXC shader compilation failed with unknown error.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get compiled bytecode
|
|
||||||
using ComPtr<IDxcBlob> bytecodeBlob = default;
|
|
||||||
ThrowIfFailed(pResult->GetResult(bytecodeBlob.GetAddressOf()));
|
|
||||||
|
|
||||||
// Get pReflection data using DXC API
|
|
||||||
if (ppReflection != null)
|
|
||||||
{
|
|
||||||
ThrowIfFailed(pResult->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), ppReflection, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
var bytecodeSize = bytecodeBlob.Get()->GetBufferSize();
|
|
||||||
var bytecode = new UnsafeArray<byte>((int)bytecodeSize, allocator);
|
|
||||||
|
|
||||||
NativeMemory.Copy(bytecodeBlob.Get()->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize);
|
|
||||||
|
|
||||||
return new CompileResult
|
|
||||||
{
|
|
||||||
bytecode = bytecode,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
for (var i = 0; i < argsArray.Count; i++)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal((nint)argPtrs[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pResult->Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
pIncludeHandler->Release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Since we are using fixed root signature layout, the pReflection pass should only validate the layout, not generate it.
|
private Result<ShaderReflectionData> PerformDXCReflection(IDxcBlob* pReflectionBlob)
|
||||||
// TODO: Ideally this should return a structured pReflection data instead of populating raw lists/dictionaries.
|
|
||||||
public Result<ShaderReflectionData> PerformDXCReflection<T>(T* pReflectionBlob)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
{
|
||||||
if (typeof(T) != typeof(IDxcBlob))
|
|
||||||
{
|
|
||||||
return Result<ShaderReflectionData>.Fail("Unsupported reflection type. Only IDxcBlob is supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ID3D12ShaderReflection* pReflection = default;
|
ID3D12ShaderReflection* pReflection = default;
|
||||||
IDxcBlob* pDxcReflectionBlob = (IDxcBlob*)pReflectionBlob;
|
var pDxcReflectionBlob = (IDxcBlob*)pReflectionBlob;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -267,7 +179,7 @@ internal unsafe partial class D3D12ShaderCompiler : IShaderCompiler
|
|||||||
var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name);
|
var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name);
|
||||||
if (resourceName == null)
|
if (resourceName == null)
|
||||||
{
|
{
|
||||||
return Result.Fail("Failed to get resource name from reflection data.");
|
return Result.Failure("Failed to get resource name from reflection data.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = new ResourceBindingInfo
|
var info = new ResourceBindingInfo
|
||||||
@@ -329,4 +241,262 @@ internal unsafe partial class D3D12ShaderCompiler : IShaderCompiler
|
|||||||
pReflection->Release();
|
pReflection->Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
IDxcIncludeHandler* pIncludeHandler = default;
|
||||||
|
IDxcBlobEncoding* pSourceBlob = default;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create DXC _compiler.Get() and _utils.Get()
|
||||||
|
var dxccID = CLSID.CLSID_DxcCompiler;
|
||||||
|
var dxcuID = CLSID.CLSID_DxcUtils;
|
||||||
|
|
||||||
|
|
||||||
|
ThrowIfFailed(_utils.Get()->CreateDefaultIncludeHandler(&pIncludeHandler));
|
||||||
|
|
||||||
|
// Create source blob
|
||||||
|
fixed (char* pPath = config.shaderPath)
|
||||||
|
{
|
||||||
|
if (_utils.Get()->LoadFile(pPath, null, &pSourceBlob).FAILED)
|
||||||
|
{
|
||||||
|
return Result.Failure($"Failed to load shader file: {config.shaderPath}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var argsArray = GetCompilerArguments(in config);
|
||||||
|
var argPtrs = stackalloc char*[argsArray.Count];
|
||||||
|
for (var i = 0; i < argsArray.Count; i++)
|
||||||
|
{
|
||||||
|
argPtrs[i] = (char*)Marshal.StringToHGlobalUni(argsArray[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
IDxcResult* pResult = default;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Compile shader
|
||||||
|
var buffer = new DxcBuffer
|
||||||
|
{
|
||||||
|
Ptr = pSourceBlob->GetBufferPointer(),
|
||||||
|
Size = pSourceBlob->GetBufferSize(),
|
||||||
|
Encoding = DXC_CP_UTF8
|
||||||
|
};
|
||||||
|
|
||||||
|
ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, pIncludeHandler, __uuidof(pResult), (void**)&pResult));
|
||||||
|
|
||||||
|
// Check compilation pResult
|
||||||
|
HRESULT hrStatus;
|
||||||
|
pResult->GetStatus(&hrStatus);
|
||||||
|
if (hrStatus.FAILED)
|
||||||
|
{
|
||||||
|
// Get error messages
|
||||||
|
IDxcBlobEncoding* pErrorBlob = default;
|
||||||
|
pResult->GetErrorBuffer(&pErrorBlob);
|
||||||
|
|
||||||
|
if (pErrorBlob != null)
|
||||||
|
{
|
||||||
|
var errorMessage = Marshal.PtrToStringUTF8((IntPtr)pErrorBlob->GetBufferPointer());
|
||||||
|
pErrorBlob->Release();
|
||||||
|
|
||||||
|
return Result.Failure($"DXC shader compilation failed:\n{errorMessage}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Result.Failure("DXC shader compilation failed with unknown error.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get compiled bytecode
|
||||||
|
IDxcBlob* pBytecodeBlob = default;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ThrowIfFailed(pResult->GetResult(&pBytecodeBlob));
|
||||||
|
|
||||||
|
ShaderReflectionData reflection = default;
|
||||||
|
if (config.options.HasFlag(CompilerOption.KeepReflections))
|
||||||
|
{
|
||||||
|
IDxcBlob* pReflection = default;
|
||||||
|
if ((pResult->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), (void**)&pReflection, null).SUCCEEDED))
|
||||||
|
{
|
||||||
|
reflection = PerformDXCReflection(pReflection).GetValueOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pReflection != null)
|
||||||
|
{
|
||||||
|
pReflection->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytecodeSize = pBytecodeBlob->GetBufferSize();
|
||||||
|
var bytecode = new UnsafeArray<byte>((int)bytecodeSize, allocator);
|
||||||
|
|
||||||
|
NativeMemory.Copy(pBytecodeBlob->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize);
|
||||||
|
|
||||||
|
return new CompileResult
|
||||||
|
{
|
||||||
|
bytecode = bytecode,
|
||||||
|
reflectionData = reflection,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (pBytecodeBlob != null)
|
||||||
|
{
|
||||||
|
pBytecodeBlob->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
for (var i = 0; i < argsArray.Count; i++)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal((nint)argPtrs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pResult != null)
|
||||||
|
{
|
||||||
|
pResult->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (pIncludeHandler != null)
|
||||||
|
{
|
||||||
|
pIncludeHandler->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<GraphicsCompiledResult> CompilePass(IPassDescriptor descriptor)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
if (descriptor is not FullPassDescriptor fullDescriptor)
|
||||||
|
{
|
||||||
|
return Result.Failure("FullPassDescriptor expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
CompileResult tsResult = default;
|
||||||
|
var tsEntry = fullDescriptor.taskShader;
|
||||||
|
if (tsEntry.IsCreated)
|
||||||
|
{
|
||||||
|
var config = new CompilerConfig
|
||||||
|
{
|
||||||
|
defines = fullDescriptor.defines.AsSpan(),
|
||||||
|
include = fullDescriptor.generatedCodePath,
|
||||||
|
shaderPath = tsEntry.shader,
|
||||||
|
entryPoint = tsEntry.entry,
|
||||||
|
stage = ShaderStage.TaskShader,
|
||||||
|
tier = CompilerTier.Tier0,
|
||||||
|
optimizeLevel = CompilerOptimizeLevel.O3,
|
||||||
|
options = CompilerOption.KeepReflections,
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = Compile(ref config, Allocator.Persistent);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return Result.Failure(result.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
tsResult = result.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompileResult msResult;
|
||||||
|
var msEntry = fullDescriptor.meshShader;
|
||||||
|
if (msEntry.IsCreated)
|
||||||
|
{
|
||||||
|
var config = new CompilerConfig
|
||||||
|
{
|
||||||
|
defines = fullDescriptor.defines.AsSpan(),
|
||||||
|
include = fullDescriptor.generatedCodePath,
|
||||||
|
shaderPath = msEntry.shader,
|
||||||
|
entryPoint = msEntry.entry,
|
||||||
|
stage = ShaderStage.MeshShader,
|
||||||
|
tier = CompilerTier.Tier0,
|
||||||
|
optimizeLevel = CompilerOptimizeLevel.O3,
|
||||||
|
options = CompilerOption.KeepReflections,
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = Compile(ref config, Allocator.Persistent);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return Result.Failure(result.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
msResult = result.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Result.Failure("Mesh shader expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
CompileResult psResult;
|
||||||
|
var psEntry = fullDescriptor.pixelShader;
|
||||||
|
if (psEntry.IsCreated)
|
||||||
|
{
|
||||||
|
var config = new CompilerConfig
|
||||||
|
{
|
||||||
|
defines = fullDescriptor.defines.AsSpan(),
|
||||||
|
include = fullDescriptor.generatedCodePath,
|
||||||
|
shaderPath = psEntry.shader,
|
||||||
|
entryPoint = psEntry.entry,
|
||||||
|
stage = ShaderStage.PixelShader,
|
||||||
|
tier = CompilerTier.Tier0,
|
||||||
|
optimizeLevel = CompilerOptimizeLevel.O3,
|
||||||
|
options = CompilerOption.KeepReflections,
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = Compile(ref config, Allocator.Persistent);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return Result.Failure(result.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
psResult = result.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Result.Failure("Pixel shader expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GraphicsCompiledResult
|
||||||
|
{
|
||||||
|
tsResult = tsResult,
|
||||||
|
msResult = msResult,
|
||||||
|
psResult = psResult,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<GraphicsCompiledResult> LoadCompiledCache(ShaderPassKey key)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
if (_compiledResults.TryGetValue(key, out var compiledResult))
|
||||||
|
{
|
||||||
|
return compiledResult;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Result.Failure("Key not found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_compiler.Dispose();
|
||||||
|
_utils.Dispose();
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,11 @@ if (model == null)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var descriptor = SDLCompiler.ResolveShader(model);
|
var descriptor = SDLCompiler.ResolveShader(model);
|
||||||
SDLCompiler.GenerateShader(descriptor, "C:/Users/Misaki/Downloads/Archive");
|
|
||||||
|
foreach (var pass in descriptor.passes)
|
||||||
|
{
|
||||||
|
SDLCompiler.GeneratePass(pass, "C:/Users/Misaki/Downloads/Archive");
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine("Shader compiled successfully:");
|
Console.WriteLine("Shader compiled successfully:");
|
||||||
|
|
||||||
|
|||||||
@@ -442,7 +442,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build default value if we have a builder (textures currently null / TODO)
|
// Build default Value if we have a builder (textures currently null / TODO)
|
||||||
if (info.Builder != null)
|
if (info.Builder != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -247,11 +247,15 @@ internal static class SDLCompiler
|
|||||||
errorMessages.AppendLine(error.ToString());
|
errorMessages.AppendLine(error.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Fail("Failed to compile shader due to errors:\n" + errorMessages.ToString());
|
return Result.Failure("Failed to compile shader due to errors:\n" + errorMessages.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
var desc = ResolveShader(model);
|
var desc = ResolveShader(model);
|
||||||
var globalPropPath = GenerateGlobalProperties(desc.globalProperties, generatedOutputDirectory);
|
var globalPropResult = GenerateGlobalProperties(desc.globalProperties, generatedOutputDirectory);
|
||||||
|
if (globalPropResult.IsFailure)
|
||||||
|
{
|
||||||
|
return Result.Failure("Failed to generate global properties: " + globalPropResult.Message);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var pass in desc.passes)
|
foreach (var pass in desc.passes)
|
||||||
{
|
{
|
||||||
@@ -261,15 +265,21 @@ internal static class SDLCompiler
|
|||||||
}
|
}
|
||||||
|
|
||||||
fullPass.includes ??= new List<string>();
|
fullPass.includes ??= new List<string>();
|
||||||
fullPass.includes.Add(globalPropPath);
|
fullPass.includes.Add(globalPropResult.Value);
|
||||||
fullPass.generatedCodePath = GeneratePass(fullPass, generatedOutputDirectory);
|
var generatedResult = GeneratePass(fullPass, generatedOutputDirectory);
|
||||||
|
if (generatedResult.IsFailure)
|
||||||
|
{
|
||||||
|
return Result.Failure("Failed to generate pass files: " + generatedResult.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPass.generatedCodePath = generatedResult.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return Result.Fail("Failed to generate shader files: " + ex.Message);
|
return Result.Failure("Failed to generate shader files: " + ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,16 +313,16 @@ internal static class SDLCompiler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GeneratePass(IPassDescriptor descriptor, string targetDirectory)
|
public static Result<string> GeneratePass(IPassDescriptor descriptor, string targetDirectory)
|
||||||
{
|
{
|
||||||
if (descriptor is not FullPassDescriptor fullPass)
|
if (descriptor is not FullPassDescriptor fullPass)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException("Only full pass descriptors are supported for compilation.");
|
return Result.Failure("Only full pass descriptors are supported for compilation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Directory.Exists(targetDirectory))
|
if (!Directory.Exists(targetDirectory))
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Target directory does not exist.", nameof(targetDirectory));
|
return Result.Failure("Target directory does not exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var outputFileName = fullPass.uniqueIdentifier.Replace(' ', '_');
|
var outputFileName = fullPass.uniqueIdentifier.Replace(' ', '_');
|
||||||
@@ -369,11 +379,11 @@ struct PerMaterialData
|
|||||||
return outputFilePath;
|
return outputFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GenerateGlobalProperties(List<PropertyDescriptor> globalProperties, string targetDirectory)
|
public static Result<string> GenerateGlobalProperties(List<PropertyDescriptor> globalProperties, string targetDirectory)
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(targetDirectory))
|
if (!Directory.Exists(targetDirectory))
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Target directory does not exist.", nameof(targetDirectory));
|
return Result.Failure("Target directory does not exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var globalFilePath = Path.Combine(targetDirectory, _GLOBAL_PROPERTY_FILE_NAME);
|
var globalFilePath = Path.Combine(targetDirectory, _GLOBAL_PROPERTY_FILE_NAME);
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ internal static partial class ShaderStructGenerator
|
|||||||
var name = $"{CamelCaseToUnderscoreRegex().Replace(enumName, "_$1")}_{names[i]}";
|
var name = $"{CamelCaseToUnderscoreRegex().Replace(enumName, "_$1")}_{names[i]}";
|
||||||
var value = values.GetValue(i);
|
var value = values.GetValue(i);
|
||||||
// sb.Append(@$"
|
// sb.Append(@$"
|
||||||
//{name} = {value},");
|
//{name} = {Value},");
|
||||||
sb.Append(@$"
|
sb.Append(@$"
|
||||||
#define {name.ToUpperInvariant()} {value}"); // Use #define for capability. Enum is only support for newer HLSL versions.
|
#define {name.ToUpperInvariant()} {value}"); // Use #define for capability. Enum is only support for newer HLSL versions.
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user