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:
@@ -14,9 +14,9 @@ using static TerraFX.Aliases.DXGI_Alias;
|
||||
|
||||
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 D3D12ResourceDatabase _resourceDatabase;
|
||||
@@ -282,8 +282,15 @@ internal unsafe class D3D12CommandBuffer : D3D12RHIObject<ID3D12GraphicsCommandL
|
||||
ThrowIfNotRecording();
|
||||
IncrementCommandCount();
|
||||
|
||||
var shaderPipeline = _pipelineLibrary.GetGraphicsPSO(pipelineKey).GetValueOrThrow();
|
||||
nativeObject.Get()->SetPipelineState(shaderPipeline.value);
|
||||
var psor = _pipelineLibrary.GetGraphicsPSO(pipelineKey);
|
||||
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)
|
||||
@@ -479,7 +486,7 @@ internal unsafe class D3D12CommandBuffer : D3D12RHIObject<ID3D12GraphicsCommandL
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (IsDisposed)
|
||||
if (Disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -489,7 +496,7 @@ internal unsafe class D3D12CommandBuffer : D3D12RHIObject<ID3D12GraphicsCommandL
|
||||
throw new InvalidOperationException("Command buffer is still recording");
|
||||
}
|
||||
|
||||
MemoryLeakException.ThrowIfRefCountNonZero(_allocator.Reset());
|
||||
_allocator.Dispose();
|
||||
_commandCount = 0;
|
||||
|
||||
base.Dispose(disposing);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
@@ -9,19 +10,18 @@ namespace Ghost.Graphics.D3D12;
|
||||
/// <summary>
|
||||
/// D3D12 implementation of command queue interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, ICommandQueue
|
||||
{
|
||||
private ComPtr<ID3D12CommandQueue> _queue;
|
||||
private ComPtr<ID3D12Fence1> _fence;
|
||||
private UniquePtr<ID3D12Fence1> _fence;
|
||||
private readonly AutoResetEvent _fenceEvent;
|
||||
private ulong _fenceValue;
|
||||
private bool _disposed;
|
||||
|
||||
public CommandQueueType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
public ID3D12CommandQueue* NativeQueue => _queue.Get();
|
||||
|
||||
public ID3D12CommandQueue* NativeQueue => nativeObject.Get();
|
||||
|
||||
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->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
|
||||
|
||||
_queue.Attach(pQueue);
|
||||
nativeObject.Attach(pQueue);
|
||||
_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)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (commandBuffer.IsEmpty)
|
||||
{
|
||||
return;
|
||||
@@ -61,7 +69,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
{
|
||||
var commandList = d3d12CommandBuffer.NativeCommandList;
|
||||
var commandListPtr = (ID3D12CommandList*)commandList;
|
||||
_queue.Get()->ExecuteCommandLists(1, &commandListPtr);
|
||||
nativeObject.Get()->ExecuteCommandLists(1, &commandListPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -71,6 +79,8 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
Span<int> executableIndices = stackalloc int[commandBuffers.Length];
|
||||
executableIndices.Fill(-1);
|
||||
|
||||
@@ -107,18 +117,22 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
_queue.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
|
||||
nativeObject.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
|
||||
}
|
||||
|
||||
public ulong Signal(ulong value)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
_fenceValue = value;
|
||||
_queue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue);
|
||||
nativeObject.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue);
|
||||
return _fenceValue;
|
||||
}
|
||||
|
||||
public void WaitForValue(ulong value)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (_fence.Get()->GetCompletedValue() < value)
|
||||
{
|
||||
var handle = new HANDLE((void*)_fenceEvent.SafeWaitHandle.DangerousGetHandle());
|
||||
@@ -131,39 +145,28 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public ulong GetCompletedValue()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return _fence.Get()->GetCompletedValue();
|
||||
}
|
||||
|
||||
public void WaitIdle()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
|
||||
WaitForValue(fenceValue);
|
||||
}
|
||||
|
||||
private static D3D12_COMMAND_LIST_TYPE ConvertCommandQueueType(CommandQueueType type)
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
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 Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_fenceEvent?.Dispose();
|
||||
_fence.Dispose();
|
||||
_queue.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Ghost.Core.Utilities;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
@@ -7,9 +9,9 @@ namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12DebugLayer
|
||||
{
|
||||
private readonly ComPtr<ID3D12Debug6> _d3d12Debug;
|
||||
private readonly ComPtr<IDXGIDebug1> _dxgiDebug;
|
||||
private readonly ComPtr<IDXGIInfoQueue> _dxgiInfoQueue;
|
||||
private UniquePtr<ID3D12Debug6> _d3d12Debug;
|
||||
private UniquePtr<IDXGIDebug1> _dxgiDebug;
|
||||
private UniquePtr<IDXGIInfoQueue> _dxgiInfoQueue;
|
||||
|
||||
public D3D12DebugLayer()
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Utilities;
|
||||
using System.Collections.Immutable;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
@@ -12,6 +13,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
#endif
|
||||
|
||||
private readonly D3D12RenderDevice _device;
|
||||
private readonly DxcShaderCompiler _shaderCompiler;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||
@@ -23,6 +25,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
private bool _disposed;
|
||||
|
||||
public IRenderDevice Device => _device;
|
||||
public IShaderCompiler ShaderCompiler => _shaderCompiler;
|
||||
public IPipelineLibrary PipelineLibrary => _pipelineLibrary;
|
||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
||||
@@ -34,6 +37,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
_debugLayer = new();
|
||||
#endif
|
||||
_device = new();
|
||||
_shaderCompiler = new();
|
||||
_descriptorAllocator = new(_device);
|
||||
|
||||
_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.Graphics;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
@@ -17,25 +19,10 @@ using static TerraFX.Aliases.D3D12_Alias;
|
||||
|
||||
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
|
||||
{
|
||||
public D3DX12_MESH_SHADER_PIPELINE_STATE_DESC psoDesc;
|
||||
public ComPtr<ID3D12PipelineState> pso;
|
||||
public UniquePtr<ID3D12PipelineState> pso;
|
||||
public ShaderPassKey shaderPass;
|
||||
|
||||
public void Dispose()
|
||||
@@ -49,12 +36,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
private readonly D3D12RenderDevice _device;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
|
||||
private ComPtr<ID3D12PipelineLibrary1> _library;
|
||||
private ComPtr<ID3D12RootSignature> _defaultRootSignature;
|
||||
private UniquePtr<ID3D12PipelineLibrary1> _library;
|
||||
private UniquePtr<ID3D12RootSignature> _defaultRootSignature;
|
||||
|
||||
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, D3D12GraphicsCompiledResult> _compiledResults;
|
||||
private readonly Dictionary<ShaderPassKey, CBufferInfo> _cbufferInfoCache;
|
||||
|
||||
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
|
||||
|
||||
@@ -63,13 +49,13 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
_device = device;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_pipelineCache = new();
|
||||
_compiledResults = new();
|
||||
_pipelineCache = new Dictionary<GraphicsPipelineKey, D3D12PipelineState>();
|
||||
_cbufferInfoCache = new Dictionary<ShaderPassKey, CBufferInfo>();
|
||||
|
||||
CreateDefaultRootSignature();
|
||||
}
|
||||
|
||||
private void CreateDefaultRootSignature()
|
||||
private Result CreateDefaultRootSignature()
|
||||
{
|
||||
_defaultRootSignature = default;
|
||||
|
||||
@@ -154,21 +140,38 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
Desc_1_1 = rootSignatureDesc
|
||||
};
|
||||
|
||||
using ComPtr<ID3DBlob> signature = default;
|
||||
using ComPtr<ID3DBlob> error = default;
|
||||
ID3DBlob* pSignature = default;
|
||||
ID3DBlob* pError = default;
|
||||
|
||||
var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, signature.GetAddressOf(), error.GetAddressOf());
|
||||
if (serializeResult.FAILED)
|
||||
try
|
||||
{
|
||||
var errorMsg = error.Get() != null ? Marshal.PtrToStringUTF8((nint)error.Get()->GetBufferPointer()) : "Unknown error";
|
||||
throw new InvalidOperationException($"Failed to serialize default root signature: {errorMsg}");
|
||||
var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, &pSignature, &pError);
|
||||
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;
|
||||
ThrowIfFailed(_device.NativeDevice->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(),
|
||||
__uuidof(pRootSignature), (void**)&pRootSignature));
|
||||
|
||||
_defaultRootSignature.Attach(pRootSignature);
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public void InitializeLibrary(string? filePath)
|
||||
@@ -208,20 +211,20 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
static Result<CBufferInfo> ValidatePassReflectionData(ref readonly GraphicsCompiledResult compiled)
|
||||
{
|
||||
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
|
||||
var msr = ValidateReflectionData(compiled.msResult.reflectionData);
|
||||
if (msr.IsFailure)
|
||||
{
|
||||
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
|
||||
},
|
||||
};
|
||||
return Result.Failure("Validation of mesh shader reflection data failed: " + msr.Message);
|
||||
}
|
||||
|
||||
if (compiled.tsResult.IsCreated)
|
||||
{
|
||||
desc.AS = new D3D12_SHADER_BYTECODE(compiled.tsResult.bytecode.GetUnsafePtr(), (nuint)compiled.tsResult.bytecode.Count);
|
||||
var psr = ValidateReflectionData(compiled.psResult.reflectionData);
|
||||
if (psr.IsFailure)
|
||||
{
|
||||
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
|
||||
@@ -419,18 +313,67 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
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++)
|
||||
{
|
||||
desc.RTVFormats[i] = descriptor.RtvFormats[i].ToDXGIFormat();
|
||||
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(descriptor.ColorMask & 0x0F);
|
||||
hash.RtvFormats[i] = descriptor.RtvFormats[i];
|
||||
}
|
||||
|
||||
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 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 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);
|
||||
if (hr == E.E_INVALIDARG)
|
||||
@@ -457,77 +404,35 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
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;
|
||||
}
|
||||
|
||||
public GraphicsPipelineKey CompilePassPSO(IPassDescriptor descriptor, ReadOnlySpan<TextureFormat> rtvs, TextureFormat dsv)
|
||||
public Result<CBufferInfo, ResultStatus> GetCBufferInfo(ShaderPassKey passId)
|
||||
{
|
||||
GraphicsPipelineKey key = default;
|
||||
|
||||
var passKey = new ShaderPassKey(descriptor.Identifier);
|
||||
var hasCompiledCache = TryGetCompiledCache(passKey, out var compiled);
|
||||
|
||||
switch (descriptor)
|
||||
if (_cbufferInfoCache.TryGetValue(passId, out var cbufferInfo))
|
||||
{
|
||||
case FullPassDescriptor fullPass:
|
||||
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 Result.Create(cbufferInfo, ResultStatus.Success);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
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.");
|
||||
}
|
||||
|
||||
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.");
|
||||
return Result.Create(default(SharedPtr<ID3D12PipelineState>), ResultStatus.NotFound);
|
||||
}
|
||||
|
||||
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.Graphics.RHI;
|
||||
using System.Runtime.Versioning;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
using static TerraFX.Aliases.D3D_Alias;
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
using static TerraFX.Aliases.DXGI_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
@@ -11,33 +12,30 @@ namespace Ghost.Graphics.D3D12;
|
||||
/// <summary>
|
||||
/// D3D12 implementation of the render device interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDevice
|
||||
{
|
||||
private ComPtr<IDXGIFactory7> _dxgiFactory;
|
||||
private ComPtr<ID3D12Device14> _device;
|
||||
private ComPtr<IDXGIAdapter1> _adapter;
|
||||
private UniquePtr<IDXGIFactory7> _dxgiFactory;
|
||||
private UniquePtr<IDXGIAdapter1> _adapter;
|
||||
|
||||
private readonly D3D12CommandQueue _graphicsQueue;
|
||||
private readonly D3D12CommandQueue _computeQueue;
|
||||
private readonly D3D12CommandQueue _copyQueue;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ICommandQueue GraphicsQueue => _graphicsQueue;
|
||||
public ICommandQueue ComputeQueue => _computeQueue;
|
||||
public ICommandQueue CopyQueue => _copyQueue;
|
||||
|
||||
public ID3D12Device14* NativeDevice => _device.Get();
|
||||
public IDXGIFactory7* DXGIFactory => _dxgiFactory.Get();
|
||||
public ID3D12Device14* NativeDevice => nativeObject.Get();
|
||||
public IDXGIAdapter1* Adapter => _adapter.Get();
|
||||
|
||||
public D3D12RenderDevice()
|
||||
{
|
||||
InitializeDevice();
|
||||
|
||||
_graphicsQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Graphics);
|
||||
_computeQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Compute);
|
||||
_copyQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Copy);
|
||||
_graphicsQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Graphics);
|
||||
_computeQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Compute);
|
||||
_copyQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Copy);
|
||||
}
|
||||
|
||||
~D3D12RenderDevice()
|
||||
@@ -60,7 +58,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
IDXGIAdapter1* pAdapter = default;
|
||||
|
||||
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++)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc = default;
|
||||
@@ -84,30 +82,77 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
_graphicsQueue?.Dispose();
|
||||
_computeQueue?.Dispose();
|
||||
_copyQueue?.Dispose();
|
||||
_graphicsQueue.Dispose();
|
||||
_computeQueue.Dispose();
|
||||
_copyQueue.Dispose();
|
||||
|
||||
_device.Reset();
|
||||
_dxgiFactory.Dispose();
|
||||
_adapter.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,9 +589,9 @@ internal sealed unsafe partial class D3D12ResourceAllocator
|
||||
// TODO: Dedicated pool for copy, render graph, and persistent resources
|
||||
|
||||
// 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 D3D12RenderDevice _device;
|
||||
@@ -599,11 +599,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||
|
||||
private ComPtr<D3D12MA_Allocator> _allocator;
|
||||
private UnsafeQueue<Handle<GPUResource>> _temResources;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public D3D12ResourceAllocator(
|
||||
IFenceSynchronizer fenceSynchronizer,
|
||||
D3D12RenderDevice device,
|
||||
@@ -620,7 +617,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
D3D12MA_Allocator* pAllocator = default;
|
||||
ThrowIfFailed(D3D12MA_CreateAllocator(&desc, &pAllocator));
|
||||
_allocator.Attach(pAllocator);
|
||||
nativeObject.Attach(pAllocator);
|
||||
|
||||
_fenceSynchronizer = fenceSynchronizer;
|
||||
_device = device;
|
||||
@@ -637,7 +634,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
}
|
||||
|
||||
[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);
|
||||
|
||||
@@ -651,6 +648,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool isTemp = false)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
CheckTexture2DSize(desc.Width, desc.Height);
|
||||
|
||||
var d3d12Format = ConvertTextureFormat(desc.Format);
|
||||
@@ -706,8 +705,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
var initialState = DetermineInitialTextureState(desc.Usage);
|
||||
|
||||
ComPtr<D3D12MA_Allocation> allocation = default;
|
||||
ThrowIfFailed(_allocator.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, allocation.GetAddressOf(), Win32Utility.IID_NULL, null));
|
||||
D3D12MA_Allocation* pAllocation = default;
|
||||
ThrowIfFailed(nativeObject.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, &pAllocation, Win32Utility.IID_NULL, null));
|
||||
|
||||
var resourceDescriptor = ResourceViewGroup.Invalid;
|
||||
if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
|
||||
@@ -716,51 +715,54 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
|
||||
|
||||
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))
|
||||
{
|
||||
resourceDescriptor.rtv = _descriptorAllocator.AllocateRTV(isTemp);
|
||||
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))
|
||||
{
|
||||
resourceDescriptor.dsv = _descriptorAllocator.AllocateDSV(isTemp);
|
||||
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))
|
||||
{
|
||||
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
|
||||
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();
|
||||
}
|
||||
|
||||
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, bool isTemp = false)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var textureDesc = desc.ToTextureDescripton();
|
||||
return CreateTexture(ref textureDesc, isTemp);
|
||||
}
|
||||
|
||||
public Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, bool isTemp = false)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
CheckBufferSize(desc.Size);
|
||||
|
||||
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);
|
||||
|
||||
ComPtr<D3D12MA_Allocation> allocation = default;
|
||||
ThrowIfFailed(_allocator.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, allocation.GetAddressOf(), Win32Utility.IID_NULL, null));
|
||||
D3D12MA_Allocation* pAllocation = default;
|
||||
ThrowIfFailed(nativeObject.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, &pAllocation, Win32Utility.IID_NULL, null));
|
||||
|
||||
var resourceDescriptor = ResourceViewGroup.Invalid;
|
||||
var pResource = allocation.Get()->GetResource();
|
||||
var pResource = pAllocation->GetResource();
|
||||
|
||||
if (desc.Usage.HasFlag(BufferUsage.Constant))
|
||||
{
|
||||
@@ -803,7 +805,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
{
|
||||
resourceDescriptor.srv = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
|
||||
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);
|
||||
}
|
||||
@@ -812,17 +814,19 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
{
|
||||
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
|
||||
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);
|
||||
}
|
||||
|
||||
var handle = TrackResource(allocation, initialState, resourceDescriptor, ResourceDesc.Buffer(desc), isTemp);
|
||||
var handle = TrackResource(pAllocation, initialState, resourceDescriptor, ResourceDesc.Buffer(desc), isTemp);
|
||||
return handle.AsGraphicsBuffer();
|
||||
}
|
||||
|
||||
public Handle<GraphicsBuffer> CreateUploadBuffer(ulong size, bool isTemp = true)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var desc = new BufferDesc
|
||||
{
|
||||
Size = size,
|
||||
@@ -835,6 +839,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var vertexBufferDesc = new BufferDesc
|
||||
{
|
||||
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)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var material = new Material();
|
||||
material.SetShader(shader, this, _resourceDatabase);
|
||||
|
||||
return _resourceDatabase.AddMaterial(ref material);
|
||||
}
|
||||
|
||||
public Identifier<Shader> CreateShader(ShaderDescriptor descriptor)
|
||||
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var shader = new Shader(descriptor);
|
||||
foreach (var pass in descriptor.passes)
|
||||
{
|
||||
@@ -883,9 +893,14 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
continue;
|
||||
}
|
||||
|
||||
var passKey = new ShaderPassKey(fullPass.uniqueIdentifier);
|
||||
var cbufferInfo = _pipelineLibrary.GetCBufferInfo(passKey).GetValueOrThrow();
|
||||
_resourceDatabase.AddShaderPass(new ShaderPassKey(fullPass.uniqueIdentifier), new ShaderPass(cbufferInfo));
|
||||
var passKey = new ShaderPassKey(fullPass.Identifier);
|
||||
var cbr = _pipelineLibrary.GetCBufferInfo(passKey);
|
||||
if (cbr.Status != ResultStatus.Success)
|
||||
{
|
||||
return Identifier<Shader>.Invalid;
|
||||
}
|
||||
|
||||
_resourceDatabase.AddShaderPass(passKey, new ShaderPass(cbr.Value));
|
||||
}
|
||||
|
||||
return _resourceDatabase.AddShader(shader);
|
||||
@@ -893,6 +908,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
public void ReleaseTempResources()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
while (_temResources.Count > 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -936,9 +953,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
}
|
||||
|
||||
_temResources.Dispose();
|
||||
_allocator.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Collections;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Diagnostics;
|
||||
@@ -19,16 +20,16 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
public struct ResourceUnion
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ComPtr<D3D12MA_Allocation> allocation;
|
||||
public UniquePtr<D3D12MA_Allocation> allocation;
|
||||
[FieldOffset(0)]
|
||||
public ComPtr<ID3D12Resource> resource;
|
||||
public UniquePtr<ID3D12Resource> resource;
|
||||
|
||||
public ResourceUnion(ComPtr<D3D12MA_Allocation> allocation)
|
||||
public ResourceUnion(D3D12MA_Allocation* allocation)
|
||||
{
|
||||
this.allocation = allocation;
|
||||
}
|
||||
|
||||
public ResourceUnion(ComPtr<ID3D12Resource> resource)
|
||||
public ResourceUnion(ID3D12Resource* 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 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.isExternal = false;
|
||||
@@ -55,7 +56,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public ResourceRecord(ComPtr<ID3D12Resource> resource, ResourceState state)
|
||||
public ResourceRecord(ID3D12Resource* resource, ResourceState state)
|
||||
{
|
||||
this.resourceUnion = new ResourceUnion(resource);
|
||||
this.isExternal = true;
|
||||
@@ -63,7 +64,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
this.viewGroup = default;
|
||||
this.cpuFenceValue = ~0u;
|
||||
this.state = state;
|
||||
this.desc = ResourceDesc.FromD3D12(resource.Get()->GetDesc());
|
||||
this.desc = ResourceDesc.FromD3D12(resource->GetDesc());
|
||||
}
|
||||
|
||||
public uint Release(D3D12DescriptorAllocator descriptorAllocator)
|
||||
@@ -73,11 +74,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
{
|
||||
if (isExternal)
|
||||
{
|
||||
refCount = resourceUnion.resource.Reset();
|
||||
refCount = resourceUnion.resource.Get()->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
refCount = resourceUnion.allocation.Reset();
|
||||
refCount = resourceUnion.allocation.Get()->Release();
|
||||
}
|
||||
|
||||
resourceUnion = default;
|
||||
@@ -131,11 +132,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
@@ -148,7 +149,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
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);
|
||||
|
||||
|
||||
@@ -1,332 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.DirectX.DXC;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal partial class D3D12ShaderCompiler
|
||||
{
|
||||
private static string GetProfileString(ShaderStage stage, CompilerTier version)
|
||||
{
|
||||
return (stage, version) switch
|
||||
{
|
||||
(ShaderStage.TaskShader, CompilerTier.Tier0) => "as_6_6",
|
||||
(ShaderStage.PixelShader, CompilerTier.Tier0) => "ps_6_6",
|
||||
(ShaderStage.MeshShader, CompilerTier.Tier0) => "ms_6_6",
|
||||
(ShaderStage.ComputeShader, CompilerTier.Tier0) => "cs_6_6",
|
||||
(ShaderStage.TaskShader, CompilerTier.Tier1) => "as_6_7",
|
||||
(ShaderStage.PixelShader, CompilerTier.Tier1) => "ps_6_7",
|
||||
(ShaderStage.MeshShader, CompilerTier.Tier1) => "ms_6_7",
|
||||
(ShaderStage.ComputeShader, CompilerTier.Tier1) => "cs_6_7",
|
||||
(ShaderStage.TaskShader, CompilerTier.Tier2) => "as_6_8",
|
||||
(ShaderStage.PixelShader, CompilerTier.Tier2) => "ps_6_8",
|
||||
(ShaderStage.MeshShader, CompilerTier.Tier2) => "ms_6_8",
|
||||
(ShaderStage.ComputeShader, CompilerTier.Tier2) => "cs_6_8",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(stage), "Unsupported shader stage or compiler version")
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetOptimizeLevelString(CompilerOptimizeLevel level)
|
||||
{
|
||||
return level switch
|
||||
{
|
||||
CompilerOptimizeLevel.O0 => DXC_ARG_OPTIMIZATION_LEVEL0,
|
||||
CompilerOptimizeLevel.O1 => DXC_ARG_OPTIMIZATION_LEVEL1,
|
||||
CompilerOptimizeLevel.O2 => DXC_ARG_OPTIMIZATION_LEVEL2,
|
||||
CompilerOptimizeLevel.O3 => DXC_ARG_OPTIMIZATION_LEVEL3,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(level), "Unsupported optimization level")
|
||||
};
|
||||
}
|
||||
|
||||
private static List<string> GetCompilerArguments(ref readonly CompilerConfig config)
|
||||
{
|
||||
var argsArray = new List<string>
|
||||
{
|
||||
"-T", GetProfileString(config.stage, config.tier), // Target profile (ms_6_6, ps_6_6)
|
||||
"-E", config.entryPoint, // Entry point
|
||||
"-HV", "2021", // HLSL version 2021
|
||||
"-enable-16bit-types", // Enable 16-bit types
|
||||
GetOptimizeLevelString(config.optimizeLevel), // Optimization level
|
||||
};
|
||||
|
||||
foreach (var define in config.defines)
|
||||
{
|
||||
argsArray.Add("-D");
|
||||
argsArray.Add(define);
|
||||
}
|
||||
|
||||
// HACK: Currently DXC does not support force include, we have to use GENERATED_CODE_PATH define as a workaround.
|
||||
// User must to write '#include GENERATED_CODE_PATH' in their shader code manually.
|
||||
if (File.Exists(config.include))
|
||||
{
|
||||
argsArray.Add("-D");
|
||||
argsArray.Add($"GENERATED_CODE_PATH={'"' + config.include.Replace("\\", "/") + '"'}");
|
||||
}
|
||||
|
||||
if (!config.options.HasFlag(CompilerOption.KeepDebugInfo))
|
||||
{
|
||||
argsArray.Add("-Qstrip_debug");
|
||||
}
|
||||
|
||||
if (!config.options.HasFlag(CompilerOption.KeepReflections))
|
||||
{
|
||||
argsArray.Add("-Qstrip_reflect");
|
||||
}
|
||||
|
||||
if (config.options.HasFlag(CompilerOption.WarnAsError))
|
||||
{
|
||||
argsArray.Add(DXC_ARG_WARNINGS_ARE_ERRORS);
|
||||
}
|
||||
|
||||
return argsArray;
|
||||
}
|
||||
|
||||
private static ShaderInputType ToInputType(D3D_SHADER_INPUT_TYPE type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER => ShaderInputType.ConstantBuffer,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_TBUFFER => ShaderInputType.Texture,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_TEXTURE => ShaderInputType.Texture,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_SAMPLER => ShaderInputType.Sampler,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_UAV_RWTYPED => ShaderInputType.UAV,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_STRUCTURED => ShaderInputType.StructuredBuffer,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_BYTEADDRESS => ShaderInputType.ByteAddressBuffer,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_UAV_RWSTRUCTURED => ShaderInputType.RWStructuredBuffer,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_UAV_RWBYTEADDRESS => ShaderInputType.RWByteAddressBuffer,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), "Unsupported shader input type")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe partial class D3D12ShaderCompiler : IShaderCompiler
|
||||
{
|
||||
private ComPtr<IDxcCompiler3> _compiler;
|
||||
private ComPtr<IDxcUtils> _utils;
|
||||
|
||||
public D3D12ShaderCompiler()
|
||||
{
|
||||
// Initialize DXC _compiler.Get() and _utils.Get()
|
||||
var dxccID = CLSID.CLSID_DxcCompiler;
|
||||
var dxcuID = CLSID.CLSID_DxcUtils;
|
||||
|
||||
IDxcCompiler3* pCompiler = default;
|
||||
IDxcUtils* pUtils = default;
|
||||
ThrowIfFailed(DxcCreateInstance(&dxccID, __uuidof(pCompiler), (void**)&pCompiler));
|
||||
ThrowIfFailed(DxcCreateInstance(&dxcuID, __uuidof(pUtils), (void**)&pUtils));
|
||||
|
||||
_compiler.Attach(pCompiler);
|
||||
_utils.Attach(pUtils);
|
||||
}
|
||||
|
||||
public Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator, void** ppReflection)
|
||||
{
|
||||
// NOTE: Should we cache the _compiler.Get() and _utils.Get() instances for better performance?
|
||||
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.
|
||||
// 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;
|
||||
IDxcBlob* pDxcReflectionBlob = (IDxcBlob*)pReflectionBlob;
|
||||
|
||||
try
|
||||
{
|
||||
// Create DXC _utils.Get() to parse pReflection data
|
||||
var dxcuID = CLSID.CLSID_DxcUtils;
|
||||
|
||||
// Create pReflection interface from blob
|
||||
var reflectionBuffer = new DxcBuffer
|
||||
{
|
||||
Ptr = pDxcReflectionBlob->GetBufferPointer(),
|
||||
Size = pDxcReflectionBlob->GetBufferSize(),
|
||||
Encoding = DXC_CP_ACP
|
||||
};
|
||||
|
||||
ThrowIfFailed(_utils.Get()->CreateReflection(&reflectionBuffer, __uuidof(pReflection), (void**)&pReflection));
|
||||
|
||||
D3D12_SHADER_DESC shaderDesc;
|
||||
ThrowIfFailed(pReflection->GetDesc(&shaderDesc));
|
||||
|
||||
var reflectionData = new ShaderReflectionData();
|
||||
|
||||
for (uint i = 0; i < shaderDesc.BoundResources; i++)
|
||||
{
|
||||
D3D12_SHADER_INPUT_BIND_DESC bindDesc;
|
||||
ThrowIfFailed(pReflection->GetResourceBindingDesc(i, &bindDesc));
|
||||
|
||||
var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name);
|
||||
if (resourceName == null)
|
||||
{
|
||||
return Result.Fail("Failed to get resource name from reflection data.");
|
||||
}
|
||||
|
||||
var info = new ResourceBindingInfo
|
||||
{
|
||||
Name = resourceName,
|
||||
Type = ToInputType(bindDesc.Type),
|
||||
BindPoint = bindDesc.BindPoint,
|
||||
BindCount = bindDesc.BindCount,
|
||||
Space = bindDesc.Space
|
||||
};
|
||||
|
||||
switch (bindDesc.Type)
|
||||
{
|
||||
case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER:
|
||||
{
|
||||
var cbuffer = pReflection->GetConstantBufferByName(bindDesc.Name);
|
||||
D3D12_SHADER_BUFFER_DESC cbufferDesc;
|
||||
ThrowIfFailed(cbuffer->GetDesc(&cbufferDesc));
|
||||
|
||||
var variables = new List<CBufferPropertyInfo>((int)cbufferDesc.Variables);
|
||||
|
||||
// Now we iterate all variables for *every* cbuffer, not just b3
|
||||
for (uint j = 0; j < cbufferDesc.Variables; j++)
|
||||
{
|
||||
var variable = cbuffer->GetVariableByIndex(j);
|
||||
D3D12_SHADER_VARIABLE_DESC varDesc;
|
||||
variable->GetDesc(&varDesc);
|
||||
|
||||
var variableName = Marshal.PtrToStringUTF8((IntPtr)varDesc.Name);
|
||||
if (variableName == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
variables.Add(new CBufferPropertyInfo
|
||||
{
|
||||
Name = variableName,
|
||||
StartOffset = varDesc.StartOffset,
|
||||
Size = varDesc.Size
|
||||
});
|
||||
}
|
||||
|
||||
info.Size = cbufferDesc.Size;
|
||||
info.Properties = variables;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: Currently we do not support resource bindings yet, everything access through bindless heaps.
|
||||
}
|
||||
|
||||
reflectionData.ResourcesBindings.Add(info);
|
||||
}
|
||||
|
||||
return reflectionData;
|
||||
}
|
||||
finally
|
||||
{
|
||||
pReflection->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,11 @@ namespace Ghost.Graphics.D3D12;
|
||||
/// <summary>
|
||||
/// D3D12 implementation of swap chain interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12SwapChain : ISwapChain
|
||||
internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapChain
|
||||
{
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
|
||||
private ComPtr<IDXGISwapChain4> _swapChain;
|
||||
private UnsafeArray<Handle<Texture>> _backBuffers;
|
||||
private bool _disposed;
|
||||
|
||||
public uint Width
|
||||
{
|
||||
@@ -109,37 +107,42 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
pTempSwapChain->QueryInterface(__uuidof(pSwapChain), (void**)&pSwapChain);
|
||||
pTempSwapChain->Release();
|
||||
|
||||
_swapChain.Attach(pSwapChain);
|
||||
nativeObject.Attach(pSwapChain);
|
||||
}
|
||||
|
||||
private void CreateBackBuffers()
|
||||
{
|
||||
for (uint i = 0; i < BufferCount; i++)
|
||||
{
|
||||
ComPtr<ID3D12Resource> backBuffer = default;
|
||||
_swapChain.Get()->GetBuffer(i, backBuffer.IID(), backBuffer.PPV());
|
||||
backBuffer.Get()->SetName($"SwapChain_BackBuffer_{i}");
|
||||
ID3D12Resource* pBackBuffer = default;
|
||||
nativeObject.Get()->GetBuffer(i, __uuidof(pBackBuffer), (void**)&pBackBuffer);
|
||||
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)]
|
||||
public Handle<Texture> GetCurrentBackBuffer()
|
||||
{
|
||||
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
|
||||
ThrowIfDisposed();
|
||||
return _backBuffers[nativeObject.Get()->GetCurrentBackBufferIndex()];
|
||||
}
|
||||
|
||||
public void Present(bool vsync = true)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var presentFlags = 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)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (Width == width && Height == height)
|
||||
{
|
||||
return;
|
||||
@@ -152,7 +155,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
}
|
||||
|
||||
// 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.");
|
||||
}
|
||||
@@ -164,9 +167,9 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
CreateBackBuffers();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -176,8 +179,8 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
_resourceDatabase.ReleaseResource(_backBuffers[i].AsResource());
|
||||
}
|
||||
|
||||
_swapChain.Dispose();
|
||||
_backBuffers.Dispose();
|
||||
_disposed = true;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Ghost.Core.Utilities;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
|
||||
@@ -16,8 +16,8 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
|
||||
|
||||
private readonly D3D12RenderDevice _device;
|
||||
|
||||
private ComPtr<ID3D12DescriptorHeap> _heap;
|
||||
private ComPtr<ID3D12DescriptorHeap> _shaderVisibleHeap;
|
||||
private UniquePtr<ID3D12DescriptorHeap> _heap;
|
||||
private UniquePtr<ID3D12DescriptorHeap> _shaderVisibleHeap;
|
||||
private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandle;
|
||||
private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandleShaderVisible;
|
||||
private D3D12_GPU_DESCRIPTOR_HANDLE _startGpuHandleShaderVisible;
|
||||
@@ -323,18 +323,25 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
|
||||
var oldSize = NumDescriptors;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
_device.NativeDevice->CopyDescriptorsSimple((uint)oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
|
||||
if (_shaderVisibleHeap.Get() != null)
|
||||
finally
|
||||
{
|
||||
_device.NativeDevice->CopyDescriptorsSimple((uint)oldSize, _startCpuHandleShaderVisible, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
oldHeap->Release();
|
||||
}
|
||||
|
||||
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_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_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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user