forked from Misaki/GhostEngine
Continue working on RHI
This commit is contained in:
@@ -43,7 +43,7 @@ internal unsafe class D3D12Buffer : IBuffer
|
||||
|
||||
public ResourceState CurrentState => _currentState;
|
||||
|
||||
public ID3D12Resource* NativeResource => _handle.ResourceHandle.GetAllocation().Resource;
|
||||
public ID3D12Resource* NativeResource => _externalResource.Get() == null ? _handle.ResourceHandle.GetAllocation().Resource : _externalResource.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for wrapping existing D3D12 resources
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Numerics;
|
||||
|
||||
@@ -13,19 +14,26 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
{
|
||||
private ComPtr<ID3D12CommandAllocator> _allocator;
|
||||
private ComPtr<ID3D12GraphicsCommandList10> _commandList;
|
||||
|
||||
private readonly D3D12PipelineStateController _stateController;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
|
||||
private readonly CommandBufferType _type;
|
||||
private bool _isRecording;
|
||||
private bool _disposed;
|
||||
|
||||
public ID3D12GraphicsCommandList10* NativeCommandList => _commandList.Get();
|
||||
|
||||
public D3D12CommandBuffer(ComPtr<ID3D12Device14> device, CommandBufferType type)
|
||||
public D3D12CommandBuffer(D3D12RenderDevice device, D3D12PipelineStateController stateController, D3D12DescriptorAllocator descriptorAllocator, CommandBufferType type)
|
||||
{
|
||||
_type = type;
|
||||
var commandListType = ConvertCommandBufferType(type);
|
||||
|
||||
device.Get()->CreateCommandAllocator(commandListType, __uuidof<ID3D12CommandAllocator>(), _allocator.GetVoidAddressOf());
|
||||
device.Get()->CreateCommandList(0u, commandListType, _allocator.Get(), null, __uuidof<ID3D12GraphicsCommandList10>(), _commandList.GetVoidAddressOf());
|
||||
device.NativeDevice->CreateCommandAllocator(commandListType, __uuidof<ID3D12CommandAllocator>(), _allocator.GetVoidAddressOf());
|
||||
device.NativeDevice->CreateCommandList(0u, commandListType, _allocator.Get(), null, __uuidof<ID3D12GraphicsCommandList10>(), _commandList.GetVoidAddressOf());
|
||||
|
||||
_stateController = stateController;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
|
||||
// Command lists are created in recording state, so close it
|
||||
_commandList.Get()->Close();
|
||||
@@ -35,7 +43,9 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
public void Begin()
|
||||
{
|
||||
if (_isRecording)
|
||||
{
|
||||
throw new InvalidOperationException("Command buffer is already recording");
|
||||
}
|
||||
|
||||
_allocator.Get()->Reset();
|
||||
_commandList.Get()->Reset(_allocator.Get(), null);
|
||||
@@ -45,7 +55,9 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
public void End()
|
||||
{
|
||||
if (!_isRecording)
|
||||
{
|
||||
throw new InvalidOperationException("Command buffer is not recording");
|
||||
}
|
||||
|
||||
_commandList.Get()->Close();
|
||||
_isRecording = false;
|
||||
@@ -99,21 +111,53 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetPipelineState(IPipelineState pipelineState)
|
||||
public void SetPipelineState(IPipelineStateController pipelineState)
|
||||
{
|
||||
// TODO: Implement pipeline state setting
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetDescriptorHeaps(IDescriptorHeap[] heaps)
|
||||
public void DrawMesh(Mesh mesh, Material material)
|
||||
{
|
||||
// TODO: Implement descriptor heap setting
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
// Bind the bindless material (sets up root signature, pipeline state, and descriptor heaps)
|
||||
var shaderPipeline = _stateController.GetShaderPipeline(material.Shader);
|
||||
if (shaderPipeline is not D3D12ShaderPipeline d3d12Pipeline)
|
||||
{
|
||||
throw new InvalidOperationException("Shader pipeline is not compiled or invalid");
|
||||
}
|
||||
|
||||
public void DrawIndexedInstanced(uint indexCount, uint instanceCount = 1, uint startIndex = 0, int baseVertex = 0, uint startInstance = 0)
|
||||
{
|
||||
_commandList.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
|
||||
// Set root signature and pipeline state
|
||||
_commandList.Get()->SetGraphicsRootSignature(d3d12Pipeline.rootSignature.Get());
|
||||
_commandList.Get()->SetPipelineState(d3d12Pipeline.pipelineState.Get());
|
||||
|
||||
// Set descriptor heaps - CRUCIAL: Use the specialized bindless heap for SM 6.6
|
||||
var heaps = stackalloc ID3D12DescriptorHeap*[2];
|
||||
heaps[0] = _descriptorAllocator.GetBindlessHeap(); // Specialized bindless heap
|
||||
heaps[1] = d3d12Pipeline.samplerHeap.Get(); // Sampler heap from shader
|
||||
_commandList.Get()->SetDescriptorHeaps(2, heaps);
|
||||
|
||||
// Bind constant buffers
|
||||
var rootParamIndex = 0u;
|
||||
foreach (var cbufferInfo in material.Shader.ConstantBuffers)
|
||||
{
|
||||
var cache = material.CBufferCaches[(int)cbufferInfo.RegisterSlot];
|
||||
_commandList.Get()->SetGraphicsRootConstantBufferView(rootParamIndex++, cache.GpuResource.GPUAddress);
|
||||
}
|
||||
|
||||
// Bind sampler descriptor table (last root parameter)
|
||||
var samplerGpuHandle = d3d12Pipeline.samplerHeap.Get()->GetGPUDescriptorHandleForHeapStart();
|
||||
_commandList.Get()->SetGraphicsRootDescriptorTable(rootParamIndex, samplerGpuHandle);
|
||||
|
||||
|
||||
// For fully bindless rendering, we don't use the Input Assembler stage
|
||||
// Instead, we use instanced drawing where each "instance" represents a triangle
|
||||
// The shader will use SV_InstanceID to index into the index buffer and then into the vertex buffer
|
||||
_commandList.Get()->IASetPrimitiveTopology(PrimitiveTopology.TriangleList);
|
||||
|
||||
// Draw without vertex/index buffers - use instanced drawing
|
||||
// Each instance represents a triangle (3 vertices)
|
||||
var triangleCount = mesh.IndexCount / 3;
|
||||
_commandList.Get()->DrawInstanced(3, triangleCount, 0, 0);
|
||||
}
|
||||
|
||||
public void Dispatch(uint threadGroupCountX, uint threadGroupCountY = 1, uint threadGroupCountZ = 1)
|
||||
|
||||
@@ -15,10 +15,13 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
private ulong _fenceValue;
|
||||
private bool _disposed;
|
||||
|
||||
public CommandQueueType Type { get; }
|
||||
public CommandQueueType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
public ID3D12CommandQueue* NativeQueue => _queue.Get();
|
||||
|
||||
public D3D12CommandQueue(ComPtr<ID3D12Device14> device, CommandQueueType type)
|
||||
public D3D12CommandQueue(ID3D12Device14* pDevice, CommandQueueType type)
|
||||
{
|
||||
Type = type;
|
||||
_fenceEvent = new AutoResetEvent(false);
|
||||
@@ -33,10 +36,10 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
fixed (void* queuePtr = &_queue)
|
||||
{
|
||||
device.Get()->CreateCommandQueue(&queueDesc, __uuidof<ID3D12CommandQueue>(), (void**)queuePtr);
|
||||
pDevice->CreateCommandQueue(&queueDesc, __uuidof<ID3D12CommandQueue>(), (void**)queuePtr);
|
||||
}
|
||||
|
||||
device.Get()->CreateFence(0, FenceFlags.None, __uuidof<ID3D12Fence1>(), _fence.GetVoidAddressOf());
|
||||
pDevice->CreateFence(0, FenceFlags.None, __uuidof<ID3D12Fence1>(), _fence.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
public void Submit(ICommandBuffer commandBuffer)
|
||||
@@ -53,11 +56,11 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
}
|
||||
}
|
||||
|
||||
public void Submit(ICommandBuffer[] commandBuffers)
|
||||
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
|
||||
{
|
||||
var commandLists = stackalloc ID3D12CommandList*[commandBuffers.Length];
|
||||
|
||||
for (int i = 0; i < commandBuffers.Length; i++)
|
||||
for (var i = 0; i < commandBuffers.Length; i++)
|
||||
{
|
||||
if (commandBuffers[i] is D3D12CommandBuffer d3d12CommandBuffer)
|
||||
{
|
||||
@@ -109,7 +112,8 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_fenceEvent?.Dispose();
|
||||
_fence.Dispose();
|
||||
|
||||
@@ -4,13 +4,13 @@ using Win32.Graphics.Dxgi;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class DebugLayer
|
||||
internal unsafe class D3D12DebugLayer
|
||||
{
|
||||
private readonly ComPtr<ID3D12Debug6> _d3d12Debug;
|
||||
private readonly ComPtr<IDXGIDebug1> _dxgiDebug;
|
||||
private readonly ComPtr<IDXGIInfoQueue> _dxgiInfoQueue;
|
||||
|
||||
public DebugLayer()
|
||||
public D3D12DebugLayer()
|
||||
{
|
||||
D3D12GetDebugInterface(__uuidof<ID3D12Debug6>(), _d3d12Debug.GetVoidAddressOf());
|
||||
_d3d12Debug.Get()->EnableDebugLayer();
|
||||
@@ -25,7 +25,7 @@ internal unsafe class DebugLayer
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_dxgiDebug.Get()->ReportLiveObjects(DXGI_DEBUG_ALL, ReportLiveObjectFlags.Detail | ReportLiveObjectFlags.IgnoreInternal);
|
||||
_dxgiDebug.Get()->ReportLiveObjects(DXGI_DEBUG_ALL, ReportLiveObjectFlags.All);
|
||||
|
||||
_d3d12Debug.Dispose();
|
||||
_dxgiDebug.Dispose();
|
||||
@@ -1,88 +1,427 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using Win32;
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of descriptor allocator interface
|
||||
/// D3D12 implementation of descriptor allocator that manages different types of descriptor heaps.
|
||||
/// </summary>
|
||||
internal unsafe class D3D12DescriptorAllocator : IDescriptorAllocator
|
||||
internal unsafe class D3D12DescriptorAllocator : IDisposable
|
||||
{
|
||||
private readonly DescriptorAllocator _internalAllocator;
|
||||
private readonly DescriptorHeapAllocator _rtvHeap;
|
||||
private readonly DescriptorHeapAllocator _dsvHeap;
|
||||
private readonly DescriptorHeapAllocator _srvHeap;
|
||||
private readonly DescriptorHeapAllocator _samplerHeap;
|
||||
private readonly BindlessDescriptorHeapAllocator _bindlessHeap;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public D3D12DescriptorAllocator(ComPtr<ID3D12Device14> device)
|
||||
public unsafe D3D12DescriptorAllocator(D3D12RenderDevice device, uint initialRtvCount = 256, uint initialDsvCount = 256, uint initialSrvCount = 1024, uint initialSamplerCount = 256, uint initialBindlessCount = 10000)
|
||||
{
|
||||
_internalAllocator = new DescriptorAllocator();
|
||||
var pDevice = device.NativeDevice;
|
||||
|
||||
_rtvHeap = new DescriptorHeapAllocator("rtv", pDevice, DescriptorHeapType.Rtv, initialRtvCount);
|
||||
_dsvHeap = new DescriptorHeapAllocator("dsv", pDevice, DescriptorHeapType.Dsv, initialDsvCount);
|
||||
_srvHeap = new DescriptorHeapAllocator("srv", pDevice, DescriptorHeapType.CbvSrvUav, initialSrvCount);
|
||||
_samplerHeap = new DescriptorHeapAllocator("sampler", pDevice, DescriptorHeapType.Sampler, initialSamplerCount);
|
||||
_bindlessHeap = new BindlessDescriptorHeapAllocator(pDevice, initialBindlessCount);
|
||||
}
|
||||
|
||||
public DescriptorHandle AllocateRTV()
|
||||
~D3D12DescriptorAllocator()
|
||||
{
|
||||
var rtvDescriptor = _internalAllocator.AllocateRTV();
|
||||
return new DescriptorHandle(rtvDescriptor.Index);
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public DescriptorHandle[] AllocateRTVs(uint count)
|
||||
#region RTV Methods
|
||||
|
||||
public RenderTargetDescriptor AllocateRTV()
|
||||
{
|
||||
var rtvDescriptors = _internalAllocator.AllocateRTVs(count);
|
||||
return rtvDescriptors.Select(desc => new DescriptorHandle(desc.Index)).ToArray();
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _rtvHeap.AllocateDescriptor();
|
||||
if (index == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate RTV descriptor");
|
||||
}
|
||||
|
||||
var cpuHandle = _rtvHeap.GetCpuHandle(index);
|
||||
return new RenderTargetDescriptor(index, cpuHandle);
|
||||
}
|
||||
|
||||
public DescriptorHandle AllocateDSV()
|
||||
public RenderTargetDescriptor[] AllocateRTVs(uint count)
|
||||
{
|
||||
var dsvDescriptor = _internalAllocator.AllocateDSV();
|
||||
return new DescriptorHandle(dsvDescriptor.Index);
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _rtvHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} RTV descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new RenderTargetDescriptor[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
var cpuHandle = _rtvHeap.GetCpuHandle(index);
|
||||
descriptors[i] = new RenderTargetDescriptor(index, cpuHandle);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public DescriptorHandle AllocateSRV()
|
||||
public void ReleaseRTV(RenderTargetDescriptor descriptor)
|
||||
{
|
||||
var srvDescriptor = _internalAllocator.AllocateSRV();
|
||||
return new DescriptorHandle(srvDescriptor.Index);
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (descriptor is RenderTargetDescriptor d3d12Descriptor)
|
||||
{
|
||||
_rtvHeap.ReleaseDescriptor(d3d12Descriptor.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public DescriptorHandle AllocateSampler()
|
||||
public void ReleaseRTVs(RenderTargetDescriptor[] descriptors)
|
||||
{
|
||||
var samplerDescriptor = _internalAllocator.AllocateSampler();
|
||||
return new DescriptorHandle(samplerDescriptor.Index);
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
ReleaseRTV(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
public DescriptorHandle AllocateBindless()
|
||||
#endregion
|
||||
|
||||
#region DSV Methods
|
||||
|
||||
public DepthStencilDescriptor AllocateDSV()
|
||||
{
|
||||
var bindlessDescriptor = _internalAllocator.AllocateBindless();
|
||||
return new DescriptorHandle(bindlessDescriptor.Index);
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _dsvHeap.AllocateDescriptor();
|
||||
if (index == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate DSV descriptor");
|
||||
}
|
||||
|
||||
var cpuHandle = _dsvHeap.GetCpuHandle(index);
|
||||
return new DepthStencilDescriptor(index, cpuHandle);
|
||||
}
|
||||
|
||||
public void ReleaseRTV(DescriptorHandle handle)
|
||||
public DepthStencilDescriptor[] AllocateDSVs(uint count)
|
||||
{
|
||||
// TODO: Convert back to internal descriptor and release
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _dsvHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} DSV descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new DepthStencilDescriptor[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
var cpuHandle = _dsvHeap.GetCpuHandle(index);
|
||||
descriptors[i] = new DepthStencilDescriptor(index, cpuHandle);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public void ReleaseDSV(DescriptorHandle handle)
|
||||
public void ReleaseDSV(DepthStencilDescriptor descriptor)
|
||||
{
|
||||
// TODO: Convert back to internal descriptor and release
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (descriptor is DepthStencilDescriptor d3d12Descriptor)
|
||||
{
|
||||
_dsvHeap.ReleaseDescriptor(d3d12Descriptor.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseSRV(DescriptorHandle handle)
|
||||
public void ReleaseDSVs(DepthStencilDescriptor[] descriptors)
|
||||
{
|
||||
// TODO: Convert back to internal descriptor and release
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
ReleaseDSV(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseSampler(DescriptorHandle handle)
|
||||
#endregion
|
||||
|
||||
#region SRV Methods
|
||||
|
||||
public ShaderResourceDescriptor AllocateSRV()
|
||||
{
|
||||
// TODO: Convert back to internal descriptor and release
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _srvHeap.AllocateDescriptor();
|
||||
if (index == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate SRV descriptor");
|
||||
}
|
||||
|
||||
var cpuHandle = _srvHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _srvHeap.GetGpuHandle(index);
|
||||
|
||||
// Copy to shader visible heap
|
||||
_srvHeap.CopyToShaderVisibleHeap(index);
|
||||
|
||||
return new ShaderResourceDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
public void ReleaseBindless(DescriptorHandle handle)
|
||||
public ShaderResourceDescriptor[] AllocateSRVs(uint count)
|
||||
{
|
||||
// TODO: Convert back to internal descriptor and release
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _srvHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} SRV descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new ShaderResourceDescriptor[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
var cpuHandle = _srvHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _srvHeap.GetGpuHandle(index);
|
||||
descriptors[i] = new ShaderResourceDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
// Copy all descriptors to shader visible heap
|
||||
_srvHeap.CopyToShaderVisibleHeap(baseIndex, count);
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public void ReleaseSRV(ShaderResourceDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (descriptor is ShaderResourceDescriptor d3d12Descriptor)
|
||||
{
|
||||
_srvHeap.ReleaseDescriptor(d3d12Descriptor.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseSRVs(ShaderResourceDescriptor[] descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
ReleaseSRV(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sampler Methods
|
||||
|
||||
public SamplerDescriptor AllocateSampler()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _samplerHeap.AllocateDescriptor();
|
||||
if (index == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate Sampler descriptor");
|
||||
}
|
||||
|
||||
var cpuHandle = _samplerHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _samplerHeap.GetGpuHandle(index);
|
||||
|
||||
// Copy to shader visible heap
|
||||
_samplerHeap.CopyToShaderVisibleHeap(index);
|
||||
|
||||
return new SamplerDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
public SamplerDescriptor[] AllocateSamplers(uint count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _samplerHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} Sampler descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new SamplerDescriptor[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
var cpuHandle = _samplerHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _samplerHeap.GetGpuHandle(index);
|
||||
descriptors[i] = new SamplerDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
// Copy all descriptors to shader visible heap
|
||||
_samplerHeap.CopyToShaderVisibleHeap(baseIndex, count);
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public void ReleaseSampler(SamplerDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (descriptor is SamplerDescriptor d3d12Descriptor)
|
||||
{
|
||||
_samplerHeap.ReleaseDescriptor(d3d12Descriptor.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseSamplers(SamplerDescriptor[] descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
ReleaseSampler(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bindless Methods
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a bindless descriptor for SM 6.6 rendering.
|
||||
/// The returned descriptor maintains a 1:1 relationship between allocation index and shader index.
|
||||
/// </summary>
|
||||
public BindlessDescriptor AllocateBindless()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _bindlessHeap.AllocateDescriptor();
|
||||
if (index == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate bindless descriptor");
|
||||
}
|
||||
|
||||
var cpuHandle = _bindlessHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _bindlessHeap.GetGpuHandle(index);
|
||||
|
||||
return new BindlessDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates multiple bindless descriptors for SM 6.6 rendering.
|
||||
/// </summary>
|
||||
public BindlessDescriptor[] AllocateBindless(uint count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _bindlessHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} bindless descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new BindlessDescriptor[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
var cpuHandle = _bindlessHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _bindlessHeap.GetGpuHandle(index);
|
||||
descriptors[i] = new BindlessDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases a bindless descriptor.
|
||||
/// </summary>
|
||||
public void ReleaseBindless(BindlessDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (descriptor is BindlessDescriptor d3d12Descriptor)
|
||||
{
|
||||
_bindlessHeap.ReleaseDescriptor(d3d12Descriptor.Index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases multiple bindless descriptors.
|
||||
/// </summary>
|
||||
public void ReleaseBindless(BindlessDescriptor[] descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
ReleaseBindless(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utility Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RTV heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ID3D12DescriptorHeap* GetRTVHeap() => _rtvHeap.Heap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DSV heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ID3D12DescriptorHeap* GetDSVHeap() => _dsvHeap.Heap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SRV heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ID3D12DescriptorHeap* GetSRVHeap() => _srvHeap.ShaderVisibleHeap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampler heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ID3D12DescriptorHeap* GetSamplerHeap() => _samplerHeap.ShaderVisibleHeap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bindless heap for SM 6.6 bindless rendering.
|
||||
/// </summary>
|
||||
public ID3D12DescriptorHeap* GetBindlessHeap() => _bindlessHeap.BindlessHeap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shader visible heaps that need to be bound to the command list.
|
||||
/// </summary>
|
||||
public ID3D12DescriptorHeap*[] GetShaderVisibleHeaps()
|
||||
{
|
||||
return [_srvHeap.ShaderVisibleHeap, _samplerHeap.ShaderVisibleHeap];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shader visible heaps including bindless heap for SM 6.6 rendering.
|
||||
/// </summary>
|
||||
public ConstPtr<ID3D12DescriptorHeap>[] GetShaderVisibleHeapsWithBindless()
|
||||
{
|
||||
return [_bindlessHeap.BindlessHeap, _srvHeap.ShaderVisibleHeap, _samplerHeap.ShaderVisibleHeap];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_rtvHeap.Dispose();
|
||||
_dsvHeap.Dispose();
|
||||
_srvHeap.Dispose();
|
||||
_samplerHeap.Dispose();
|
||||
_bindlessHeap.Dispose();
|
||||
|
||||
_internalAllocator?.Dispose();
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs
Normal file
61
Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
|
||||
{
|
||||
#if DEBUG
|
||||
private readonly D3D12DebugLayer _debugLayer;
|
||||
#endif
|
||||
|
||||
private readonly D3D12RenderDevice _device;
|
||||
private readonly D3D12PipelineStateController _stateController;
|
||||
private readonly D3D12ResourceAllocator _resourceAllocator;
|
||||
|
||||
private readonly D3D12PipelineStateController _pipelineState;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
|
||||
public IRenderDevice Device => _device;
|
||||
public IPipelineStateController PipelineStateController => _stateController;
|
||||
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
||||
|
||||
public D3D12GraphicsEngine(RenderSystem renderSystem)
|
||||
{
|
||||
#if DEBUG
|
||||
_debugLayer = new();
|
||||
#endif
|
||||
|
||||
_device = new();
|
||||
_stateController = new(_device);
|
||||
_resourceAllocator = new(_device, renderSystem);
|
||||
|
||||
_pipelineState = new(_device);
|
||||
_descriptorAllocator = new(_device);
|
||||
}
|
||||
|
||||
public IRenderer CreateRenderer()
|
||||
{
|
||||
return new D3D12Renderer(this, _resourceAllocator);
|
||||
}
|
||||
|
||||
public ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics)
|
||||
{
|
||||
return new D3D12CommandBuffer(_device, _stateController, _descriptorAllocator, type);
|
||||
}
|
||||
|
||||
public ISwapChain CreateSwapChain(SwapChainDesc desc)
|
||||
{
|
||||
return new D3D12SwapChain(_device.DXGIFactory, ((D3D12CommandQueue)_device.ComputeQueue).NativeQueue, desc);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_descriptorAllocator.Dispose();
|
||||
_resourceAllocator.Dispose();
|
||||
_device.Dispose();
|
||||
|
||||
#if DEBUG
|
||||
_debugLayer.Dispose();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
253
Ghost.Graphics/D3D12/D3D12PipelineStateController.cs
Normal file
253
Ghost.Graphics/D3D12/D3D12PipelineStateController.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal class D3D12ShaderPipeline : IShaderPipeline
|
||||
{
|
||||
public ComPtr<ID3D12RootSignature> rootSignature;
|
||||
public ComPtr<ID3D12PipelineState> pipelineState;
|
||||
public ComPtr<ID3D12DescriptorHeap> samplerHeap;
|
||||
public D3D12ShaderCompiler.CompileResult vsResult;
|
||||
public D3D12ShaderCompiler.CompileResult psResult;
|
||||
public D3D12ShaderCompiler.CompileResult csResult;
|
||||
|
||||
public PipelineType Type
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe class D3D12PipelineStateController : IPipelineStateController, IDisposable
|
||||
{
|
||||
private const string _VS_ENTRY_POINT = "VSMain";
|
||||
private const string _PS_ENTRY_POINT = "PSMain";
|
||||
private const string _PROFILE_VS_6_6 = "vs_6_6";
|
||||
|
||||
private readonly ID3D12Device14* _device;
|
||||
|
||||
private readonly Dictionary<Shader, D3D12ShaderPipeline> _shaderPipelines;
|
||||
|
||||
public D3D12PipelineStateController(D3D12RenderDevice device)
|
||||
{
|
||||
_device = device.NativeDevice;
|
||||
_shaderPipelines = new();
|
||||
}
|
||||
|
||||
// TODO: Support compute shaders
|
||||
public void ColectionShader(ReadOnlySpan<Shader> shaders)
|
||||
{
|
||||
foreach (var shader in shaders)
|
||||
{
|
||||
_shaderPipelines.TryAdd(shader, new()
|
||||
{
|
||||
Type = PipelineType.Graphics
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void CompileCollected()
|
||||
{
|
||||
foreach (var kvp in _shaderPipelines)
|
||||
{
|
||||
var vsResult = D3D12ShaderCompiler.CompileDXC(kvp.Key, _VS_ENTRY_POINT, _PROFILE_VS_6_6);
|
||||
var psResult = D3D12ShaderCompiler.CompileDXC(kvp.Key, _PS_ENTRY_POINT, _PROFILE_VS_6_6);
|
||||
|
||||
kvp.Value.vsResult = vsResult;
|
||||
kvp.Value.psResult = psResult;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateRootSignature(Shader shader, D3D12ShaderPipeline shaderPipeline)
|
||||
{
|
||||
// Calculate total root parameters: CBVs + Regular texture descriptor table + Sampler table
|
||||
var totalRootParams = shader.ConstantBuffers.Count + (shader.RegularTextures.Count > 0 ? 1 : 0) + 1; // +1 for sampler
|
||||
var rootParameters = new RootParameter1[totalRootParams];
|
||||
|
||||
var parameterIndex = 0;
|
||||
|
||||
// Add CBV root parameters
|
||||
foreach (var cbufferInfo in shader.ConstantBuffers)
|
||||
{
|
||||
rootParameters[parameterIndex++] = new RootParameter1
|
||||
{
|
||||
ParameterType = RootParameterType.Cbv,
|
||||
ShaderVisibility = ShaderVisibility.All,
|
||||
Descriptor = new RootDescriptor1(cbufferInfo.RegisterSlot, 0),
|
||||
};
|
||||
}
|
||||
|
||||
// Add regular texture descriptor table if we have regular textures
|
||||
if (shader.RegularTextures.Count > 0)
|
||||
{
|
||||
var textureRanges = new DescriptorRange1[1];
|
||||
textureRanges[0] = new DescriptorRange1
|
||||
{
|
||||
RangeType = DescriptorRangeType.Srv,
|
||||
NumDescriptors = (uint)shader.RegularTextures.Count,
|
||||
BaseShaderRegister = 0, // Start from t0
|
||||
RegisterSpace = 0,
|
||||
Flags = DescriptorRangeFlags.None,
|
||||
OffsetInDescriptorsFromTableStart = 0
|
||||
};
|
||||
|
||||
fixed (DescriptorRange1* textureRangesPtr = textureRanges)
|
||||
{
|
||||
rootParameters[parameterIndex++] = new RootParameter1
|
||||
{
|
||||
ParameterType = RootParameterType.DescriptorTable,
|
||||
ShaderVisibility = ShaderVisibility.All,
|
||||
DescriptorTable = new RootDescriptorTable1(1, textureRangesPtr)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Sampler descriptor table (still needed for samplers)
|
||||
var samplerRanges = new DescriptorRange1[1];
|
||||
samplerRanges[0] = new DescriptorRange1
|
||||
{
|
||||
RangeType = DescriptorRangeType.Sampler,
|
||||
NumDescriptors = 1,
|
||||
BaseShaderRegister = 0, // s0
|
||||
RegisterSpace = 0,
|
||||
Flags = DescriptorRangeFlags.None,
|
||||
OffsetInDescriptorsFromTableStart = 0
|
||||
};
|
||||
|
||||
fixed (DescriptorRange1* samplerRangesPtr = samplerRanges)
|
||||
{
|
||||
rootParameters[parameterIndex] = new RootParameter1
|
||||
{
|
||||
ParameterType = RootParameterType.DescriptorTable,
|
||||
ShaderVisibility = ShaderVisibility.All,
|
||||
DescriptorTable = new RootDescriptorTable1(1, samplerRangesPtr)
|
||||
};
|
||||
}
|
||||
|
||||
// Create root signature with the modern flag
|
||||
fixed (RootParameter1* rootParamsPtr = rootParameters)
|
||||
{
|
||||
var rootSignatureDesc = new RootSignatureDescription1
|
||||
{
|
||||
NumParameters = (uint)rootParameters.Length,
|
||||
pParameters = rootParamsPtr,
|
||||
NumStaticSamplers = 0,
|
||||
pStaticSamplers = null,
|
||||
// Key difference: Use the modern flag for direct heap indexing
|
||||
Flags = RootSignatureFlags.AllowInputAssemblerInputLayout |
|
||||
RootSignatureFlags.CbvSrvUavHeapDirectlyIndexed
|
||||
};
|
||||
|
||||
var versionedDesc = new VersionedRootSignatureDescription
|
||||
{
|
||||
Version = RootSignatureVersion.V1_1,
|
||||
Desc_1_1 = rootSignatureDesc
|
||||
};
|
||||
|
||||
using ComPtr<ID3DBlob> signature = default;
|
||||
using ComPtr<ID3DBlob> error = default;
|
||||
|
||||
D3D12SerializeVersionedRootSignature(&versionedDesc, signature.GetAddressOf(), error.GetAddressOf());
|
||||
|
||||
_device->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof<ID3D12RootSignature>(), shaderPipeline.rootSignature.GetVoidAddressOf());
|
||||
}
|
||||
}
|
||||
|
||||
private void CreatePipelineStateObject(D3D12ShaderPipeline shaderPipeline)
|
||||
{
|
||||
var psoDesc = new GraphicsPipelineStateDescription
|
||||
{
|
||||
pRootSignature = shaderPipeline.rootSignature.Get(),
|
||||
VS = new ShaderBytecode(shaderPipeline.vsResult.bytecode.GetUnsafePtr(), (nuint)shaderPipeline.vsResult.bytecode.Count),
|
||||
PS = new ShaderBytecode(shaderPipeline.psResult.bytecode.GetUnsafePtr(), (nuint)shaderPipeline.vsResult.bytecode.Count),
|
||||
InputLayout = D3D12PipelineResource.InputLayoutDescription,
|
||||
RasterizerState = RasterizerDescription.CullNone,
|
||||
BlendState = BlendDescription.Opaque,
|
||||
DepthStencilState = DepthStencilDescription.Default,
|
||||
SampleMask = uint.MaxValue,
|
||||
PrimitiveTopologyType = PrimitiveTopologyType.Triangle,
|
||||
NumRenderTargets = 1,
|
||||
SampleDesc = new SampleDescription(1, 0),
|
||||
DSVFormat = Format.Unknown,
|
||||
};
|
||||
|
||||
psoDesc.RTVFormats[0] = D3D12PipelineResource.SWAP_CHAIN_BACK_BUFFER_FORMAT;
|
||||
|
||||
_device->CreateGraphicsPipelineState(&psoDesc, __uuidof<ID3D12PipelineState>(), shaderPipeline.pipelineState.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
private void CreateSamplerHeap(D3D12ShaderPipeline shaderPipeline)
|
||||
{
|
||||
// Create sampler heap
|
||||
var samplerHeapDesc = new DescriptorHeapDescription
|
||||
{
|
||||
Type = DescriptorHeapType.Sampler,
|
||||
NumDescriptors = 1,
|
||||
Flags = DescriptorHeapFlags.ShaderVisible
|
||||
};
|
||||
|
||||
_device->CreateDescriptorHeap(&samplerHeapDesc, __uuidof<ID3D12DescriptorHeap>(), shaderPipeline.samplerHeap.GetVoidAddressOf());
|
||||
|
||||
// Create default sampler
|
||||
var samplerDesc = new SamplerDescription
|
||||
{
|
||||
Filter = Filter.MinMagMipLinear,
|
||||
AddressU = TextureAddressMode.Wrap,
|
||||
AddressV = TextureAddressMode.Wrap,
|
||||
AddressW = TextureAddressMode.Wrap,
|
||||
MipLODBias = 0,
|
||||
MaxAnisotropy = 1,
|
||||
MinLOD = 0,
|
||||
MaxLOD = float.MaxValue
|
||||
};
|
||||
|
||||
// Set border color manually
|
||||
samplerDesc.BorderColor[0] = 0;
|
||||
samplerDesc.BorderColor[1] = 0;
|
||||
samplerDesc.BorderColor[2] = 0;
|
||||
samplerDesc.BorderColor[3] = 0;
|
||||
|
||||
var samplerHandle = shaderPipeline.samplerHeap.Get()->GetCPUDescriptorHandleForHeapStart();
|
||||
_device->CreateSampler(&samplerDesc, samplerHandle);
|
||||
}
|
||||
|
||||
// TODO: Pipeline variants (keywords)
|
||||
// TODO: Disk caching
|
||||
// TODO: Async compilation
|
||||
public void PreCookPipelineState()
|
||||
{
|
||||
foreach (var kvp in _shaderPipelines)
|
||||
{
|
||||
CreateRootSignature(kvp.Key, kvp.Value);
|
||||
CreatePipelineStateObject(kvp.Value);
|
||||
CreateSamplerHeap(kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public IShaderPipeline GetShaderPipeline(Shader shader)
|
||||
{
|
||||
if (_shaderPipelines.TryGetValue(shader, out var pipeline))
|
||||
{
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException($"Shader pipeline not found for shader: {shader}");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var kvp in _shaderPipelines)
|
||||
{
|
||||
kvp.Value.rootSignature.Dispose();
|
||||
kvp.Value.pipelineState.Dispose();
|
||||
kvp.Value.samplerHeap.Dispose();
|
||||
kvp.Value.vsResult.Dispose();
|
||||
kvp.Value.psResult.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,14 +18,12 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
private readonly D3D12CommandQueue _graphicsQueue;
|
||||
private readonly D3D12CommandQueue _computeQueue;
|
||||
private readonly D3D12CommandQueue _copyQueue;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ICommandQueue GraphicsQueue => _graphicsQueue;
|
||||
public ICommandQueue ComputeQueue => _computeQueue;
|
||||
public ICommandQueue CopyQueue => _copyQueue;
|
||||
public IDescriptorAllocator DescriptorAllocator => _descriptorAllocator;
|
||||
|
||||
public ID3D12Device14* NativeDevice => _device.Get();
|
||||
public IDXGIFactory7* DXGIFactory => _dxgiFactory.Get();
|
||||
@@ -38,8 +36,11 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
_graphicsQueue = new D3D12CommandQueue(_device, CommandQueueType.Graphics);
|
||||
_computeQueue = new D3D12CommandQueue(_device, CommandQueueType.Compute);
|
||||
_copyQueue = new D3D12CommandQueue(_device, CommandQueueType.Copy);
|
||||
}
|
||||
|
||||
_descriptorAllocator = new D3D12DescriptorAllocator(_device);
|
||||
~D3D12RenderDevice()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void InitializeDevice()
|
||||
@@ -78,22 +79,13 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
}
|
||||
}
|
||||
|
||||
public ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics)
|
||||
{
|
||||
return new D3D12CommandBuffer(_device, type);
|
||||
}
|
||||
|
||||
public ISwapChain CreateSwapChain(SwapChainDesc desc)
|
||||
{
|
||||
return new D3D12SwapChain(_dxgiFactory, _graphicsQueue.NativeQueue, desc);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_descriptorAllocator?.Dispose();
|
||||
_graphicsQueue?.Dispose();
|
||||
_computeQueue?.Dispose();
|
||||
_copyQueue?.Dispose();
|
||||
@@ -103,5 +95,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
_adapter.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
@@ -7,62 +10,51 @@ namespace Ghost.Graphics.D3D12;
|
||||
/// D3D12 implementation of render target interface
|
||||
/// Supports either color OR depth rendering, not both
|
||||
/// </summary>
|
||||
internal unsafe class D3D12RenderTarget : IRenderTarget
|
||||
internal unsafe class D3D12RenderTarget : D3D12Texture, IRenderTarget
|
||||
{
|
||||
private readonly D3D12Texture _target;
|
||||
private bool _disposed;
|
||||
|
||||
public uint Width
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public uint Height
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public RenderTargetType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ITexture Target => _target;
|
||||
private D3D12RenderTarget(ComPtr<ID3D12Resource> resource, uint width, uint height, uint slice, TextureFormat format, RenderTargetType type, uint mipLevels = 1)
|
||||
: base(resource, width, height, slice, format, mipLevels)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
private D3D12RenderTarget(TextureHandle handle, ref readonly RenderTargetDesc desc, ref readonly TextureDesc texDesc)
|
||||
: base(handle, in texDesc)
|
||||
{
|
||||
Type = desc.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new render target with its own texture
|
||||
/// </summary>
|
||||
public D3D12RenderTarget(TextureHandle handle, ref readonly RenderTargetDesc desc)
|
||||
/// <param name="handle">The handle to the texture resource</param>
|
||||
/// <param name="desc">The descriptor to describe the render target</param>
|
||||
/// <returns>New render target instance</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static D3D12RenderTarget Create(TextureHandle handle, ref readonly RenderTargetDesc desc)
|
||||
{
|
||||
Width = desc.Width;
|
||||
Height = desc.Height;
|
||||
Type = desc.Type;
|
||||
|
||||
// Create the target texture based on type
|
||||
var usage = Type == RenderTargetType.Color ? TextureUsage.RenderTarget : TextureUsage.DepthStencil;
|
||||
var textureDesc = new TextureDesc(desc.Width, desc.Height, desc.Format, desc.Dimension, desc.MipLevels, usage);
|
||||
_target = new D3D12Texture(handle, in textureDesc);
|
||||
var texDesc = RenderTargetDesc.ToTextureDescriptor(desc);
|
||||
return new D3D12RenderTarget(handle, in desc, in texDesc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrap an existing texture as a render target (for swap chain back buffers)
|
||||
/// Create a new render target from an existing D3D12 resource
|
||||
/// </summary>
|
||||
public D3D12RenderTarget(D3D12Texture existingTexture, RenderTargetType type)
|
||||
/// <param name="resource">The existing D3D12 resource</param>
|
||||
/// <param name="width">The width of the render target</param>
|
||||
/// <param name="height">The height of the render target</param>
|
||||
/// <param name="format">The format of the render target</param>
|
||||
/// <param name="type">The type of the render target</param>
|
||||
/// <param name="mipLevels">The number of mip levels</param>
|
||||
/// <returns>New render target instance</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static D3D12RenderTarget Create(ComPtr<ID3D12Resource> resource, uint width, uint height, uint slice, TextureFormat format, RenderTargetType type, uint mipLevels = 1)
|
||||
{
|
||||
_target = existingTexture;
|
||||
Width = existingTexture.Width;
|
||||
Height = existingTexture.Height;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_target?.Dispose();
|
||||
_disposed = true;
|
||||
return new D3D12RenderTarget(resource, width, height, slice, format, type, mipLevels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
@@ -11,18 +11,18 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
{
|
||||
private struct FrameResource : IDisposable
|
||||
{
|
||||
public ICommandBuffer CommandBuffer;
|
||||
public ulong FenceValue;
|
||||
public ICommandBuffer commandBuffer;
|
||||
public ulong fenceValue;
|
||||
|
||||
public FrameResource(IRenderDevice device)
|
||||
public FrameResource(IGraphicsEngine graphicsEngine)
|
||||
{
|
||||
CommandBuffer = device.CreateCommandBuffer();
|
||||
FenceValue = 0;
|
||||
commandBuffer = graphicsEngine.CreateCommandBuffer();
|
||||
fenceValue = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public readonly void Dispose()
|
||||
{
|
||||
CommandBuffer?.Dispose();
|
||||
commandBuffer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,11 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
private readonly FrameResource[] _frameResources;
|
||||
private uint _frameIndex;
|
||||
|
||||
private IRenderTarget? _destinationTarget; // Final destination (custom render target or swap chain back buffer)
|
||||
private readonly IResourceAllocator _resourceAllocator;
|
||||
|
||||
private IRenderTarget? _customRenderTarget; // User-provided render target
|
||||
private IRenderTarget? _offScreenRenderTarget; // Off-screen target for swap chain
|
||||
private ISwapChain? _swapChain;
|
||||
private IRenderTarget? _offScreenRenderTarget; // Always render to off-screen first
|
||||
|
||||
private readonly Lock _lock = new();
|
||||
private uint _pendingWidth;
|
||||
@@ -43,41 +45,39 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
// TODO: Add render passes support
|
||||
// private ImmutableArray<IRenderPass> _renderPasses;
|
||||
|
||||
public D3D12Renderer(IRenderDevice device)
|
||||
public D3D12Renderer(IGraphicsEngine graphicsEngine, IResourceAllocator resourceAllocator)
|
||||
{
|
||||
_commandQueue = device.GraphicsQueue;
|
||||
_resourceAllocator = resourceAllocator;
|
||||
_commandQueue = graphicsEngine.Device.GraphicsQueue;
|
||||
|
||||
// Create frame resources for double buffering
|
||||
_frameResources = new FrameResource[2];
|
||||
for (int i = 0; i < _frameResources.Length; i++)
|
||||
_frameResources = new FrameResource[D3D12PipelineResource.BACK_BUFFER_COUNT];
|
||||
for (var i = 0; i < _frameResources.Length; i++)
|
||||
{
|
||||
_frameResources[i] = new FrameResource(device);
|
||||
_frameResources[i] = new FrameResource(graphicsEngine);
|
||||
}
|
||||
}
|
||||
|
||||
~D3D12Renderer()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void SetRenderTarget(IRenderTarget? renderTarget)
|
||||
{
|
||||
_destinationTarget = renderTarget;
|
||||
_swapChain = null; // Clear swap chain when using custom render target
|
||||
_customRenderTarget = renderTarget;
|
||||
_swapChain = null;
|
||||
|
||||
// Create or update off-screen render target to match destination size
|
||||
if (_destinationTarget != null)
|
||||
{
|
||||
CreateOrUpdateOffScreenRenderTarget(_destinationTarget.Width, _destinationTarget.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
_offScreenRenderTarget?.Dispose();
|
||||
_offScreenRenderTarget = null;
|
||||
}
|
||||
// Clean up off-screen target when switching to render target mode
|
||||
_offScreenRenderTarget?.Dispose();
|
||||
_offScreenRenderTarget = null;
|
||||
}
|
||||
|
||||
public void SetSwapChain(ISwapChain? swapChain)
|
||||
{
|
||||
_swapChain = swapChain;
|
||||
_destinationTarget = null; // Clear custom render target when using swap chain
|
||||
_customRenderTarget = null;
|
||||
|
||||
// Create or update off-screen render target to match swap chain size
|
||||
if (_swapChain != null)
|
||||
{
|
||||
CreateOrUpdateOffScreenRenderTarget(_swapChain.Width, _swapChain.Height);
|
||||
@@ -88,6 +88,7 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
_offScreenRenderTarget = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void RequestResize(uint width, uint height)
|
||||
{
|
||||
lock (_lock)
|
||||
@@ -104,7 +105,9 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
public void ExecutePendingResize()
|
||||
{
|
||||
if (!_resizeRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint newWidth, newHeight;
|
||||
lock (_lock)
|
||||
@@ -131,59 +134,37 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
{
|
||||
ExecutePendingResize();
|
||||
|
||||
// Get current frame resource
|
||||
var frameIndex = _frameIndex % (uint)_frameResources.Length;
|
||||
ref var frame = ref _frameResources[frameIndex];
|
||||
|
||||
// Wait for this frame resource to be available
|
||||
if (frame.FenceValue > 0)
|
||||
if (frame.fenceValue > 0)
|
||||
{
|
||||
_commandQueue.WaitForValue(frame.FenceValue);
|
||||
_commandQueue.WaitForValue(frame.fenceValue);
|
||||
}
|
||||
|
||||
// Begin command recording
|
||||
frame.CommandBuffer.Begin();
|
||||
frame.commandBuffer.Begin();
|
||||
|
||||
// Determine the final destination target
|
||||
IRenderTarget? finalDestination = null;
|
||||
ITexture? swapChainBackBuffer = null;
|
||||
|
||||
if (_destinationTarget != null)
|
||||
if (_customRenderTarget != null)
|
||||
{
|
||||
// Rendering to custom render target
|
||||
finalDestination = _destinationTarget;
|
||||
// Render target mode: render directly to custom target
|
||||
RenderScene(_customRenderTarget, frame.commandBuffer);
|
||||
}
|
||||
else if (_swapChain != null)
|
||||
else if (_swapChain != null && _offScreenRenderTarget != null)
|
||||
{
|
||||
// Rendering to swap chain - get back buffer as render target
|
||||
finalDestination = _swapChain.GetCurrentBackBufferRenderTarget();
|
||||
swapChainBackBuffer = _swapChain.GetCurrentBackBuffer();
|
||||
// Swap chain mode: render to off-screen, then blit to back buffer
|
||||
var backBufferRT = _swapChain.GetCurrentBackBuffer();
|
||||
|
||||
// For testing, we render directly to the back buffer
|
||||
RenderScene(backBufferRT, frame.commandBuffer);
|
||||
//BlitToDestination(_offScreenRenderTarget, backBufferRT, frame.CommandBuffer);
|
||||
}
|
||||
|
||||
if (finalDestination != null && _offScreenRenderTarget != null)
|
||||
{
|
||||
// Always render to off-screen first, then blit to final destination
|
||||
RenderScene(_offScreenRenderTarget, frame.CommandBuffer);
|
||||
BlitToDestination(_offScreenRenderTarget, finalDestination, swapChainBackBuffer, frame.CommandBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No destination - skip rendering
|
||||
frame.CommandBuffer.End();
|
||||
return;
|
||||
}
|
||||
frame.commandBuffer.End();
|
||||
|
||||
// End command recording
|
||||
frame.CommandBuffer.End();
|
||||
|
||||
// Submit commands
|
||||
_commandQueue.Submit(frame.CommandBuffer);
|
||||
|
||||
// Present if using swap chain
|
||||
_commandQueue.Submit(frame.commandBuffer);
|
||||
_swapChain?.Present();
|
||||
|
||||
// Signal fence for this frame
|
||||
frame.FenceValue = _commandQueue.Signal(++_frameIndex);
|
||||
frame.fenceValue = _commandQueue.Signal(++_frameIndex);
|
||||
}
|
||||
|
||||
private void RenderScene(IRenderTarget target, ICommandBuffer cmd)
|
||||
@@ -207,13 +188,13 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
cmd.EndRenderPass();
|
||||
}
|
||||
|
||||
private void BlitToDestination(IRenderTarget source, IRenderTarget destination, ITexture? swapChainBackBuffer, ICommandBuffer cmd)
|
||||
private void BlitToDestination(IRenderTarget source, IRenderTarget destination, ICommandBuffer cmd)
|
||||
{
|
||||
// Handle swap chain back buffer transitions if needed
|
||||
if (swapChainBackBuffer != null)
|
||||
if (_swapChain != null)
|
||||
{
|
||||
// Transition back buffer to render target
|
||||
cmd.ResourceBarrier(swapChainBackBuffer, ResourceState.Present, ResourceState.RenderTarget);
|
||||
cmd.ResourceBarrier(destination, ResourceState.Present, ResourceState.RenderTarget);
|
||||
}
|
||||
|
||||
// For now, we'll do a simple copy operation
|
||||
@@ -231,22 +212,24 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
cmd.EndRenderPass();
|
||||
|
||||
// Handle swap chain back buffer transitions if needed
|
||||
if (swapChainBackBuffer != null)
|
||||
if (_swapChain != null)
|
||||
{
|
||||
// Transition back buffer to present
|
||||
cmd.ResourceBarrier(swapChainBackBuffer, ResourceState.RenderTarget, ResourceState.Present);
|
||||
cmd.ResourceBarrier(destination, ResourceState.RenderTarget, ResourceState.Present);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateOrUpdateOffScreenRenderTarget(uint width, uint height)
|
||||
{
|
||||
// Check if we need to recreate the off-screen render target
|
||||
if (_offScreenRenderTarget == null || _offScreenRenderTarget.Width != width || _offScreenRenderTarget.Height != height)
|
||||
if (_offScreenRenderTarget == null ||
|
||||
_offScreenRenderTarget.Width != width ||
|
||||
_offScreenRenderTarget.Height != height)
|
||||
{
|
||||
_offScreenRenderTarget?.Dispose();
|
||||
|
||||
var desc = RenderTargetDesc.Color(width, height, TextureFormat.B8G8R8A8_UNorm);
|
||||
_offScreenRenderTarget = _device.CreateRenderTarget(desc);
|
||||
var desc = RenderTargetDesc.Color(width, height, 1, TextureFormat.R8G8B8A8_UNorm);
|
||||
_offScreenRenderTarget = _resourceAllocator.CreateRenderTarget(in desc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,16 +238,19 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
// Wait for all frame resources to complete
|
||||
foreach (ref var frame in _frameResources.AsSpan())
|
||||
{
|
||||
if (frame.FenceValue > 0)
|
||||
if (frame.fenceValue > 0)
|
||||
{
|
||||
_commandQueue.WaitForValue(frame.FenceValue);
|
||||
_commandQueue.WaitForValue(frame.fenceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WaitIdle();
|
||||
|
||||
@@ -276,5 +262,7 @@ public unsafe class D3D12Renderer : IRenderer
|
||||
_offScreenRenderTarget?.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ using ResourceHandle = Ghost.Graphics.Data.ResourceHandle;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12ResourceAllocator
|
||||
internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource>, IDisposable
|
||||
{
|
||||
private readonly struct AllocationInfo : IDisposable
|
||||
{
|
||||
@@ -60,14 +60,14 @@ internal unsafe class D3D12ResourceAllocator
|
||||
}
|
||||
}
|
||||
|
||||
public D3D12ResourceAllocator(IDXGIAdapter* pAdapter, ID3D12Device* pDevice, RenderSystem renderSystem)
|
||||
public D3D12ResourceAllocator(D3D12RenderDevice device, RenderSystem renderSystem)
|
||||
{
|
||||
_renderSystem = renderSystem;
|
||||
|
||||
var desc = new AllocatorDesc
|
||||
{
|
||||
pAdapter = pAdapter,
|
||||
pDevice = pDevice,
|
||||
pAdapter = (IDXGIAdapter*)device.Adapter,
|
||||
pDevice = (ID3D12Device*)device.NativeDevice,
|
||||
Flags = AllocatorFlags.DefaultPoolsNotZeroed | AllocatorFlags.MSAATexturesAlwaysCommitted
|
||||
};
|
||||
|
||||
@@ -92,7 +92,7 @@ internal unsafe class D3D12ResourceAllocator
|
||||
}
|
||||
}
|
||||
|
||||
private ResourceHandle TrackResource(in Allocation allocation, bool isTemp)
|
||||
private ResourceHandle TrackResource(ref readonly Allocation allocation, bool isTemp)
|
||||
{
|
||||
int id;
|
||||
uint generation;
|
||||
@@ -130,7 +130,7 @@ internal unsafe class D3D12ResourceAllocator
|
||||
return handle;
|
||||
}
|
||||
|
||||
public TextureHandle CreateTexture2D(in TextureDesc desc, bool tempResource = false)
|
||||
public TextureHandle CreateTextureHandle(ref readonly TextureDesc desc, bool tempResource = false)
|
||||
{
|
||||
CheckTexture2DSize(desc.Width, desc.Height);
|
||||
|
||||
@@ -139,7 +139,7 @@ internal unsafe class D3D12ResourceAllocator
|
||||
desc.Width,
|
||||
desc.Height,
|
||||
mipLevels: (ushort)desc.MipLevels,
|
||||
arraySize: 1,
|
||||
arraySize: (ushort)desc.Slice,
|
||||
flags: ConvertTextureUsage(desc.Usage)
|
||||
);
|
||||
|
||||
@@ -157,7 +157,7 @@ internal unsafe class D3D12ResourceAllocator
|
||||
return new(TrackResource(in allocation, tempResource));
|
||||
}
|
||||
|
||||
public BufferHandle CreateBuffer(in BufferDesc desc, bool tempResource = false)
|
||||
public BufferHandle CreateBufferHandle(ref readonly BufferDesc desc, bool tempResource = false)
|
||||
{
|
||||
CheckBufferSize((uint)desc.Size);
|
||||
|
||||
@@ -176,10 +176,30 @@ internal unsafe class D3D12ResourceAllocator
|
||||
return new(TrackResource(in allocation, tempResource));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public BufferHandle CreateUploadBuffer(uint sizeInBytes, bool tempResource = false)
|
||||
{
|
||||
var desc = new BufferDesc(sizeInBytes, BufferUsage.Upload, MemoryType.Upload);
|
||||
return CreateBuffer(in desc, tempResource);
|
||||
return CreateBufferHandle(in desc, tempResource);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IRenderTarget CreateRenderTarget(ref readonly RenderTargetDesc desc, bool tempResource = false)
|
||||
{
|
||||
var textureDesc = RenderTargetDesc.ToTextureDescriptor(desc);
|
||||
return D3D12RenderTarget.Create(CreateTextureHandle(in textureDesc), in desc);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ITexture CreateTexture(ref readonly TextureDesc desc, bool tempResource = false)
|
||||
{
|
||||
return new D3D12Texture(CreateTextureHandle(in desc, tempResource), in desc);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IBuffer CreateBuffer(ref readonly BufferDesc desc, bool tempResource = false)
|
||||
{
|
||||
return new D3D12Buffer(CreateBufferHandle(in desc, tempResource), in desc);
|
||||
}
|
||||
|
||||
#region Conversion Methods
|
||||
@@ -203,13 +223,19 @@ internal unsafe class D3D12ResourceAllocator
|
||||
var flags = ResourceFlags.None;
|
||||
|
||||
if (usage.HasFlag(TextureUsage.RenderTarget))
|
||||
{
|
||||
flags |= ResourceFlags.AllowRenderTarget;
|
||||
}
|
||||
|
||||
if (usage.HasFlag(TextureUsage.DepthStencil))
|
||||
{
|
||||
flags |= ResourceFlags.AllowDepthStencil;
|
||||
}
|
||||
|
||||
if (usage.HasFlag(TextureUsage.UnorderedAccess))
|
||||
{
|
||||
flags |= ResourceFlags.AllowUnorderedAccess;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
@@ -219,7 +245,9 @@ internal unsafe class D3D12ResourceAllocator
|
||||
var flags = ResourceFlags.None;
|
||||
|
||||
if (usage.HasFlag(BufferUsage.Raw))
|
||||
{
|
||||
flags |= ResourceFlags.AllowUnorderedAccess;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
@@ -238,13 +266,19 @@ internal unsafe class D3D12ResourceAllocator
|
||||
private static ResourceStates DetermineInitialTextureState(TextureUsage usage)
|
||||
{
|
||||
if (usage.HasFlag(TextureUsage.RenderTarget))
|
||||
{
|
||||
return ResourceStates.RenderTarget;
|
||||
}
|
||||
|
||||
if (usage.HasFlag(TextureUsage.DepthStencil))
|
||||
{
|
||||
return ResourceStates.DepthWrite;
|
||||
}
|
||||
|
||||
if (usage.HasFlag(TextureUsage.UnorderedAccess))
|
||||
{
|
||||
return ResourceStates.UnorderedAccess;
|
||||
}
|
||||
|
||||
return ResourceStates.Common;
|
||||
}
|
||||
@@ -252,19 +286,29 @@ internal unsafe class D3D12ResourceAllocator
|
||||
private static ResourceStates DetermineInitialBufferState(BufferUsage usage, MemoryType memoryType)
|
||||
{
|
||||
if (memoryType == MemoryType.Upload)
|
||||
{
|
||||
return ResourceStates.GenericRead;
|
||||
}
|
||||
|
||||
if (memoryType == MemoryType.Readback)
|
||||
{
|
||||
return ResourceStates.CopyDest;
|
||||
}
|
||||
|
||||
if (usage.HasFlag(BufferUsage.Vertex))
|
||||
{
|
||||
return ResourceStates.VertexAndConstantBuffer;
|
||||
}
|
||||
|
||||
if (usage.HasFlag(BufferUsage.Index))
|
||||
{
|
||||
return ResourceStates.IndexBuffer;
|
||||
}
|
||||
|
||||
if (usage.HasFlag(BufferUsage.Constant))
|
||||
{
|
||||
return ResourceStates.VertexAndConstantBuffer;
|
||||
}
|
||||
|
||||
return ResourceStates.Common;
|
||||
}
|
||||
@@ -275,20 +319,20 @@ internal unsafe class D3D12ResourceAllocator
|
||||
{
|
||||
while (_temResources.Count > 0)
|
||||
{
|
||||
ref var handle = ref _temResources.Peek();
|
||||
ref var info = ref _allocations[handle.id];
|
||||
var handle = _temResources.Peek();
|
||||
var info = _allocations[handle.id];
|
||||
|
||||
if (info.Allocated && info.cpuFenceValue > _renderSystem.CPUFenceValue)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ReleaseAllocation(in handle);
|
||||
ReleaseResource(handle);
|
||||
_temResources.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public Allocation GetAllocation(in ResourceHandle handle)
|
||||
public ID3D12Resource* GetResource(ResourceHandle handle)
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
{
|
||||
@@ -301,10 +345,10 @@ internal unsafe class D3D12ResourceAllocator
|
||||
throw new InvalidOperationException($"Resource with ID {handle.id} and generation {handle.generation} is not allocated or has been released.");
|
||||
}
|
||||
|
||||
return allocationInfo.allocation;
|
||||
return allocationInfo.allocation.Resource;
|
||||
}
|
||||
|
||||
public void ReleaseAllocation(in ResourceHandle handle)
|
||||
public void ReleaseResource(ResourceHandle handle)
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
{
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
internal class D3D12ResourceFactory : IResourceFactory
|
||||
{
|
||||
private readonly D3D12ResourceAllocator _allocator;
|
||||
|
||||
public unsafe D3D12ResourceFactory(D3D12RenderDevice device, RenderSystem renderSystem)
|
||||
{
|
||||
_allocator = new D3D12ResourceAllocator((IDXGIAdapter*)device.Adapter, (ID3D12Device*)device.NativeDevice, renderSystem);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IRenderTarget CreateRenderTarget(ref readonly RenderTargetDesc desc)
|
||||
{
|
||||
var usage = desc.Type == RenderTargetType.Color ? TextureUsage.RenderTarget : TextureUsage.DepthStencil;
|
||||
if (desc.CreationFlags.HasFlag(RenderTargetCreationFlags.AllowUAV))
|
||||
{
|
||||
usage |= TextureUsage.UnorderedAccess;
|
||||
}
|
||||
|
||||
var textureDesc = new TextureDesc(desc.Width, desc.Height, desc.Format, desc.Dimension, 1, usage);
|
||||
return new D3D12RenderTarget(CreateTextureHandle(in textureDesc), in desc);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TextureHandle CreateTextureHandle(ref readonly TextureDesc desc)
|
||||
{
|
||||
return _allocator.CreateTexture2D(in desc);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ITexture CreateTexture(ref readonly TextureDesc desc)
|
||||
{
|
||||
return new D3D12Texture(CreateTextureHandle(in desc), in desc);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public BufferHandle CreateBufferHandle(ref readonly BufferDesc desc)
|
||||
{
|
||||
return _allocator.CreateBuffer(in desc);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IBuffer CreateBuffer(ref readonly BufferDesc desc)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
245
Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs
Normal file
245
Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
using Ghost.Graphics.Data;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D.Dxc;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe static class D3D12ShaderCompiler
|
||||
{
|
||||
public struct CompileResult : IDisposable
|
||||
{
|
||||
public UnsafeArray<byte> bytecode;
|
||||
public ComPtr<IDxcBlob> reflection;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
bytecode.Dispose();
|
||||
reflection.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static CompileResult CompileDXC(Shader shader, string entryPoint, string profile)
|
||||
{
|
||||
using ComPtr<IDxcCompiler3> compiler = default;
|
||||
using ComPtr<IDxcUtils> utils = default;
|
||||
|
||||
// Create DXC compiler and utils
|
||||
DxcCreateInstance(CLSID_DxcCompiler, __uuidof<IDxcCompiler3>(), compiler.GetVoidAddressOf());
|
||||
DxcCreateInstance(CLSID_DxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf());
|
||||
|
||||
// Create source blob
|
||||
using ComPtr<IDxcBlobEncoding> sourceBlob = default;
|
||||
var sourceBytes = System.Text.Encoding.UTF8.GetBytes(shader.Source);
|
||||
fixed (byte* sourceBytesPtr = sourceBytes)
|
||||
{
|
||||
utils.Get()->CreateBlob(sourceBytesPtr, (uint)sourceBytes.Length, DXC_CP_UTF8, sourceBlob.GetAddressOf());
|
||||
}
|
||||
|
||||
// Prepare compilation arguments - NOTE: NO -Qstrip_reflect to keep reflection data
|
||||
var argsArray = new string[]
|
||||
{
|
||||
"-T", profile, // Target profile (vs_6_6, ps_6_6)
|
||||
"-E", entryPoint, // Entry point
|
||||
"-HV", "2021", // HLSL version 2021 (required for SM 6.6)
|
||||
"-enable-16bit-types", // Enable 16-bit types
|
||||
"-O3", // Optimization level
|
||||
"-Qstrip_debug" // Strip debug info but KEEP reflection
|
||||
};
|
||||
|
||||
// Convert to wide strings (DXC expects LPCWSTR)
|
||||
var wideArgs = new nuint[argsArray.Length];
|
||||
var argPointers = new IntPtr[argsArray.Length];
|
||||
|
||||
for (var i = 0; i < argsArray.Length; i++)
|
||||
{
|
||||
argPointers[i] = Marshal.StringToHGlobalUni(argsArray[i]);
|
||||
wideArgs[i] = (nuint)argPointers[i];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Compile shader
|
||||
using ComPtr<IDxcResult> result = default;
|
||||
fixed (nuint* argsPtr = wideArgs)
|
||||
{
|
||||
var buffer = new DxcBuffer
|
||||
{
|
||||
Ptr = sourceBlob.Get()->GetBufferPointer(),
|
||||
Size = sourceBlob.Get()->GetBufferSize(),
|
||||
Encoding = DXC_CP_UTF8
|
||||
};
|
||||
|
||||
compiler.Get()->Compile(&buffer, (char**)argsPtr, (uint)argsArray.Length, null, __uuidof<IDxcResult>(), result.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
// Check compilation result
|
||||
HResult hrStatus;
|
||||
result.Get()->GetStatus(&hrStatus);
|
||||
if (hrStatus.Failure)
|
||||
{
|
||||
// Get error messages
|
||||
using ComPtr<IDxcBlobEncoding> errorBlob = default;
|
||||
result.Get()->GetErrorBuffer(errorBlob.GetAddressOf());
|
||||
|
||||
if (errorBlob.Get() != null)
|
||||
{
|
||||
var errorMessage = Marshal.PtrToStringUni((IntPtr)errorBlob.Get()->GetBufferPointer());
|
||||
throw new Exception($"DXC shader compilation failed: {errorMessage}");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("DXC shader compilation failed with unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
// Get compiled bytecode
|
||||
using ComPtr<IDxcBlob> bytecodeBlob = default;
|
||||
result.Get()->GetResult(bytecodeBlob.GetAddressOf());
|
||||
|
||||
if (bytecodeBlob.Get() == null)
|
||||
{
|
||||
throw new Exception("DXC compilation succeeded but no bytecode was produced");
|
||||
}
|
||||
|
||||
// Get reflection data using DXC API
|
||||
using ComPtr<IDxcBlob> reflectionBlob = default;
|
||||
result.Get()->GetOutput(DxcOutKind.Reflection, __uuidof<IDxcBlob>(), reflectionBlob.GetVoidAddressOf(), null);
|
||||
|
||||
if (reflectionBlob.Get() == null)
|
||||
{
|
||||
throw new Exception("DXC compilation succeeded but no reflection data was produced");
|
||||
}
|
||||
|
||||
var bytecodeSize = bytecodeBlob.Get()->GetBufferSize();
|
||||
var bytecode = new UnsafeArray<byte>((int)bytecodeSize, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
|
||||
NativeMemory.Copy(bytecodeBlob.Get()->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize);
|
||||
|
||||
return new CompileResult
|
||||
{
|
||||
bytecode = bytecode,
|
||||
reflection = reflectionBlob.Move()
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Free allocated wide strings
|
||||
for (var i = 0; i < argPointers.Length; i++)
|
||||
{
|
||||
Marshal.FreeHGlobal(argPointers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void PerformDXCReflection(Shader shader, IDxcBlob* reflectionBlob)
|
||||
{
|
||||
// Create DXC utils to parse reflection data
|
||||
using ComPtr<IDxcUtils> utils = default;
|
||||
DxcCreateInstance(CLSID_DxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf());
|
||||
|
||||
// Create reflection interface from blob
|
||||
var reflectionData = new DxcBuffer
|
||||
{
|
||||
Ptr = reflectionBlob->GetBufferPointer(),
|
||||
Size = reflectionBlob->GetBufferSize(),
|
||||
Encoding = DXC_CP_ACP
|
||||
};
|
||||
|
||||
using ComPtr<ID3D12ShaderReflection> reflection = default;
|
||||
utils.Get()->CreateReflection(&reflectionData, __uuidof<ID3D12ShaderReflection>(), reflection.GetVoidAddressOf());
|
||||
|
||||
if (reflection.Get() == null)
|
||||
{
|
||||
throw new Exception("Failed to create shader reflection from DXC output");
|
||||
}
|
||||
|
||||
ShaderDescription shaderDesc;
|
||||
reflection.Get()->GetDesc(&shaderDesc);
|
||||
|
||||
var cbufferRegistry = shader.ConstantBuffers.ToDictionary(cb => cb.Name);
|
||||
var textureRegistry = shader.RegularTextures.ToDictionary(t => t.Name);
|
||||
|
||||
for (uint i = 0; i < shaderDesc.BoundResources; i++)
|
||||
{
|
||||
ShaderInputBindDescription bindDesc;
|
||||
reflection.Get()->GetResourceBindingDesc(i, &bindDesc);
|
||||
|
||||
if (bindDesc.Type == ShaderInputType.ConstantBuffer)
|
||||
{
|
||||
var cbufferName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
|
||||
if (cbufferName == null || cbufferRegistry.ContainsKey(cbufferName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var cbuffer = reflection.Get()->GetConstantBufferByName(bindDesc.Name);
|
||||
ShaderBufferDescription cbufferDesc;
|
||||
cbuffer->GetDesc(&cbufferDesc);
|
||||
|
||||
var cbufferInfo = new CBufferInfo
|
||||
{
|
||||
Name = cbufferName,
|
||||
Size = cbufferDesc.Size,
|
||||
RegisterSlot = bindDesc.BindPoint
|
||||
};
|
||||
cbufferRegistry.Add(cbufferName, cbufferInfo);
|
||||
|
||||
for (uint j = 0; j < cbufferDesc.Variables; j++)
|
||||
{
|
||||
var variable = cbuffer->GetVariableByIndex(j);
|
||||
ShaderVariableDescription varDesc;
|
||||
variable->GetDesc(&varDesc);
|
||||
|
||||
var variableName = Marshal.PtrToStringAnsi((IntPtr)varDesc.Name);
|
||||
if (variableName == null || shader.PropertyNameToIdMap.ContainsKey(variableName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var propInfo = new PropertyInfo
|
||||
{
|
||||
Name = variableName,
|
||||
CBufferIndex = cbufferInfo.RegisterSlot,
|
||||
ByteOffset = varDesc.StartOffset,
|
||||
Size = varDesc.Size
|
||||
};
|
||||
|
||||
// Add to the list and create the name-to-ID mapping
|
||||
var newId = shader.Properties.Count;
|
||||
shader.Properties.Add(propInfo);
|
||||
shader.PropertyNameToIdMap.Add(variableName, newId);
|
||||
}
|
||||
}
|
||||
else if (bindDesc.Type == ShaderInputType.Texture)
|
||||
{
|
||||
var textureName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
|
||||
if (textureName == null || textureRegistry.ContainsKey(textureName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// ALL texture input slots are regular textures!
|
||||
// Bindless textures don't use explicit texture inputs - they use ResourceDescriptorHeap[index]
|
||||
var textureInfo = new TextureInfo
|
||||
{
|
||||
Name = textureName,
|
||||
RegisterSlot = bindDesc.BindPoint,
|
||||
RootParameterIndex = (uint)shader.ConstantBuffers.Count // Descriptor table comes after CBVs
|
||||
};
|
||||
|
||||
textureRegistry.Add(textureName, textureInfo);
|
||||
}
|
||||
}
|
||||
|
||||
shader.ConstantBuffers.Clear();
|
||||
shader.ConstantBuffers.AddRange(cbufferRegistry.Values);
|
||||
|
||||
shader.RegularTextures.Clear();
|
||||
shader.RegularTextures.AddRange(textureRegistry.Values);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi;
|
||||
@@ -13,47 +15,46 @@ namespace Ghost.Graphics.D3D12;
|
||||
internal unsafe class D3D12SwapChain : ISwapChain
|
||||
{
|
||||
private ComPtr<IDXGISwapChain4> _swapChain;
|
||||
private readonly D3D12Texture[] _backBuffers;
|
||||
private readonly D3D12RenderTarget[] _backBufferRenderTargets;
|
||||
private uint _currentBackBufferIndex;
|
||||
private readonly D3D12RenderTarget[] _backBuffers;
|
||||
private bool _disposed;
|
||||
|
||||
public uint Width
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public uint Height
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public uint BufferCount
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public D3D12SwapChain(ComPtr<IDXGIFactory7> factory, ID3D12CommandQueue* commandQueue, SwapChainDesc desc)
|
||||
public D3D12SwapChain(IDXGIFactory7* pFactory, ID3D12CommandQueue* pCommandQueue, SwapChainDesc desc)
|
||||
{
|
||||
_backBuffers = new D3D12Texture[desc.BufferCount];
|
||||
_backBufferRenderTargets = new D3D12RenderTarget[desc.BufferCount];
|
||||
_backBuffers = new D3D12RenderTarget[D3D12PipelineResource.BACK_BUFFER_COUNT];
|
||||
|
||||
Width = desc.Width;
|
||||
Height = desc.Height;
|
||||
BufferCount = desc.BufferCount;
|
||||
Width = desc.width;
|
||||
Height = desc.height;
|
||||
BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT;
|
||||
|
||||
CreateSwapChain(factory, commandQueue, desc);
|
||||
CreateSwapChain(pFactory, pCommandQueue, desc);
|
||||
CreateBackBuffers();
|
||||
}
|
||||
|
||||
private void CreateSwapChain(ComPtr<IDXGIFactory7> factory, ID3D12CommandQueue* commandQueue, SwapChainDesc desc)
|
||||
private void CreateSwapChain(IDXGIFactory7* pFactory, ID3D12CommandQueue* commandQueue, SwapChainDesc desc)
|
||||
{
|
||||
var swapChainDesc = new SwapChainDescription1
|
||||
{
|
||||
Width = desc.Width,
|
||||
Height = desc.Height,
|
||||
Format = ConvertTextureFormat(desc.Format),
|
||||
Width = desc.width,
|
||||
Height = desc.height,
|
||||
Format = ConvertTextureFormat(desc.format),
|
||||
SampleDesc = new SampleDescription(1, 0),
|
||||
BufferUsage = Usage.BackBuffer | Usage.RenderTargetOutput,
|
||||
BufferCount = desc.BufferCount,
|
||||
BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT,
|
||||
Scaling = Scaling.Stretch,
|
||||
SwapEffect = SwapEffect.FlipDiscard,
|
||||
AlphaMode = AlphaMode.Ignore,
|
||||
@@ -63,15 +64,15 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
|
||||
using ComPtr<IDXGISwapChain1> tempSwapChain = default;
|
||||
|
||||
switch (desc.Target.Type)
|
||||
switch (desc.target.Type)
|
||||
{
|
||||
case SwapChainTargetType.Composition:
|
||||
factory.Get()->CreateSwapChainForComposition((IUnknown*)commandQueue, &swapChainDesc, null, tempSwapChain.GetAddressOf());
|
||||
pFactory->CreateSwapChainForComposition((IUnknown*)commandQueue, &swapChainDesc, null, tempSwapChain.GetAddressOf());
|
||||
|
||||
// Set the composition surface
|
||||
if (desc.Target.CompositionSurface != null)
|
||||
if (desc.target.CompositionSurface != null)
|
||||
{
|
||||
var swapChainPanelNative = ISwapChainPanelNative.FromSwapChainPanel(desc.Target.CompositionSurface);
|
||||
using var swapChainPanelNative = ISwapChainPanelNative.FromSwapChainPanel(desc.target.CompositionSurface);
|
||||
swapChainPanelNative.SetSwapChain((IntPtr)tempSwapChain.Get());
|
||||
}
|
||||
break;
|
||||
@@ -82,9 +83,9 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
Windowed = true,
|
||||
};
|
||||
|
||||
factory.Get()->CreateSwapChainForHwnd(
|
||||
pFactory->CreateSwapChainForHwnd(
|
||||
(IUnknown*)commandQueue,
|
||||
desc.Target.WindowHandle,
|
||||
desc.target.WindowHandle,
|
||||
&swapChainDesc,
|
||||
&swapChainFullscreenDesc,
|
||||
null,
|
||||
@@ -99,8 +100,6 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
{
|
||||
throw new InvalidOperationException("Failed to create IDXGISwapChain4 interface.");
|
||||
}
|
||||
|
||||
_currentBackBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
}
|
||||
|
||||
private void CreateBackBuffers()
|
||||
@@ -111,23 +110,14 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
_swapChain.Get()->GetBuffer(i, __uuidof<ID3D12Resource>(), backBuffer.GetVoidAddressOf());
|
||||
backBuffer.Get()->SetName($"SwapChain_BackBuffer_{i}");
|
||||
|
||||
_backBuffers[i] = new D3D12Texture(backBuffer.Move(), Width, Height, TextureFormat.B8G8R8A8_UNorm);
|
||||
|
||||
// Create render target wrapper for the back buffer
|
||||
_backBufferRenderTargets[i] = new D3D12RenderTarget(_backBuffers[i], RenderTargetType.Color);
|
||||
_backBuffers[i] = D3D12RenderTarget.Create(backBuffer.Move(), Width, Height, 1, TextureFormat.B8G8R8A8_UNorm, RenderTargetType.Color);
|
||||
}
|
||||
}
|
||||
|
||||
public ITexture GetCurrentBackBuffer()
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IRenderTarget GetCurrentBackBuffer()
|
||||
{
|
||||
_currentBackBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
return _backBuffers[_currentBackBufferIndex];
|
||||
}
|
||||
|
||||
public IRenderTarget GetCurrentBackBufferRenderTarget()
|
||||
{
|
||||
_currentBackBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
return _backBufferRenderTargets[_currentBackBufferIndex];
|
||||
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
|
||||
}
|
||||
|
||||
public void Present(bool vsync = true)
|
||||
@@ -149,7 +139,6 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
// Release old back buffers and render targets
|
||||
for (var i = 0; i < _backBuffers.Length; i++)
|
||||
{
|
||||
_backBufferRenderTargets[i]?.Dispose();
|
||||
_backBuffers[i]?.Dispose();
|
||||
}
|
||||
|
||||
@@ -164,7 +153,6 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
|
||||
// Recreate back buffers
|
||||
CreateBackBuffers();
|
||||
_currentBackBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
}
|
||||
|
||||
private static Format ConvertTextureFormat(TextureFormat format)
|
||||
@@ -188,7 +176,6 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
|
||||
for (var i = 0; i < _backBuffers.Length; i++)
|
||||
{
|
||||
_backBufferRenderTargets[i]?.Dispose();
|
||||
_backBuffers[i]?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@ internal unsafe class D3D12Texture : ITexture
|
||||
get;
|
||||
}
|
||||
|
||||
public uint Slice
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public TextureFormat Format
|
||||
{
|
||||
get;
|
||||
@@ -49,13 +54,14 @@ internal unsafe class D3D12Texture : ITexture
|
||||
|
||||
public ID3D12Resource* NativeResource => _handle.IsValid ? _handle.ResourceHandle.GetAllocation().Resource : _externalResource.Get();
|
||||
|
||||
public D3D12Texture(ComPtr<ID3D12Resource> resource, uint width, uint height, TextureFormat format, uint mipLevels = 1)
|
||||
public D3D12Texture(ComPtr<ID3D12Resource> resource, uint width, uint height, uint slice, TextureFormat format, uint mipLevels = 1)
|
||||
{
|
||||
_handle = TextureHandle.Invalid;
|
||||
_externalResource = resource.Move();
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
Slice = slice;
|
||||
Format = format;
|
||||
MipLevels = mipLevels;
|
||||
_currentState = ResourceState.Common;
|
||||
@@ -69,6 +75,7 @@ internal unsafe class D3D12Texture : ITexture
|
||||
|
||||
Width = desc.Width;
|
||||
Height = desc.Height;
|
||||
Slice = desc.Slice;
|
||||
Format = desc.Format;
|
||||
|
||||
var mipLevels = desc.MipLevels;
|
||||
@@ -84,7 +91,7 @@ internal unsafe class D3D12Texture : ITexture
|
||||
|
||||
~D3D12Texture()
|
||||
{
|
||||
Dispose();
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private static uint GetBytesPerPixel(TextureFormat format)
|
||||
@@ -107,6 +114,12 @@ internal unsafe class D3D12Texture : ITexture
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
@@ -123,7 +136,5 @@ internal unsafe class D3D12Texture : ITexture
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,421 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of descriptor allocator that manages different types of descriptor heaps.
|
||||
/// </summary>
|
||||
internal class DescriptorAllocator : IDisposable
|
||||
{
|
||||
private readonly DescriptorHeapAllocator _rtvHeap;
|
||||
private readonly DescriptorHeapAllocator _dsvHeap;
|
||||
private readonly DescriptorHeapAllocator _srvHeap;
|
||||
private readonly DescriptorHeapAllocator _samplerHeap;
|
||||
private readonly BindlessDescriptorHeapAllocator _bindlessHeap;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public DescriptorAllocator(uint initialRtvCount = 256, uint initialDsvCount = 256, uint initialSrvCount = 1024, uint initialSamplerCount = 256, uint initialBindlessCount = 10000)
|
||||
{
|
||||
var device = GraphicsPipeline.GraphicsDevice;
|
||||
|
||||
_rtvHeap = new DescriptorHeapAllocator("rtv", device.NativeDevice, DescriptorHeapType.Rtv, initialRtvCount);
|
||||
_dsvHeap = new DescriptorHeapAllocator("dsv", device.NativeDevice, DescriptorHeapType.Dsv, initialDsvCount);
|
||||
_srvHeap = new DescriptorHeapAllocator("srv", device.NativeDevice, DescriptorHeapType.CbvSrvUav, initialSrvCount);
|
||||
_samplerHeap = new DescriptorHeapAllocator("sampler", device.NativeDevice, DescriptorHeapType.Sampler, initialSamplerCount);
|
||||
_bindlessHeap = new BindlessDescriptorHeapAllocator(device.NativeDevice, initialBindlessCount);
|
||||
}
|
||||
|
||||
#region RTV Methods
|
||||
|
||||
public RenderTargetDescriptor AllocateRTV()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _rtvHeap.AllocateDescriptor();
|
||||
if (index == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate RTV descriptor");
|
||||
}
|
||||
|
||||
var cpuHandle = _rtvHeap.GetCpuHandle(index);
|
||||
return new RenderTargetDescriptor(index, cpuHandle);
|
||||
}
|
||||
|
||||
public RenderTargetDescriptor[] AllocateRTVs(uint count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _rtvHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} RTV descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new RenderTargetDescriptor[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
var cpuHandle = _rtvHeap.GetCpuHandle(index);
|
||||
descriptors[i] = new RenderTargetDescriptor(index, cpuHandle);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public void ReleaseRTV(RenderTargetDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (descriptor is RenderTargetDescriptor d3d12Descriptor)
|
||||
{
|
||||
_rtvHeap.ReleaseDescriptor(d3d12Descriptor.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseRTVs(RenderTargetDescriptor[] descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
ReleaseRTV(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DSV Methods
|
||||
|
||||
public DepthStencilDescriptor AllocateDSV()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _dsvHeap.AllocateDescriptor();
|
||||
if (index == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate DSV descriptor");
|
||||
}
|
||||
|
||||
var cpuHandle = _dsvHeap.GetCpuHandle(index);
|
||||
return new DepthStencilDescriptor(index, cpuHandle);
|
||||
}
|
||||
|
||||
public DepthStencilDescriptor[] AllocateDSVs(uint count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _dsvHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} DSV descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new DepthStencilDescriptor[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
var cpuHandle = _dsvHeap.GetCpuHandle(index);
|
||||
descriptors[i] = new DepthStencilDescriptor(index, cpuHandle);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public void ReleaseDSV(DepthStencilDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (descriptor is DepthStencilDescriptor d3d12Descriptor)
|
||||
{
|
||||
_dsvHeap.ReleaseDescriptor(d3d12Descriptor.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseDSVs(DepthStencilDescriptor[] descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
ReleaseDSV(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SRV Methods
|
||||
|
||||
public ShaderResourceDescriptor AllocateSRV()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _srvHeap.AllocateDescriptor();
|
||||
if (index == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate SRV descriptor");
|
||||
}
|
||||
|
||||
var cpuHandle = _srvHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _srvHeap.GetGpuHandle(index);
|
||||
|
||||
// Copy to shader visible heap
|
||||
_srvHeap.CopyToShaderVisibleHeap(index);
|
||||
|
||||
return new ShaderResourceDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
public ShaderResourceDescriptor[] AllocateSRVs(uint count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _srvHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} SRV descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new ShaderResourceDescriptor[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
var cpuHandle = _srvHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _srvHeap.GetGpuHandle(index);
|
||||
descriptors[i] = new ShaderResourceDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
// Copy all descriptors to shader visible heap
|
||||
_srvHeap.CopyToShaderVisibleHeap(baseIndex, count);
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public void ReleaseSRV(ShaderResourceDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (descriptor is ShaderResourceDescriptor d3d12Descriptor)
|
||||
{
|
||||
_srvHeap.ReleaseDescriptor(d3d12Descriptor.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseSRVs(ShaderResourceDescriptor[] descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
ReleaseSRV(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sampler Methods
|
||||
|
||||
public SamplerDescriptor AllocateSampler()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _samplerHeap.AllocateDescriptor();
|
||||
if (index == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate Sampler descriptor");
|
||||
}
|
||||
|
||||
var cpuHandle = _samplerHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _samplerHeap.GetGpuHandle(index);
|
||||
|
||||
// Copy to shader visible heap
|
||||
_samplerHeap.CopyToShaderVisibleHeap(index);
|
||||
|
||||
return new SamplerDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
public SamplerDescriptor[] AllocateSamplers(uint count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _samplerHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} Sampler descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new SamplerDescriptor[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
var cpuHandle = _samplerHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _samplerHeap.GetGpuHandle(index);
|
||||
descriptors[i] = new SamplerDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
// Copy all descriptors to shader visible heap
|
||||
_samplerHeap.CopyToShaderVisibleHeap(baseIndex, count);
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public void ReleaseSampler(SamplerDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (descriptor is SamplerDescriptor d3d12Descriptor)
|
||||
{
|
||||
_samplerHeap.ReleaseDescriptor(d3d12Descriptor.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseSamplers(SamplerDescriptor[] descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
ReleaseSampler(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bindless Methods
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a bindless descriptor for SM 6.6 rendering.
|
||||
/// The returned descriptor maintains a 1:1 relationship between allocation index and shader index.
|
||||
/// </summary>
|
||||
public BindlessDescriptor AllocateBindless()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _bindlessHeap.AllocateDescriptor();
|
||||
if (index == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate bindless descriptor");
|
||||
}
|
||||
|
||||
var cpuHandle = _bindlessHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _bindlessHeap.GetGpuHandle(index);
|
||||
|
||||
return new BindlessDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates multiple bindless descriptors for SM 6.6 rendering.
|
||||
/// </summary>
|
||||
public BindlessDescriptor[] AllocateBindless(uint count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _bindlessHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == uint.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} bindless descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new BindlessDescriptor[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
var cpuHandle = _bindlessHeap.GetCpuHandle(index);
|
||||
var gpuHandle = _bindlessHeap.GetGpuHandle(index);
|
||||
descriptors[i] = new BindlessDescriptor(index, cpuHandle, gpuHandle);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases a bindless descriptor.
|
||||
/// </summary>
|
||||
public void ReleaseBindless(BindlessDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (descriptor is BindlessDescriptor d3d12Descriptor)
|
||||
{
|
||||
_bindlessHeap.ReleaseDescriptor(d3d12Descriptor.Index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases multiple bindless descriptors.
|
||||
/// </summary>
|
||||
public void ReleaseBindless(BindlessDescriptor[] descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
ReleaseBindless(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utility Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RTV heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ConstPtr<ID3D12DescriptorHeap> GetRTVHeap() => _rtvHeap.Heap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DSV heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ConstPtr<ID3D12DescriptorHeap> GetDSVHeap() => _dsvHeap.Heap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SRV heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ConstPtr<ID3D12DescriptorHeap> GetSRVHeap() => _srvHeap.ShaderVisibleHeap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampler heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ConstPtr<ID3D12DescriptorHeap> GetSamplerHeap() => _samplerHeap.ShaderVisibleHeap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bindless heap for SM 6.6 bindless rendering.
|
||||
/// </summary>
|
||||
public ConstPtr<ID3D12DescriptorHeap> GetBindlessHeap() => _bindlessHeap.BindlessHeap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shader visible heaps that need to be bound to the command list.
|
||||
/// </summary>
|
||||
public ConstPtr<ID3D12DescriptorHeap>[] GetShaderVisibleHeaps()
|
||||
{
|
||||
return [_srvHeap.ShaderVisibleHeap, _samplerHeap.ShaderVisibleHeap];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shader visible heaps including bindless heap for SM 6.6 rendering.
|
||||
/// </summary>
|
||||
public ConstPtr<ID3D12DescriptorHeap>[] GetShaderVisibleHeapsWithBindless()
|
||||
{
|
||||
return [_bindlessHeap.BindlessHeap, _srvHeap.ShaderVisibleHeap, _samplerHeap.ShaderVisibleHeap];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_rtvHeap.Dispose();
|
||||
_dsvHeap.Dispose();
|
||||
_srvHeap.Dispose();
|
||||
_samplerHeap.Dispose();
|
||||
_bindlessHeap.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -2,129 +2,131 @@ using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// Base class descriptor implementations.
|
||||
/// </summary>
|
||||
public abstract class Descriptor
|
||||
{
|
||||
protected readonly uint index;
|
||||
protected readonly bool isShaderVisible;
|
||||
|
||||
protected Descriptor(uint index, bool isShaderVisible)
|
||||
{
|
||||
this.index = index;
|
||||
this.isShaderVisible = isShaderVisible;
|
||||
}
|
||||
|
||||
public bool IsValid => index != uint.MaxValue;
|
||||
public uint Index => index;
|
||||
public bool IsShaderVisible => isShaderVisible;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CPU descriptor handle.
|
||||
/// </summary>
|
||||
public abstract CpuDescriptorHandle CpuHandle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GPU descriptor handle (only valid for shader-visible descriptors).
|
||||
/// </summary>
|
||||
public abstract GpuDescriptorHandle GpuHandle
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of render target view (RTV) descriptor.
|
||||
/// </summary>
|
||||
public sealed class RenderTargetDescriptor : Descriptor
|
||||
public readonly struct RenderTargetDescriptor
|
||||
{
|
||||
private readonly CpuDescriptorHandle _cpuHandle;
|
||||
|
||||
public RenderTargetDescriptor(uint index, CpuDescriptorHandle cpuHandle)
|
||||
: base(index, false)
|
||||
public uint Index
|
||||
{
|
||||
_cpuHandle = cpuHandle;
|
||||
get;
|
||||
}
|
||||
|
||||
public override CpuDescriptorHandle CpuHandle => _cpuHandle;
|
||||
public override GpuDescriptorHandle GpuHandle => default;
|
||||
public CpuDescriptorHandle CpuHandle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public RenderTargetDescriptor(uint index, CpuDescriptorHandle cpuHandle)
|
||||
{
|
||||
Index = index;
|
||||
CpuHandle = cpuHandle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of depth stencil view (DSV) descriptor.
|
||||
/// </summary>
|
||||
public sealed class DepthStencilDescriptor : Descriptor
|
||||
public readonly struct DepthStencilDescriptor
|
||||
{
|
||||
private readonly CpuDescriptorHandle _cpuHandle;
|
||||
|
||||
public DepthStencilDescriptor(uint index, CpuDescriptorHandle cpuHandle)
|
||||
: base(index, false) // DSVs are not shader visible
|
||||
public uint Index
|
||||
{
|
||||
_cpuHandle = cpuHandle;
|
||||
get;
|
||||
}
|
||||
|
||||
public override CpuDescriptorHandle CpuHandle => _cpuHandle;
|
||||
public override GpuDescriptorHandle GpuHandle => default;
|
||||
public CpuDescriptorHandle CpuHandle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public DepthStencilDescriptor(uint index, CpuDescriptorHandle cpuHandle)
|
||||
{
|
||||
Index = index;
|
||||
CpuHandle = cpuHandle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of shader resource view (SRV) descriptor.
|
||||
/// </summary>
|
||||
public sealed class ShaderResourceDescriptor : Descriptor
|
||||
public sealed class ShaderResourceDescriptor
|
||||
{
|
||||
private readonly CpuDescriptorHandle _cpuHandle;
|
||||
private readonly GpuDescriptorHandle _gpuHandle;
|
||||
|
||||
public ShaderResourceDescriptor(uint index, CpuDescriptorHandle cpuHandle, GpuDescriptorHandle gpuHandle)
|
||||
: base(index, true)
|
||||
public uint Index
|
||||
{
|
||||
_cpuHandle = cpuHandle;
|
||||
_gpuHandle = gpuHandle;
|
||||
get;
|
||||
}
|
||||
|
||||
public override CpuDescriptorHandle CpuHandle => _cpuHandle;
|
||||
public override GpuDescriptorHandle GpuHandle => _gpuHandle;
|
||||
public CpuDescriptorHandle CpuHandle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public GpuDescriptorHandle GpuHandle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ShaderResourceDescriptor(uint index, CpuDescriptorHandle cpuHandle, GpuDescriptorHandle gpuHandle)
|
||||
{
|
||||
Index = index;
|
||||
CpuHandle = cpuHandle;
|
||||
GpuHandle = gpuHandle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of sampler descriptor.
|
||||
/// </summary>
|
||||
public sealed class SamplerDescriptor : Descriptor
|
||||
public sealed class SamplerDescriptor
|
||||
{
|
||||
private readonly CpuDescriptorHandle _cpuHandle;
|
||||
private readonly GpuDescriptorHandle _gpuHandle;
|
||||
|
||||
public SamplerDescriptor(uint index, CpuDescriptorHandle cpuHandle, GpuDescriptorHandle gpuHandle)
|
||||
: base(index, true)
|
||||
public uint Index
|
||||
{
|
||||
_cpuHandle = cpuHandle;
|
||||
_gpuHandle = gpuHandle;
|
||||
get;
|
||||
}
|
||||
|
||||
public override CpuDescriptorHandle CpuHandle => _cpuHandle;
|
||||
public override GpuDescriptorHandle GpuHandle => _gpuHandle;
|
||||
public CpuDescriptorHandle CpuHandle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public GpuDescriptorHandle GpuHandle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public SamplerDescriptor(uint index, CpuDescriptorHandle cpuHandle, GpuDescriptorHandle gpuHandle)
|
||||
{
|
||||
Index = index;
|
||||
CpuHandle = cpuHandle;
|
||||
GpuHandle = gpuHandle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of bindless descriptor for SM 6.6 rendering.
|
||||
/// This descriptor maintains a 1:1 relationship between allocation indices and shader indices.
|
||||
/// </summary>
|
||||
public sealed class BindlessDescriptor : Descriptor
|
||||
public sealed class BindlessDescriptor
|
||||
{
|
||||
private readonly CpuDescriptorHandle _cpuHandle;
|
||||
private readonly GpuDescriptorHandle _gpuHandle;
|
||||
|
||||
public BindlessDescriptor(uint index, CpuDescriptorHandle cpuHandle, GpuDescriptorHandle gpuHandle)
|
||||
: base(index, true)
|
||||
public uint Index
|
||||
{
|
||||
_cpuHandle = cpuHandle;
|
||||
_gpuHandle = gpuHandle;
|
||||
get;
|
||||
}
|
||||
|
||||
public override CpuDescriptorHandle CpuHandle => _cpuHandle;
|
||||
public override GpuDescriptorHandle GpuHandle => _gpuHandle;
|
||||
public CpuDescriptorHandle CpuHandle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public GpuDescriptorHandle GpuHandle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public BindlessDescriptor(uint index, CpuDescriptorHandle cpuHandle, GpuDescriptorHandle gpuHandle)
|
||||
{
|
||||
Index = index;
|
||||
CpuHandle = cpuHandle;
|
||||
GpuHandle = gpuHandle;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
|
||||
{
|
||||
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
|
||||
|
||||
private readonly ConstPtr<ID3D12Device14> _device;
|
||||
private readonly ComPtr<ID3D12Device14> _device;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private ComPtr<ID3D12DescriptorHeap> _bindlessHeap;
|
||||
@@ -42,12 +42,14 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
|
||||
|
||||
public readonly ConstPtr<ID3D12DescriptorHeap> BindlessHeap => new(_bindlessHeap.Get());
|
||||
|
||||
public BindlessDescriptorHeapAllocator(ConstPtr<ID3D12Device14> device, uint numDescriptors = 10000)
|
||||
public BindlessDescriptorHeapAllocator(ComPtr<ID3D12Device14> device, uint numDescriptors = 10000)
|
||||
{
|
||||
_device = device;
|
||||
device.Get()->AddRef();
|
||||
|
||||
HeapType = DescriptorHeapType.CbvSrvUav;
|
||||
NumDescriptors = numDescriptors;
|
||||
_stride = device.Ptr->GetDescriptorHandleIncrementSize(DescriptorHeapType.CbvSrvUav);
|
||||
_stride = device.Get()->GetDescriptorHandleIncrementSize(DescriptorHeapType.CbvSrvUav);
|
||||
_freeDescriptors = new Queue<uint>();
|
||||
|
||||
var success = AllocateResources(numDescriptors);
|
||||
@@ -169,7 +171,7 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
|
||||
|
||||
fixed (void* heapPtr = &_bindlessHeap)
|
||||
{
|
||||
var hr = _device.Ptr->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||
var hr = _device.Get()->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||
if (hr.Failure)
|
||||
{
|
||||
return false;
|
||||
@@ -204,7 +206,7 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
|
||||
// Copy old descriptors to new heap
|
||||
if (oldHeap.Get() is not null)
|
||||
{
|
||||
_device.Ptr->CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
_device.Get()->CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -7,6 +7,8 @@ namespace Ghost.Graphics.D3D12.Utilities;
|
||||
|
||||
internal unsafe static class D3D12PipelineResource
|
||||
{
|
||||
public const int BACK_BUFFER_COUNT = 2;
|
||||
|
||||
private readonly static InputElementDescription[] s_inputElementDescs = [
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.PositionName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.NormalName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
@@ -11,8 +10,7 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
|
||||
{
|
||||
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
|
||||
|
||||
private readonly ConstPtr<ID3D12Device14> _device;
|
||||
private readonly Lock _lock = new();
|
||||
private ComPtr<ID3D12Device14> _device;
|
||||
|
||||
private ComPtr<ID3D12DescriptorHeap> _heap;
|
||||
private ComPtr<ID3D12DescriptorHeap> _shaderVisibleHeap;
|
||||
@@ -22,6 +20,8 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
|
||||
private DescriptorIndex _searchStart;
|
||||
private bool[] _allocatedDescriptors = [];
|
||||
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public DescriptorHeapType HeapType
|
||||
{
|
||||
get;
|
||||
@@ -47,16 +47,18 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly ConstPtr<ID3D12DescriptorHeap> Heap => new(_heap.Get());
|
||||
public readonly ConstPtr<ID3D12DescriptorHeap> ShaderVisibleHeap => new(_shaderVisibleHeap.Get());
|
||||
public readonly ID3D12DescriptorHeap* Heap => _heap.Get();
|
||||
public readonly ID3D12DescriptorHeap* ShaderVisibleHeap => _shaderVisibleHeap.Get();
|
||||
|
||||
public DescriptorHeapAllocator(string name, ConstPtr<ID3D12Device14> device, DescriptorHeapType type, uint numDescriptors)
|
||||
public DescriptorHeapAllocator(string name, ComPtr<ID3D12Device14> device, DescriptorHeapType type, uint numDescriptors)
|
||||
{
|
||||
_device = device;
|
||||
device.Get()->AddRef();
|
||||
|
||||
HeapType = type;
|
||||
NumDescriptors = numDescriptors;
|
||||
ShaderVisible = type == DescriptorHeapType.CbvSrvUav || type == DescriptorHeapType.Sampler;
|
||||
Stride = device.Ptr->GetDescriptorHandleIncrementSize(type);
|
||||
Stride = device.Get()->GetDescriptorHandleIncrementSize(type);
|
||||
|
||||
var success = AllocateResources(numDescriptors);
|
||||
Debug.Assert(success);
|
||||
@@ -172,7 +174,7 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
|
||||
|
||||
public void CopyToShaderVisibleHeap(DescriptorIndex index, uint count = 1)
|
||||
{
|
||||
_device.Ptr->CopyDescriptorsSimple(count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
|
||||
_device.Get()->CopyDescriptorsSimple(count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
|
||||
}
|
||||
|
||||
private bool AllocateResources(uint numDescriptors)
|
||||
@@ -191,7 +193,7 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
|
||||
|
||||
fixed (void* heapPtr = &_heap)
|
||||
{
|
||||
var hr = _device.Ptr->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||
var hr = _device.Get()->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||
if (hr.Failure)
|
||||
{
|
||||
return false;
|
||||
@@ -207,7 +209,7 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
|
||||
|
||||
fixed (void* heapPtr = &_shaderVisibleHeap)
|
||||
{
|
||||
var hr = _device.Ptr->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||
var hr = _device.Get()->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||
if (hr.Failure)
|
||||
{
|
||||
return false;
|
||||
@@ -233,11 +235,11 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
|
||||
return false;
|
||||
}
|
||||
|
||||
_device.Ptr->CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
_device.Get()->CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
|
||||
if (_shaderVisibleHeap.Get() is not null)
|
||||
{
|
||||
_device.Ptr->CopyDescriptorsSimple(oldSize, _startCpuHandleShaderVisible, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
_device.Get()->CopyDescriptorsSimple(oldSize, _startCpuHandleShaderVisible, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -246,6 +248,8 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_device.Dispose();
|
||||
|
||||
_heap.Dispose();
|
||||
_shaderVisibleHeap.Dispose();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user