Continue working on RHI

This commit is contained in:
2025-09-12 21:44:32 +09:00
parent 1b0ef03728
commit 1dfed83e38
49 changed files with 1780 additions and 2195 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View 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
}
}

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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