Added new RHI abstraction layer;
Added new console debug page to UnitTest;
This commit is contained in:
134
Ghost.Graphics/D3D12/D3D12Buffer.cs
Normal file
134
Ghost.Graphics/D3D12/D3D12Buffer.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of buffer interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12Buffer : IBuffer
|
||||
{
|
||||
private ComPtr<ID3D12Resource> _resource;
|
||||
private ResourceState _currentState;
|
||||
private void* _mappedPtr;
|
||||
private bool _disposed;
|
||||
|
||||
public BufferUsage Usage
|
||||
{
|
||||
get;
|
||||
}
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public ulong Size
|
||||
{
|
||||
get;
|
||||
}
|
||||
public ResourceState CurrentState => _currentState;
|
||||
|
||||
public ID3D12Resource* NativeResource => _resource.Get();
|
||||
|
||||
public D3D12Buffer(ComPtr<ID3D12Device14> device, BufferDesc desc)
|
||||
{
|
||||
Usage = desc.Usage;
|
||||
Size = desc.Size;
|
||||
_currentState = ResourceState.Common;
|
||||
|
||||
CreateBuffer(device, desc);
|
||||
}
|
||||
|
||||
private void CreateBuffer(ComPtr<ID3D12Device14> device, BufferDesc desc)
|
||||
{
|
||||
var resourceDesc = new ResourceDescription
|
||||
{
|
||||
Dimension = ResourceDimension.Buffer,
|
||||
Alignment = 0,
|
||||
Width = desc.Size,
|
||||
Height = 1,
|
||||
DepthOrArraySize = 1,
|
||||
MipLevels = 1,
|
||||
Format = Win32.Graphics.Dxgi.Common.Format.Unknown,
|
||||
SampleDesc = new Win32.Graphics.Dxgi.Common.SampleDescription(1, 0),
|
||||
Layout = TextureLayout.RowMajor,
|
||||
Flags = ConvertBufferUsage(desc.Usage)
|
||||
};
|
||||
|
||||
var heapProps = new HeapProperties
|
||||
{
|
||||
Type = ConvertMemoryType(desc.MemoryType),
|
||||
CPUPageProperty = CpuPageProperty.Unknown,
|
||||
MemoryPoolPreference = MemoryPool.Unknown,
|
||||
CreationNodeMask = 1,
|
||||
VisibleNodeMask = 1
|
||||
};
|
||||
|
||||
var initialState = desc.MemoryType switch
|
||||
{
|
||||
MemoryType.Upload => Win32.Graphics.Direct3D12.ResourceStates.GenericRead,
|
||||
MemoryType.Readback => Win32.Graphics.Direct3D12.ResourceStates.CopyDest,
|
||||
_ => Win32.Graphics.Direct3D12.ResourceStates.Common
|
||||
};
|
||||
|
||||
device.Get()->CreateCommittedResource(
|
||||
&heapProps,
|
||||
HeapFlags.None,
|
||||
&resourceDesc,
|
||||
initialState,
|
||||
null,
|
||||
__uuidof<ID3D12Resource>(),
|
||||
_resource.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
public void* Map()
|
||||
{
|
||||
if (_mappedPtr != null)
|
||||
return _mappedPtr;
|
||||
|
||||
var range = new Win32.Graphics.Direct3D12.Range { Begin = 0, End = 0 };
|
||||
|
||||
fixed (void** ptr = &_mappedPtr)
|
||||
{
|
||||
_resource.Get()->Map(0, &range, ptr);
|
||||
}
|
||||
return _mappedPtr;
|
||||
}
|
||||
|
||||
public void Unmap()
|
||||
{
|
||||
if (_mappedPtr != null)
|
||||
{
|
||||
_resource.Get()->Unmap(0, null);
|
||||
_mappedPtr = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static HeapType ConvertMemoryType(MemoryType memoryType)
|
||||
{
|
||||
return memoryType switch
|
||||
{
|
||||
MemoryType.Default => HeapType.Default,
|
||||
MemoryType.Upload => HeapType.Upload,
|
||||
MemoryType.Readback => HeapType.Readback,
|
||||
_ => throw new ArgumentException($"Unknown memory type: {memoryType}")
|
||||
};
|
||||
}
|
||||
|
||||
private static ResourceFlags ConvertBufferUsage(BufferUsage usage)
|
||||
{
|
||||
var flags = ResourceFlags.None;
|
||||
|
||||
if ((usage & BufferUsage.Raw) != 0)
|
||||
flags |= ResourceFlags.AllowUnorderedAccess;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
Unmap();
|
||||
_resource.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
162
Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
Normal file
162
Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Runtime.InteropServices;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Numerics;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of command buffer interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
{
|
||||
private ComPtr<ID3D12CommandAllocator> _allocator;
|
||||
private ComPtr<ID3D12GraphicsCommandList10> _commandList;
|
||||
private readonly CommandBufferType _type;
|
||||
private bool _isRecording;
|
||||
private bool _disposed;
|
||||
|
||||
public ID3D12GraphicsCommandList10* NativeCommandList => _commandList.Get();
|
||||
|
||||
public D3D12CommandBuffer(ComPtr<ID3D12Device14> device, 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());
|
||||
|
||||
// Command lists are created in recording state, so close it
|
||||
_commandList.Get()->Close();
|
||||
_isRecording = false;
|
||||
}
|
||||
|
||||
public void Begin()
|
||||
{
|
||||
if (_isRecording)
|
||||
throw new InvalidOperationException("Command buffer is already recording");
|
||||
|
||||
_allocator.Get()->Reset();
|
||||
_commandList.Get()->Reset(_allocator.Get(), null);
|
||||
_isRecording = true;
|
||||
}
|
||||
|
||||
public void End()
|
||||
{
|
||||
if (!_isRecording)
|
||||
throw new InvalidOperationException("Command buffer is not recording");
|
||||
|
||||
_commandList.Get()->Close();
|
||||
_isRecording = false;
|
||||
}
|
||||
|
||||
public void BeginRenderPass(IRenderTarget renderTarget, Color128 clearColor)
|
||||
{
|
||||
// TODO: Implement render pass begin
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void EndRenderPass()
|
||||
{
|
||||
// TODO: Implement render pass end
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetViewport(ViewportDesc viewport)
|
||||
{
|
||||
var d3d12Viewport = new Viewport(viewport.Width, viewport.Height, viewport.X, viewport.Y, viewport.MinDepth, viewport.MaxDepth);
|
||||
_commandList.Get()->RSSetViewports(1, &d3d12Viewport);
|
||||
}
|
||||
|
||||
public void SetScissorRect(RectDesc rect)
|
||||
{
|
||||
var d3d12Rect = new Rect(rect.Left, rect.Top, rect.Right, rect.Bottom);
|
||||
_commandList.Get()->RSSetScissorRects(1, &d3d12Rect);
|
||||
}
|
||||
|
||||
public void ResourceBarrier(IResource resource, ResourceState before, ResourceState after)
|
||||
{
|
||||
if (resource is D3D12Texture d3d12Texture)
|
||||
{
|
||||
_commandList.Get()->ResourceBarrierTransition(d3d12Texture.NativeResource,
|
||||
ConvertResourceState(before), ConvertResourceState(after));
|
||||
}
|
||||
else if (resource is D3D12Buffer d3d12Buffer)
|
||||
{
|
||||
_commandList.Get()->ResourceBarrierTransition(d3d12Buffer.NativeResource,
|
||||
ConvertResourceState(before), ConvertResourceState(after));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Resource must be a D3D12 resource", nameof(resource));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetGraphicsRootSignature(IRootSignature rootSignature)
|
||||
{
|
||||
// TODO: Implement root signature setting
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetPipelineState(IPipelineState pipelineState)
|
||||
{
|
||||
// TODO: Implement pipeline state setting
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetDescriptorHeaps(IDescriptorHeap[] heaps)
|
||||
{
|
||||
// TODO: Implement descriptor heap setting
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void Dispatch(uint threadGroupCountX, uint threadGroupCountY = 1, uint threadGroupCountZ = 1)
|
||||
{
|
||||
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||
}
|
||||
|
||||
private static CommandListType ConvertCommandBufferType(CommandBufferType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
CommandBufferType.Graphics => CommandListType.Direct,
|
||||
CommandBufferType.Compute => CommandListType.Compute,
|
||||
CommandBufferType.Copy => CommandListType.Copy,
|
||||
_ => throw new ArgumentException($"Unknown command buffer type: {type}")
|
||||
};
|
||||
}
|
||||
|
||||
private static Win32.Graphics.Direct3D12.ResourceStates ConvertResourceState(ResourceState state)
|
||||
{
|
||||
return state switch
|
||||
{
|
||||
ResourceState.Common or ResourceState.Present => Win32.Graphics.Direct3D12.ResourceStates.Common,
|
||||
ResourceState.VertexAndConstantBuffer => Win32.Graphics.Direct3D12.ResourceStates.VertexAndConstantBuffer,
|
||||
ResourceState.IndexBuffer => Win32.Graphics.Direct3D12.ResourceStates.IndexBuffer,
|
||||
ResourceState.RenderTarget => Win32.Graphics.Direct3D12.ResourceStates.RenderTarget,
|
||||
ResourceState.UnorderedAccess => Win32.Graphics.Direct3D12.ResourceStates.UnorderedAccess,
|
||||
ResourceState.DepthWrite => Win32.Graphics.Direct3D12.ResourceStates.DepthWrite,
|
||||
ResourceState.DepthRead => Win32.Graphics.Direct3D12.ResourceStates.DepthRead,
|
||||
ResourceState.PixelShaderResource => Win32.Graphics.Direct3D12.ResourceStates.PixelShaderResource,
|
||||
ResourceState.CopyDest => Win32.Graphics.Direct3D12.ResourceStates.CopyDest,
|
||||
ResourceState.CopySource => Win32.Graphics.Direct3D12.ResourceStates.CopySource,
|
||||
_ => throw new ArgumentException($"Unknown resource state: {state}")
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_commandList.Dispose();
|
||||
_allocator.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
120
Ghost.Graphics/D3D12/D3D12CommandQueue.cs
Normal file
120
Ghost.Graphics/D3D12/D3D12CommandQueue.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of command queue interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
{
|
||||
private ComPtr<ID3D12CommandQueue> _queue;
|
||||
private ComPtr<ID3D12Fence1> _fence;
|
||||
private readonly AutoResetEvent _fenceEvent;
|
||||
private ulong _fenceValue;
|
||||
private bool _disposed;
|
||||
|
||||
public CommandQueueType Type { get; }
|
||||
public ID3D12CommandQueue* NativeQueue => _queue.Get();
|
||||
|
||||
public D3D12CommandQueue(ComPtr<ID3D12Device14> device, CommandQueueType type)
|
||||
{
|
||||
Type = type;
|
||||
_fenceEvent = new AutoResetEvent(false);
|
||||
_fenceValue = 0;
|
||||
|
||||
var queueDesc = new CommandQueueDescription
|
||||
{
|
||||
Type = ConvertCommandQueueType(type),
|
||||
Priority = (int)CommandQueuePriority.Normal,
|
||||
Flags = CommandQueueFlags.None,
|
||||
};
|
||||
|
||||
fixed (void* queuePtr = &_queue)
|
||||
{
|
||||
device.Get()->CreateCommandQueue(&queueDesc, __uuidof<ID3D12CommandQueue>(), (void**)queuePtr);
|
||||
}
|
||||
|
||||
device.Get()->CreateFence(0, FenceFlags.None, __uuidof<ID3D12Fence1>(), _fence.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
public void Submit(ICommandBuffer commandBuffer)
|
||||
{
|
||||
if (commandBuffer is D3D12CommandBuffer d3d12CommandBuffer)
|
||||
{
|
||||
var commandList = d3d12CommandBuffer.NativeCommandList;
|
||||
var commandListPtr = (ID3D12CommandList*)commandList;
|
||||
_queue.Get()->ExecuteCommandLists(1, &commandListPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Command buffer must be a D3D12CommandBuffer", nameof(commandBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
public void Submit(ICommandBuffer[] commandBuffers)
|
||||
{
|
||||
var commandLists = stackalloc ID3D12CommandList*[commandBuffers.Length];
|
||||
|
||||
for (int i = 0; i < commandBuffers.Length; i++)
|
||||
{
|
||||
if (commandBuffers[i] is D3D12CommandBuffer d3d12CommandBuffer)
|
||||
{
|
||||
commandLists[i] = (ID3D12CommandList*)d3d12CommandBuffer.NativeCommandList;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Command buffer at index {i} must be a D3D12CommandBuffer", nameof(commandBuffers));
|
||||
}
|
||||
}
|
||||
|
||||
_queue.Get()->ExecuteCommandLists((uint)commandBuffers.Length, commandLists);
|
||||
}
|
||||
|
||||
public ulong Signal(ulong value)
|
||||
{
|
||||
_fenceValue = value;
|
||||
_queue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue);
|
||||
return _fenceValue;
|
||||
}
|
||||
|
||||
public void WaitForValue(ulong value)
|
||||
{
|
||||
if (_fence.Get()->GetCompletedValue() < value)
|
||||
{
|
||||
var handle = new Handle((void*)_fenceEvent.SafeWaitHandle.DangerousGetHandle());
|
||||
if (_fence.Get()->SetEventOnCompletion(value, handle).Success)
|
||||
{
|
||||
_fenceEvent.WaitOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetCompletedValue()
|
||||
{
|
||||
return _fence.Get()->GetCompletedValue();
|
||||
}
|
||||
|
||||
private static CommandListType ConvertCommandQueueType(CommandQueueType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
CommandQueueType.Graphics => CommandListType.Direct,
|
||||
CommandQueueType.Compute => CommandListType.Compute,
|
||||
CommandQueueType.Copy => CommandListType.Copy,
|
||||
_ => throw new ArgumentException($"Unknown command queue type: {type}")
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_fenceEvent?.Dispose();
|
||||
_fence.Dispose();
|
||||
_queue.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
88
Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs
Normal file
88
Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of descriptor allocator interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12DescriptorAllocator : IDescriptorAllocator
|
||||
{
|
||||
private readonly DescriptorAllocator _internalAllocator;
|
||||
private bool _disposed;
|
||||
|
||||
public D3D12DescriptorAllocator(ComPtr<ID3D12Device14> device)
|
||||
{
|
||||
_internalAllocator = new DescriptorAllocator();
|
||||
}
|
||||
|
||||
public DescriptorHandle AllocateRTV()
|
||||
{
|
||||
var rtvDescriptor = _internalAllocator.AllocateRTV();
|
||||
return new DescriptorHandle(rtvDescriptor.Index);
|
||||
}
|
||||
|
||||
public DescriptorHandle[] AllocateRTVs(uint count)
|
||||
{
|
||||
var rtvDescriptors = _internalAllocator.AllocateRTVs(count);
|
||||
return rtvDescriptors.Select(desc => new DescriptorHandle(desc.Index)).ToArray();
|
||||
}
|
||||
|
||||
public DescriptorHandle AllocateDSV()
|
||||
{
|
||||
var dsvDescriptor = _internalAllocator.AllocateDSV();
|
||||
return new DescriptorHandle(dsvDescriptor.Index);
|
||||
}
|
||||
|
||||
public DescriptorHandle AllocateSRV()
|
||||
{
|
||||
var srvDescriptor = _internalAllocator.AllocateSRV();
|
||||
return new DescriptorHandle(srvDescriptor.Index);
|
||||
}
|
||||
|
||||
public DescriptorHandle AllocateSampler()
|
||||
{
|
||||
var samplerDescriptor = _internalAllocator.AllocateSampler();
|
||||
return new DescriptorHandle(samplerDescriptor.Index);
|
||||
}
|
||||
|
||||
public DescriptorHandle AllocateBindless()
|
||||
{
|
||||
var bindlessDescriptor = _internalAllocator.AllocateBindless();
|
||||
return new DescriptorHandle(bindlessDescriptor.Index);
|
||||
}
|
||||
|
||||
public void ReleaseRTV(DescriptorHandle handle)
|
||||
{
|
||||
// TODO: Convert back to internal descriptor and release
|
||||
}
|
||||
|
||||
public void ReleaseDSV(DescriptorHandle handle)
|
||||
{
|
||||
// TODO: Convert back to internal descriptor and release
|
||||
}
|
||||
|
||||
public void ReleaseSRV(DescriptorHandle handle)
|
||||
{
|
||||
// TODO: Convert back to internal descriptor and release
|
||||
}
|
||||
|
||||
public void ReleaseSampler(DescriptorHandle handle)
|
||||
{
|
||||
// TODO: Convert back to internal descriptor and release
|
||||
}
|
||||
|
||||
public void ReleaseBindless(DescriptorHandle handle)
|
||||
{
|
||||
// TODO: Convert back to internal descriptor and release
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_internalAllocator?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
123
Ghost.Graphics/D3D12/D3D12RenderDevice.cs
Normal file
123
Ghost.Graphics/D3D12/D3D12RenderDevice.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of the render device interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
{
|
||||
private ComPtr<IDXGIFactory7> _dxgiFactory;
|
||||
private ComPtr<ID3D12Device14> _device;
|
||||
private ComPtr<IDXGIAdapter1> _adapter;
|
||||
|
||||
private D3D12CommandQueue _graphicsQueue;
|
||||
private D3D12CommandQueue _computeQueue;
|
||||
private D3D12CommandQueue _copyQueue;
|
||||
private D3D12DescriptorAllocator _descriptorAllocator;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ICommandQueue GraphicsQueue => _graphicsQueue;
|
||||
public ICommandQueue ComputeQueue => _computeQueue;
|
||||
public ICommandQueue CopyQueue => _copyQueue;
|
||||
public IDescriptorAllocator DescriptorAllocator => _descriptorAllocator;
|
||||
|
||||
public ConstPtr<ID3D12Device14> NativeDevice => new(_device.Get());
|
||||
public ConstPtr<IDXGIFactory7> DXGIFactory => new(_dxgiFactory.Get());
|
||||
public ConstPtr<IDXGIAdapter1> Adapter => new(_adapter.Get());
|
||||
|
||||
public D3D12RenderDevice()
|
||||
{
|
||||
InitializeDevice();
|
||||
|
||||
_graphicsQueue = new D3D12CommandQueue(_device, CommandQueueType.Graphics);
|
||||
_computeQueue = new D3D12CommandQueue(_device, CommandQueueType.Compute);
|
||||
_copyQueue = new D3D12CommandQueue(_device, CommandQueueType.Copy);
|
||||
|
||||
_descriptorAllocator = new D3D12DescriptorAllocator(_device);
|
||||
}
|
||||
|
||||
private void InitializeDevice()
|
||||
{
|
||||
#if DEBUG
|
||||
CreateDXGIFactory2(true, __uuidof<IDXGIFactory7>(), _dxgiFactory.GetVoidAddressOf());
|
||||
#else
|
||||
CreateDXGIFactory2(false, __uuidof<IDXGIFactory7>(), _dxgiFactory.GetVoidAddressOf());
|
||||
#endif
|
||||
|
||||
using ComPtr<IDXGIAdapter1> adapter = default;
|
||||
|
||||
for (uint adapterIndex = 0;
|
||||
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, GpuPreference.HighPerformance, __uuidof<IDXGIAdapter1>(), adapter.ReleaseAndGetVoidAddressOf()).Success;
|
||||
adapterIndex++)
|
||||
{
|
||||
AdapterDescription1 desc = default;
|
||||
adapter.Get()->GetDesc1(&desc);
|
||||
|
||||
// Don't select the Basic Render Driver adapter.
|
||||
if ((desc.Flags & AdapterFlags.Software) != AdapterFlags.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (D3D12CreateDevice((IUnknown*)adapter.Get(), FeatureLevel.Level_12_0, __uuidof<ID3D12Device14>(), _device.GetVoidAddressOf()).Success)
|
||||
{
|
||||
_adapter = adapter.Move();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_device.Get() == null)
|
||||
{
|
||||
throw new PlatformNotSupportedException("Cannot create ID3D12Device with feature level 12.0");
|
||||
}
|
||||
}
|
||||
|
||||
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 IRenderTarget CreateRenderTarget(RenderTargetDesc desc)
|
||||
{
|
||||
return new D3D12RenderTarget(_device, _descriptorAllocator, desc);
|
||||
}
|
||||
|
||||
public ITexture CreateTexture(TextureDesc desc)
|
||||
{
|
||||
return new D3D12Texture(_device, desc);
|
||||
}
|
||||
|
||||
public IBuffer CreateBuffer(BufferDesc desc)
|
||||
{
|
||||
return new D3D12Buffer(_device, desc);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_descriptorAllocator?.Dispose();
|
||||
_graphicsQueue?.Dispose();
|
||||
_computeQueue?.Dispose();
|
||||
_copyQueue?.Dispose();
|
||||
|
||||
_device.Reset();
|
||||
_dxgiFactory.Dispose();
|
||||
_adapter.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
40
Ghost.Graphics/D3D12/D3D12RenderTarget.cs
Normal file
40
Ghost.Graphics/D3D12/D3D12RenderTarget.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of render target interface
|
||||
/// Supports either color OR depth rendering, not both
|
||||
/// </summary>
|
||||
internal unsafe class D3D12RenderTarget : IRenderTarget
|
||||
{
|
||||
private readonly D3D12Texture _target;
|
||||
private bool _disposed;
|
||||
|
||||
public uint Width { get; }
|
||||
public uint Height { get; }
|
||||
public RenderTargetType Type { get; }
|
||||
public ITexture Target => _target;
|
||||
|
||||
public D3D12RenderTarget(ComPtr<ID3D12Device14> device, D3D12DescriptorAllocator descriptorAllocator, 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, 1, usage);
|
||||
_target = new D3D12Texture(device, textureDesc);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_target?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
222
Ghost.Graphics/D3D12/D3D12Renderer.cs
Normal file
222
Ghost.Graphics/D3D12/D3D12Renderer.cs
Normal file
@@ -0,0 +1,222 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Data;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of the renderer interface using RHI abstractions
|
||||
/// </summary>
|
||||
public unsafe class D3D12Renderer : IRenderer
|
||||
{
|
||||
private struct FrameResource : IDisposable
|
||||
{
|
||||
public ICommandBuffer CommandBuffer;
|
||||
public ulong FenceValue;
|
||||
|
||||
public FrameResource(IRenderDevice device)
|
||||
{
|
||||
CommandBuffer = device.CreateCommandBuffer();
|
||||
FenceValue = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CommandBuffer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IRenderDevice _device;
|
||||
private readonly ICommandQueue _commandQueue;
|
||||
private readonly FrameResource[] _frameResources;
|
||||
private uint _frameIndex;
|
||||
|
||||
private IRenderTarget? _renderTarget;
|
||||
private ISwapChain? _swapChain;
|
||||
|
||||
private readonly Lock _lock = new();
|
||||
private uint _pendingWidth;
|
||||
private uint _pendingHeight;
|
||||
private bool _resizeRequested;
|
||||
private bool _disposed;
|
||||
|
||||
// TODO: Add render passes support
|
||||
// private ImmutableArray<IRenderPass> _renderPasses;
|
||||
|
||||
public D3D12Renderer(IRenderDevice device)
|
||||
{
|
||||
_device = device;
|
||||
_commandQueue = device.GraphicsQueue;
|
||||
|
||||
// Create frame resources for double buffering
|
||||
_frameResources = new FrameResource[2];
|
||||
for (int i = 0; i < _frameResources.Length; i++)
|
||||
{
|
||||
_frameResources[i] = new FrameResource(device);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRenderTarget(IRenderTarget? renderTarget)
|
||||
{
|
||||
_renderTarget = renderTarget;
|
||||
_swapChain = null; // Clear swap chain when using render target
|
||||
}
|
||||
|
||||
public void SetSwapChain(ISwapChain? swapChain)
|
||||
{
|
||||
_swapChain = swapChain;
|
||||
_renderTarget = null; // Clear render target when using swap chain
|
||||
}
|
||||
|
||||
public void RequestResize(uint width, uint height)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_pendingWidth == width && _pendingHeight == height)
|
||||
return;
|
||||
|
||||
_resizeRequested = true;
|
||||
_pendingWidth = width;
|
||||
_pendingHeight = height;
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecutePendingResize()
|
||||
{
|
||||
if (!_resizeRequested)
|
||||
return;
|
||||
|
||||
uint newWidth, newHeight;
|
||||
lock (_lock)
|
||||
{
|
||||
newWidth = _pendingWidth;
|
||||
newHeight = _pendingHeight;
|
||||
_resizeRequested = false;
|
||||
}
|
||||
|
||||
// Wait for GPU to complete
|
||||
WaitIdle();
|
||||
|
||||
// Resize swap chain if present
|
||||
_swapChain?.Resize(newWidth, newHeight);
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
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)
|
||||
{
|
||||
_commandQueue.WaitForValue(frame.FenceValue);
|
||||
}
|
||||
|
||||
// Begin command recording
|
||||
frame.CommandBuffer.Begin();
|
||||
|
||||
if (_renderTarget != null)
|
||||
{
|
||||
RenderToTarget(_renderTarget, frame.CommandBuffer);
|
||||
}
|
||||
else if (_swapChain != null)
|
||||
{
|
||||
RenderToSwapChain(_swapChain, frame.CommandBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No render target - skip rendering
|
||||
frame.CommandBuffer.End();
|
||||
return;
|
||||
}
|
||||
|
||||
// End command recording
|
||||
frame.CommandBuffer.End();
|
||||
|
||||
// Submit commands
|
||||
_commandQueue.Submit(frame.CommandBuffer);
|
||||
|
||||
// Present if using swap chain
|
||||
_swapChain?.Present();
|
||||
|
||||
// Signal fence for this frame
|
||||
frame.FenceValue = _commandQueue.Signal(++_frameIndex);
|
||||
}
|
||||
|
||||
private void RenderToTarget(IRenderTarget target, ICommandBuffer cmd)
|
||||
{
|
||||
var clearColor = new Color128 { r = 1.0f, g = 0.0f, b = 1.0f, a = 1.0f };
|
||||
|
||||
cmd.BeginRenderPass(target, clearColor);
|
||||
|
||||
var viewport = new ViewportDesc(target.Width, target.Height);
|
||||
var scissor = new RectDesc(0, 0, (int)target.Width, (int)target.Height);
|
||||
|
||||
cmd.SetViewport(viewport);
|
||||
cmd.SetScissorRect(scissor);
|
||||
|
||||
// TODO: Execute render passes
|
||||
// foreach (var pass in _renderPasses)
|
||||
// {
|
||||
// pass.Execute(cmd);
|
||||
// }
|
||||
|
||||
cmd.EndRenderPass();
|
||||
}
|
||||
|
||||
private void RenderToSwapChain(ISwapChain swapChain, ICommandBuffer cmd)
|
||||
{
|
||||
var backBuffer = swapChain.GetCurrentBackBuffer();
|
||||
|
||||
// Transition back buffer to render target
|
||||
cmd.ResourceBarrier(backBuffer, ResourceState.Present, ResourceState.RenderTarget);
|
||||
|
||||
// Create temporary render target for back buffer
|
||||
// TODO: This should be cached/reused
|
||||
var renderTarget = CreateBackBufferRenderTarget(backBuffer);
|
||||
|
||||
RenderToTarget(renderTarget, cmd);
|
||||
|
||||
// Transition back buffer to present
|
||||
cmd.ResourceBarrier(backBuffer, ResourceState.RenderTarget, ResourceState.Present);
|
||||
|
||||
renderTarget.Dispose();
|
||||
}
|
||||
|
||||
private IRenderTarget CreateBackBufferRenderTarget(ITexture backBuffer)
|
||||
{
|
||||
// TODO: Create render target from back buffer texture
|
||||
// This is a simplified implementation
|
||||
var desc = RenderTargetDesc.Color(backBuffer.Width, backBuffer.Height, backBuffer.Format);
|
||||
return _device.CreateRenderTarget(desc);
|
||||
}
|
||||
|
||||
public void WaitIdle()
|
||||
{
|
||||
// Wait for all frame resources to complete
|
||||
foreach (ref var frame in _frameResources.AsSpan())
|
||||
{
|
||||
if (frame.FenceValue > 0)
|
||||
{
|
||||
_commandQueue.WaitForValue(frame.FenceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
WaitIdle();
|
||||
|
||||
foreach (ref var frame in _frameResources.AsSpan())
|
||||
{
|
||||
frame.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
174
Ghost.Graphics/D3D12/D3D12SwapChain.cs
Normal file
174
Ghost.Graphics/D3D12/D3D12SwapChain.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of swap chain interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12SwapChain : ISwapChain
|
||||
{
|
||||
private ComPtr<IDXGISwapChain4> _swapChain;
|
||||
private readonly D3D12Texture[] _backBuffers;
|
||||
private uint _currentBackBufferIndex;
|
||||
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)
|
||||
{
|
||||
_backBuffers = new D3D12Texture[desc.BufferCount];
|
||||
|
||||
Width = desc.Width;
|
||||
Height = desc.Height;
|
||||
BufferCount = desc.BufferCount;
|
||||
|
||||
CreateSwapChain(factory, commandQueue, desc);
|
||||
CreateBackBuffers();
|
||||
}
|
||||
|
||||
private void CreateSwapChain(ComPtr<IDXGIFactory7> factory, ID3D12CommandQueue* commandQueue, SwapChainDesc desc)
|
||||
{
|
||||
var swapChainDesc = new SwapChainDescription1
|
||||
{
|
||||
Width = desc.Width,
|
||||
Height = desc.Height,
|
||||
Format = ConvertTextureFormat(desc.Format),
|
||||
SampleDesc = new SampleDescription(1, 0),
|
||||
BufferUsage = Usage.BackBuffer | Usage.RenderTargetOutput,
|
||||
BufferCount = desc.BufferCount,
|
||||
Scaling = Scaling.Stretch,
|
||||
SwapEffect = SwapEffect.FlipDiscard,
|
||||
AlphaMode = AlphaMode.Ignore,
|
||||
Flags = SwapChainFlags.AllowTearing,
|
||||
Stereo = false,
|
||||
};
|
||||
|
||||
using ComPtr<IDXGISwapChain1> tempSwapChain = default;
|
||||
|
||||
switch (desc.Target.Type)
|
||||
{
|
||||
case SwapChainTargetType.Composition:
|
||||
factory.Get()->CreateSwapChainForComposition((IUnknown*)commandQueue, &swapChainDesc, null, tempSwapChain.GetAddressOf());
|
||||
|
||||
// Set the composition surface
|
||||
if (desc.Target.CompositionSurface != null)
|
||||
{
|
||||
var swapChainPanelNative = ISwapChainPanelNative.FromSwapChainPanel(desc.Target.CompositionSurface);
|
||||
swapChainPanelNative.SetSwapChain((IntPtr)tempSwapChain.Get());
|
||||
}
|
||||
break;
|
||||
|
||||
case SwapChainTargetType.WindowHandle:
|
||||
var swapChainFullscreenDesc = new SwapChainFullscreenDescription
|
||||
{
|
||||
Windowed = true,
|
||||
};
|
||||
|
||||
factory.Get()->CreateSwapChainForHwnd(
|
||||
(IUnknown*)commandQueue,
|
||||
desc.Target.WindowHandle,
|
||||
&swapChainDesc,
|
||||
&swapChainFullscreenDesc,
|
||||
null,
|
||||
tempSwapChain.GetAddressOf());
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Unsupported swap chain target type.");
|
||||
}
|
||||
|
||||
if (tempSwapChain.Get()->QueryInterface(__uuidof<IDXGISwapChain4>(), _swapChain.GetVoidAddressOf()).Failure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to create IDXGISwapChain4 interface.");
|
||||
}
|
||||
|
||||
_currentBackBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
}
|
||||
|
||||
private void CreateBackBuffers()
|
||||
{
|
||||
for (uint i = 0; i < BufferCount; i++)
|
||||
{
|
||||
ComPtr<ID3D12Resource> backBuffer = default;
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
public ITexture GetCurrentBackBuffer()
|
||||
{
|
||||
_currentBackBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
return _backBuffers[_currentBackBufferIndex];
|
||||
}
|
||||
|
||||
public void Present(bool vsync = true)
|
||||
{
|
||||
var presentFlags = PresentFlags.None;
|
||||
var syncInterval = vsync ? 1u : 0u;
|
||||
|
||||
if (_swapChain.Get()->Present(syncInterval, presentFlags).Failure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to present swap chain.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Resize(uint width, uint height)
|
||||
{
|
||||
if (Width == width && Height == height)
|
||||
return;
|
||||
|
||||
// Release old back buffers
|
||||
for (int i = 0; i < _backBuffers.Length; i++)
|
||||
{
|
||||
_backBuffers[i]?.Dispose();
|
||||
}
|
||||
|
||||
// Resize the swap chain
|
||||
if (_swapChain.Get()->ResizeBuffers(BufferCount, width, height, Format.B8G8R8A8Unorm, SwapChainFlags.AllowTearing).Failure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to resize swap chain buffers.");
|
||||
}
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
|
||||
// Recreate back buffers
|
||||
CreateBackBuffers();
|
||||
_currentBackBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
}
|
||||
|
||||
private static Format ConvertTextureFormat(TextureFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
TextureFormat.R8G8B8A8_UNorm => Format.R8G8B8A8Unorm,
|
||||
TextureFormat.B8G8R8A8_UNorm => Format.B8G8R8A8Unorm,
|
||||
TextureFormat.R16G16B16A16_Float => Format.R16G16B16A16Float,
|
||||
TextureFormat.R32G32B32A32_Float => Format.R32G32B32A32Float,
|
||||
_ => throw new ArgumentException($"Unsupported texture format: {format}")
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
for (int i = 0; i < _backBuffers.Length; i++)
|
||||
{
|
||||
_backBuffers[i]?.Dispose();
|
||||
}
|
||||
|
||||
_swapChain.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
135
Ghost.Graphics/D3D12/D3D12Texture.cs
Normal file
135
Ghost.Graphics/D3D12/D3D12Texture.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of texture interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12Texture : ITexture
|
||||
{
|
||||
private ComPtr<ID3D12Resource> _resource;
|
||||
private ResourceState _currentState;
|
||||
private bool _disposed;
|
||||
|
||||
public uint Width { get; }
|
||||
public uint Height { get; }
|
||||
public TextureFormat Format { get; }
|
||||
public uint MipLevels { get; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public ulong Size { get; }
|
||||
public ResourceState CurrentState => _currentState;
|
||||
|
||||
public ID3D12Resource* NativeResource => _resource.Get();
|
||||
|
||||
public D3D12Texture(ComPtr<ID3D12Resource> resource, uint width, uint height, TextureFormat format, uint mipLevels = 1)
|
||||
{
|
||||
_resource = resource.Move();
|
||||
Width = width;
|
||||
Height = height;
|
||||
Format = format;
|
||||
MipLevels = mipLevels;
|
||||
_currentState = ResourceState.Common;
|
||||
|
||||
var desc = _resource.Get()->GetDesc();
|
||||
Size = (ulong)(desc.Width * desc.Height * GetBytesPerPixel(format));
|
||||
}
|
||||
|
||||
public D3D12Texture(ComPtr<ID3D12Device14> device, TextureDesc desc)
|
||||
{
|
||||
Width = desc.Width;
|
||||
Height = desc.Height;
|
||||
Format = desc.Format;
|
||||
MipLevels = desc.MipLevels;
|
||||
_currentState = ResourceState.Common;
|
||||
|
||||
CreateTexture(device, desc);
|
||||
Size = (ulong)(Width * Height * GetBytesPerPixel(Format));
|
||||
}
|
||||
|
||||
private void CreateTexture(ComPtr<ID3D12Device14> device, TextureDesc desc)
|
||||
{
|
||||
var resourceDesc = new ResourceDescription
|
||||
{
|
||||
Dimension = ResourceDimension.Texture2D,
|
||||
Alignment = 0,
|
||||
Width = desc.Width,
|
||||
Height = desc.Height,
|
||||
DepthOrArraySize = 1,
|
||||
MipLevels = (ushort)desc.MipLevels,
|
||||
Format = ConvertTextureFormat(desc.Format),
|
||||
SampleDesc = new Win32.Graphics.Dxgi.Common.SampleDescription(1, 0),
|
||||
Layout = TextureLayout.Unknown,
|
||||
Flags = ConvertTextureUsage(desc.Usage)
|
||||
};
|
||||
|
||||
var heapProps = new HeapProperties
|
||||
{
|
||||
Type = HeapType.Default,
|
||||
CPUPageProperty = CpuPageProperty.Unknown,
|
||||
MemoryPoolPreference = MemoryPool.Unknown,
|
||||
CreationNodeMask = 1,
|
||||
VisibleNodeMask = 1
|
||||
};
|
||||
|
||||
device.Get()->CreateCommittedResource(
|
||||
&heapProps,
|
||||
HeapFlags.None,
|
||||
&resourceDesc,
|
||||
Win32.Graphics.Direct3D12.ResourceStates.Common,
|
||||
null,
|
||||
__uuidof<ID3D12Resource>(),
|
||||
_resource.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
private static Win32.Graphics.Dxgi.Common.Format ConvertTextureFormat(TextureFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
TextureFormat.R8G8B8A8_UNorm => Win32.Graphics.Dxgi.Common.Format.R8G8B8A8Unorm,
|
||||
TextureFormat.B8G8R8A8_UNorm => Win32.Graphics.Dxgi.Common.Format.B8G8R8A8Unorm,
|
||||
TextureFormat.R16G16B16A16_Float => Win32.Graphics.Dxgi.Common.Format.R16G16B16A16Float,
|
||||
TextureFormat.R32G32B32A32_Float => Win32.Graphics.Dxgi.Common.Format.R32G32B32A32Float,
|
||||
TextureFormat.D24_UNorm_S8_UInt => Win32.Graphics.Dxgi.Common.Format.D24UnormS8Uint,
|
||||
TextureFormat.D32_Float => Win32.Graphics.Dxgi.Common.Format.D32Float,
|
||||
_ => throw new ArgumentException($"Unsupported texture format: {format}")
|
||||
};
|
||||
}
|
||||
|
||||
private static ResourceFlags ConvertTextureUsage(TextureUsage usage)
|
||||
{
|
||||
var flags = ResourceFlags.None;
|
||||
|
||||
if ((usage & TextureUsage.RenderTarget) != 0)
|
||||
flags |= ResourceFlags.AllowRenderTarget;
|
||||
if ((usage & TextureUsage.DepthStencil) != 0)
|
||||
flags |= ResourceFlags.AllowDepthStencil;
|
||||
if ((usage & TextureUsage.UnorderedAccess) != 0)
|
||||
flags |= ResourceFlags.AllowUnorderedAccess;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
private static uint GetBytesPerPixel(TextureFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
TextureFormat.R8G8B8A8_UNorm => 4,
|
||||
TextureFormat.B8G8R8A8_UNorm => 4,
|
||||
TextureFormat.R16G16B16A16_Float => 8,
|
||||
TextureFormat.R32G32B32A32_Float => 16,
|
||||
TextureFormat.D24_UNorm_S8_UInt => 4,
|
||||
TextureFormat.D32_Float => 4,
|
||||
_ => 4
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_resource.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Data;
|
||||
using System.Collections.Immutable;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
@@ -8,6 +6,12 @@ using Win32.Graphics.Dxgi;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// Legacy D3D12 GraphicsDevice - DEPRECATED
|
||||
/// Use D3D12RenderDevice instead for new code
|
||||
/// This class remains for compatibility during migration
|
||||
/// </summary>
|
||||
[Obsolete("Use D3D12RenderDevice instead")]
|
||||
internal unsafe class GraphicsDevice
|
||||
{
|
||||
private ComPtr<IDXGIFactory7> _dxgiFactory;
|
||||
@@ -15,14 +19,8 @@ internal unsafe class GraphicsDevice
|
||||
private ComPtr<IDXGIAdapter1> _adapter;
|
||||
private ComPtr<ID3D12CommandQueue> _commandQueue;
|
||||
|
||||
private ImmutableArray<Renderer> _initializeQueue;
|
||||
private ImmutableArray<Renderer> _renderers;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ReadOnlySpan<Renderer> InitializeQueue => _initializeQueue.AsSpan();
|
||||
public ReadOnlySpan<Renderer> Renderers => _renderers.AsSpan();
|
||||
|
||||
public ConstPtr<ID3D12Device14> NativeDevice => new(_device.Get());
|
||||
public ConstPtr<IDXGIFactory7> DXGIFactory => new(_dxgiFactory.Get());
|
||||
public ConstPtr<IDXGIAdapter1> Adapter => new(_adapter.Get());
|
||||
@@ -32,9 +30,6 @@ internal unsafe class GraphicsDevice
|
||||
{
|
||||
InitializeDevice();
|
||||
InitializeCommandQueue();
|
||||
|
||||
_initializeQueue = ImmutableArray<Renderer>.Empty;
|
||||
_renderers = ImmutableArray<Renderer>.Empty;
|
||||
}
|
||||
|
||||
private void InitializeDevice()
|
||||
@@ -88,48 +83,6 @@ internal unsafe class GraphicsDevice
|
||||
}
|
||||
}
|
||||
|
||||
public Renderer CreateRenderer(in SwapChainPresenter presenter)
|
||||
{
|
||||
var renderView = new Renderer(this, in presenter);
|
||||
ImmutableInterlocked.Update(ref _initializeQueue, old => old.Add(renderView));
|
||||
|
||||
return renderView;
|
||||
}
|
||||
|
||||
public void RemoveRenderer(Renderer renderer)
|
||||
{
|
||||
if (renderer is Renderer dx12RenderView)
|
||||
{
|
||||
dx12RenderView.Dispose();
|
||||
|
||||
var index = _initializeQueue.IndexOf(dx12RenderView);
|
||||
if (index > -1)
|
||||
{
|
||||
ImmutableInterlocked.Update(ref _initializeQueue, old => old.RemoveAt(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
ImmutableInterlocked.Update(ref _renderers, old => old.Remove(dx12RenderView));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializePendingRenderers()
|
||||
{
|
||||
if (_initializeQueue.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var renderer in _initializeQueue.AsSpan())
|
||||
{
|
||||
renderer.Initialize();
|
||||
}
|
||||
|
||||
ImmutableInterlocked.Update(ref _renderers, old => old.AddRange(_initializeQueue));
|
||||
_initializeQueue = _initializeQueue.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
@@ -137,11 +90,6 @@ internal unsafe class GraphicsDevice
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
renderer.Dispose();
|
||||
}
|
||||
|
||||
_commandQueue.Dispose();
|
||||
_device.Reset();
|
||||
_dxgiFactory.Dispose();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.Data;
|
||||
using System.Collections.Immutable;
|
||||
@@ -13,6 +13,13 @@ namespace Ghost.Graphics.D3D12;
|
||||
// TODO: We should split the renderer and swap chain into different classes to allow for more flexibility in rendering pipelines.
|
||||
// Each renderer can have a render target (swap chain or texture).
|
||||
// When render target is null, skip the render pass execution.
|
||||
|
||||
/// <summary>
|
||||
/// Legacy D3D12 Renderer - DEPRECATED
|
||||
/// Use D3D12Renderer instead for new code
|
||||
/// This class remains for compatibility during migration
|
||||
/// </summary>
|
||||
[Obsolete("Use D3D12Renderer instead")]
|
||||
internal unsafe class Renderer
|
||||
{
|
||||
private struct FrameResource : IDisposable
|
||||
|
||||
@@ -28,7 +28,7 @@ internal unsafe class ResourceAllocator
|
||||
}
|
||||
|
||||
public AllocationInfo(in Allocation allocation, uint generation)
|
||||
: this(allocation, GraphicsPipeline.CPUFenceValue + 1, generation)
|
||||
: this(allocation, 0 /* TODO: Use proper fence value from render system */, generation)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -65,12 +65,12 @@ internal unsafe class ResourceAllocator
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceAllocator()
|
||||
public ResourceAllocator(IDXGIAdapter* pAdapter, ID3D12Device* pDevice)
|
||||
{
|
||||
var desc = new AllocatorDesc
|
||||
{
|
||||
pAdapter = (IDXGIAdapter*)GraphicsPipeline.GraphicsDevice.Adapter.Ptr,
|
||||
pDevice = (ID3D12Device*)GraphicsPipeline.GraphicsDevice.NativeDevice.Ptr,
|
||||
pAdapter = pAdapter,
|
||||
pDevice = pDevice,
|
||||
Flags = AllocatorFlags.DefaultPoolsNotZeroed | AllocatorFlags.MSAATexturesAlwaysCommitted
|
||||
};
|
||||
|
||||
@@ -181,10 +181,8 @@ internal unsafe class ResourceAllocator
|
||||
{
|
||||
ref var handle = ref _temResources.Peek();
|
||||
ref var info = ref _allocations[handle.id];
|
||||
if (info.cpuFenceValue > GraphicsPipeline.GPUFenceValue)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// TODO: Implement proper fence-based cleanup with RenderSystem
|
||||
// For now, just release all temp resources
|
||||
|
||||
ReleaseAllocation(in handle);
|
||||
_temResources.Dequeue();
|
||||
|
||||
Reference in New Issue
Block a user