forked from Misaki/GhostEngine
Add new interfaces and refactor rendering logic
Added a new `ConstPtr<T>` struct for type-safe pointers. Added a new `ICommandBuffer` interface for resource copying. Added a new `IRenderPass` interface to define render passes. Added a new `IResource` interface for GPU resources. Added a new `IResourceAllocator` interface for resource management. Added a new `ISwapChainPanelNative` struct for native interactions. Added a new `D3D12Utility` class for Direct3D 12 utilities. Added a new package reference for `Vortice.Win32.Graphics.D3D12MemoryAllocator`. Changed project file to allow unsafe code blocks. Changed `Result` struct methods to improve clarity. Changed error handling in `ProjectService` and `AssetDatabase` to use `Result.Failure()`. Changed `launchSettings.json` to enable native debugging. Changed rendering logic in `ScenePage.xaml.cs` to use `IRenderer`. Changed `IGraphicsDevice` interface to include renderer properties. Changed `IRenderView` to `IRenderer` and updated its methods. Changed `Mesh` class to use the new `IResource` interface for buffers. Changed `GraphicsAPI` enum to include a `None` value. Changed various aspects of the `GraphicsPipeline` class for new architecture. Removed the old `DX12RenderView` class and replaced it with `DX12Renderer`. Removed unnecessary code in the `ResourceView` class.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Data;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Vortice.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.DX12;
|
||||
@@ -11,4 +13,14 @@ internal class DX12CommandBuffer : ICommandBuffer
|
||||
{
|
||||
_commandList = commandList;
|
||||
}
|
||||
|
||||
public void CopyResource(IResource dstResource, uint dstOffset, IResource srcResource, uint srcOffset, uint size)
|
||||
{
|
||||
GraphicsPipeline.CheckAPI(GraphicsAPI.DX12).EnsureSuccess();
|
||||
|
||||
var dstDXResource = Unsafe.As<DX12Resource>(dstResource);
|
||||
var srcDXResource = Unsafe.As<DX12Resource>(srcResource);
|
||||
|
||||
_commandList.CopyBufferRegion(dstDXResource.NativeResource, dstOffset, srcDXResource.NativeResource, srcOffset, size);
|
||||
}
|
||||
}
|
||||
@@ -1,82 +1,93 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Data;
|
||||
using Vortice.Direct3D;
|
||||
using Vortice.Direct3D12;
|
||||
using Vortice.DXGI;
|
||||
using System.Collections.Immutable;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi;
|
||||
using static Win32.Apis;
|
||||
using static Win32.Graphics.Direct3D12.Apis;
|
||||
using static Win32.Graphics.Dxgi.Apis;
|
||||
|
||||
namespace Ghost.Graphics.DX12;
|
||||
|
||||
internal class DX12GraphicsDevice : IGraphicsDevice
|
||||
internal unsafe class DX12GraphicsDevice : IGraphicsDevice
|
||||
{
|
||||
private readonly IDXGIFactory7 _dxgiFactory;
|
||||
private readonly ID3D12Device14 _device;
|
||||
private readonly ID3D12CommandQueue _commandQueue;
|
||||
|
||||
private readonly List<IRenderView> _renderViews = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
#if DEBUG
|
||||
private readonly DX12DebugLayer _debugLayer;
|
||||
#endif
|
||||
private readonly ComPtr<IDXGIFactory7> _dxgiFactory;
|
||||
private readonly ComPtr<ID3D12Device14> _device;
|
||||
private readonly ComPtr<ID3D12CommandQueue> _commandQueue;
|
||||
|
||||
private ImmutableArray<IRenderer> _renderers;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ID3D12Device14 Device => _device;
|
||||
public IDXGIFactory7 DXGIFactory => _dxgiFactory;
|
||||
public ID3D12CommandQueue CommandQueue => _commandQueue;
|
||||
public static GraphicsAPI TargetAPI => GraphicsAPI.DX12;
|
||||
public ReadOnlySpan<IRenderer> Renderers => _renderers.AsSpan();
|
||||
|
||||
public static IGraphicsDevice Create() => new DX12GraphicsDevice();
|
||||
public ConstPtr<ID3D12Device14> NativeDevice => new(_device.Get());
|
||||
public ConstPtr<IDXGIFactory7> DXGIFactory => new(_dxgiFactory.Get());
|
||||
public ConstPtr<ID3D12CommandQueue> CommandQueue => new(_commandQueue.Get());
|
||||
|
||||
private DX12GraphicsDevice()
|
||||
public DX12GraphicsDevice()
|
||||
{
|
||||
#if DEBUG
|
||||
_debugLayer = new DX12DebugLayer();
|
||||
#endif
|
||||
|
||||
InitializeDevice(out _dxgiFactory, out _device);
|
||||
InitializeCommandQueue(out _commandQueue);
|
||||
InitializeDevice();
|
||||
InitializeCommandQueue();
|
||||
|
||||
_renderers = ImmutableArray<IRenderer>.Empty;
|
||||
}
|
||||
|
||||
private void InitializeDevice(out IDXGIFactory7 factory, out ID3D12Device14 device)
|
||||
private void InitializeDevice()
|
||||
{
|
||||
fixed (void* factoryPtr = &_dxgiFactory)
|
||||
{
|
||||
#if DEBUG
|
||||
factory = DXGI.CreateDXGIFactory2<IDXGIFactory7>(true);
|
||||
CreateDXGIFactory2(true, __uuidof<IDXGIFactory2>(), &factoryPtr);
|
||||
//factory = DXGI.CreateDXGIFactory2<IDXGIFactory7>(true);
|
||||
#else
|
||||
factory = DXGI.CreateDXGIFactory2<IDXGIFactory7>(false);
|
||||
//factory = DXGI.CreateDXGIFactory2<IDXGIFactory7>(false);
|
||||
CreateDXGIFactory2(false, __uuidof<IDXGIFactory2>(), &factoryPtr);
|
||||
#endif
|
||||
}
|
||||
|
||||
using ComPtr<IDXGIAdapter1> adapter = default;
|
||||
|
||||
ID3D12Device14? d3d12Device = default;
|
||||
for (uint adapterIndex = 0;
|
||||
factory.EnumAdapters1(adapterIndex, out var adapter).Success;
|
||||
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, GpuPreference.HighPerformance, __uuidof<IDXGIAdapter1>(), (void**)adapter.ReleaseAndGetAddressOf()).Success;
|
||||
adapterIndex++)
|
||||
{
|
||||
var desc = adapter.Description1;
|
||||
AdapterDescription1 desc = default;
|
||||
adapter.Get()->GetDesc1(&desc);
|
||||
|
||||
// Don't select the Basic Render Driver adapter.
|
||||
if ((desc.Flags & AdapterFlags.Software) != AdapterFlags.None)
|
||||
{
|
||||
adapter.Dispose();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (D3D12.D3D12CreateDevice(adapter, FeatureLevel.Level_11_0, out d3d12Device).Success)
|
||||
fixed (void* devicePtr = &_device)
|
||||
{
|
||||
adapter.Dispose();
|
||||
break;
|
||||
if (D3D12CreateDevice((IUnknown*)adapter.Get(), FeatureLevel.Level_11_0, __uuidof<ID3D12Device>(), (void**)devicePtr).Success)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
adapter.Dispose();
|
||||
}
|
||||
|
||||
if (d3d12Device == null)
|
||||
if (_device.Get() == null)
|
||||
{
|
||||
throw new PlatformNotSupportedException("Cannot create ID3D12Device");
|
||||
}
|
||||
|
||||
device = d3d12Device;
|
||||
}
|
||||
|
||||
private void InitializeCommandQueue(out ID3D12CommandQueue queue)
|
||||
private void InitializeCommandQueue()
|
||||
{
|
||||
var queueDesc = new CommandQueueDescription
|
||||
{
|
||||
@@ -85,32 +96,26 @@ internal class DX12GraphicsDevice : IGraphicsDevice
|
||||
Flags = CommandQueueFlags.None,
|
||||
};
|
||||
|
||||
queue = _device.CreateCommandQueue(queueDesc);
|
||||
fixed (void* queuePtr = &_commandQueue)
|
||||
{
|
||||
_device.Get()->CreateCommandQueue(&queueDesc, __uuidof<ID3D12CommandQueue>(), &queuePtr);
|
||||
}
|
||||
}
|
||||
|
||||
public IRenderView CreateRenderView(in SwapChainPresenter swapChainSurface)
|
||||
public IRenderer CreateRenderer(in SwapChainPresenter presenter)
|
||||
{
|
||||
var renderView = new DX12RenderView(this, swapChainSurface);
|
||||
lock (_lock)
|
||||
{
|
||||
_renderViews.Add(renderView);
|
||||
}
|
||||
var renderView = new DX12Renderer(this, in presenter);
|
||||
ImmutableInterlocked.Update(ref _renderers, old => old.Add(renderView));
|
||||
|
||||
return renderView;
|
||||
}
|
||||
|
||||
public void OnRender()
|
||||
public void RemoveRenderer(IRenderer renderer)
|
||||
{
|
||||
lock (_lock)
|
||||
if (renderer is DX12Renderer dx12RenderView)
|
||||
{
|
||||
foreach (var renderView in _renderViews)
|
||||
{
|
||||
renderView.ExecutePendingResize();
|
||||
|
||||
renderView.BeginRender();
|
||||
renderView.Render();
|
||||
renderView.EndRender();
|
||||
}
|
||||
dx12RenderView.Dispose();
|
||||
ImmutableInterlocked.Update(ref _renderers, old => old.Remove(dx12RenderView));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,15 +126,19 @@ internal class DX12GraphicsDevice : IGraphicsDevice
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var renderView in _renderViews)
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
renderer.Dispose();
|
||||
}
|
||||
|
||||
foreach (var renderView in _renderers)
|
||||
{
|
||||
renderView.Dispose();
|
||||
}
|
||||
_renderViews.Clear();
|
||||
|
||||
_commandQueue.Release();
|
||||
_device.Release();
|
||||
_dxgiFactory.Release();
|
||||
_commandQueue.Dispose();
|
||||
_device.Dispose();
|
||||
_dxgiFactory.Dispose();
|
||||
|
||||
#if DEBUG
|
||||
_debugLayer.Dispose();
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.DX12.Utilities;
|
||||
using Vortice.Direct3D12;
|
||||
using Vortice.DXGI;
|
||||
|
||||
namespace Ghost.Graphics.DX12;
|
||||
|
||||
internal class DX12RenderView : IRenderView
|
||||
{
|
||||
private const int _RENDER_TARGET_VIEW_HEAP_SIZE = 1024;
|
||||
private const int _DEPTH_STENCIL_VIEW_HEAP_SIZE = 256;
|
||||
|
||||
private readonly DX12GraphicsDevice _graphicsDevice;
|
||||
private readonly SwapChainPresenter _swapChainPresenter;
|
||||
|
||||
private readonly IDXGISwapChain4 _swapChain;
|
||||
private readonly ID3D12Resource[] _renderTargets;
|
||||
private readonly uint[] _renderTargetDescriptorIndexes;
|
||||
private uint _backBufferIndex;
|
||||
|
||||
private readonly ID3D12CommandAllocator[] _commandAllocators;
|
||||
private readonly ID3D12GraphicsCommandList10 _commandList;
|
||||
|
||||
private readonly ID3D12Fence1 _fence;
|
||||
private readonly AutoResetEvent _fenceEvent;
|
||||
private readonly ulong[] _fenceValues;
|
||||
|
||||
private readonly D3D12DescriptorAllocator _rtvHeap;
|
||||
|
||||
private readonly ICommandBuffer _commandBuffer;
|
||||
|
||||
private readonly Lock _lock = new();
|
||||
private uint _pendingWidth;
|
||||
private uint _pendingHeight;
|
||||
private bool _resizeRequested;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public DX12RenderView(DX12GraphicsDevice graphicsDevice, in SwapChainPresenter swapChainSurface)
|
||||
{
|
||||
_graphicsDevice = graphicsDevice;
|
||||
_swapChainPresenter = swapChainSurface;
|
||||
|
||||
_rtvHeap = new(_graphicsDevice.Device, DescriptorHeapType.RenderTargetView, _RENDER_TARGET_VIEW_HEAP_SIZE);
|
||||
|
||||
_fenceEvent = new AutoResetEvent(false);
|
||||
_renderTargets = new ID3D12Resource[GraphicsPipeline.FRAME_COUNT];
|
||||
_fenceValues = new ulong[GraphicsPipeline.FRAME_COUNT];
|
||||
_renderTargetDescriptorIndexes = new uint[GraphicsPipeline.FRAME_COUNT];
|
||||
|
||||
InitializeSwapChain(out _swapChain);
|
||||
InitializeCommandObjects(out _commandAllocators, out _commandList, out _fence);
|
||||
CreateRenderTargets();
|
||||
|
||||
_commandBuffer = new DX12CommandBuffer(_commandList);
|
||||
}
|
||||
|
||||
private void InitializeSwapChain(out IDXGISwapChain4 swapChain)
|
||||
{
|
||||
var swapChainDesc = new SwapChainDescription1
|
||||
{
|
||||
Width = _swapChainPresenter.Width,
|
||||
Height = _swapChainPresenter.Height,
|
||||
Format = Format.B8G8R8A8_UNorm,
|
||||
Stereo = false,
|
||||
SampleDescription = new SampleDescription(1, 0),
|
||||
BufferUsage = Usage.Backbuffer | Usage.RenderTargetOutput,
|
||||
BufferCount = GraphicsPipeline.FRAME_COUNT,
|
||||
Scaling = Scaling.Stretch,
|
||||
SwapEffect = SwapEffect.FlipDiscard,
|
||||
AlphaMode = AlphaMode.Ignore,
|
||||
Flags = SwapChainFlags.AllowTearing
|
||||
};
|
||||
|
||||
switch (_swapChainPresenter.Type)
|
||||
{
|
||||
case SwapChainPresenter.TargetType.Composition:
|
||||
var swapChain1 = _graphicsDevice.DXGIFactory.CreateSwapChainForComposition(_graphicsDevice.CommandQueue, swapChainDesc);
|
||||
swapChain = swapChain1.QueryInterface<IDXGISwapChain4>();
|
||||
swapChain1.Dispose();
|
||||
|
||||
_backBufferIndex = swapChain.CurrentBackBufferIndex;
|
||||
_swapChainPresenter.SwapChainPanelNative!.SetSwapChain(swapChain);
|
||||
break;
|
||||
case SwapChainPresenter.TargetType.Hwnd:
|
||||
var swapChainFullscreenDesc = new SwapChainFullscreenDescription
|
||||
{
|
||||
Windowed = true,
|
||||
};
|
||||
|
||||
var swapChain2 = _graphicsDevice.DXGIFactory.CreateSwapChainForHwnd(
|
||||
_graphicsDevice.CommandQueue,
|
||||
_swapChainPresenter.Hwnd,
|
||||
swapChainDesc,
|
||||
swapChainFullscreenDesc,
|
||||
null);
|
||||
swapChain = swapChain2.QueryInterface<IDXGISwapChain4>();
|
||||
swapChain2.Dispose();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unsupported swap chain surface type.");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeCommandObjects(out ID3D12CommandAllocator[] commandAllocator, out ID3D12GraphicsCommandList10 commandList, out ID3D12Fence1 fence)
|
||||
{
|
||||
commandAllocator = new ID3D12CommandAllocator[GraphicsPipeline.FRAME_COUNT];
|
||||
for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++)
|
||||
{
|
||||
commandAllocator[i] = _graphicsDevice.Device.CreateCommandAllocator(CommandListType.Direct);
|
||||
}
|
||||
|
||||
commandList = _graphicsDevice.Device.CreateCommandList<ID3D12GraphicsCommandList10>(CommandListType.Direct, commandAllocator[0], null!);
|
||||
commandList.Close();
|
||||
fence = _graphicsDevice.Device.CreateFence<ID3D12Fence1>(_fenceValues[_backBufferIndex], FenceFlags.None);
|
||||
|
||||
_fenceValues[_backBufferIndex]++;
|
||||
}
|
||||
|
||||
private void CreateRenderTargets()
|
||||
{
|
||||
for (var i = 0u; i < GraphicsPipeline.FRAME_COUNT; i++)
|
||||
{
|
||||
_renderTargets[i] = _swapChain.GetBuffer<ID3D12Resource>(i);
|
||||
_renderTargets[i].Name = $"RenderTarget_{i}";
|
||||
_renderTargetDescriptorIndexes[i] = _rtvHeap.AllocateDescriptor();
|
||||
|
||||
var rtvHandle = _rtvHeap.GetCpuHandle(_renderTargetDescriptorIndexes[i]);
|
||||
_graphicsDevice.Device.CreateRenderTargetView(_renderTargets[i], null, rtvHandle);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
uint newHeight;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
newWidth = _pendingWidth;
|
||||
newHeight = _pendingHeight;
|
||||
_resizeRequested = false;
|
||||
}
|
||||
|
||||
WaitIdle();
|
||||
|
||||
for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++)
|
||||
{
|
||||
if (_renderTargets[i] is not null)
|
||||
{
|
||||
_renderTargets[i].Dispose();
|
||||
_rtvHeap.ReleaseDescriptor(_renderTargetDescriptorIndexes[i]);
|
||||
}
|
||||
|
||||
_fenceValues[i] = _fenceValues[_backBufferIndex];
|
||||
}
|
||||
|
||||
_swapChain.ResizeBuffers(GraphicsPipeline.FRAME_COUNT, newWidth, newHeight, Format.B8G8R8A8_UNorm, SwapChainFlags.AllowTearing).CheckError();
|
||||
|
||||
CreateRenderTargets();
|
||||
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
|
||||
}
|
||||
|
||||
public ICommandBuffer BeginRender()
|
||||
{
|
||||
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
|
||||
|
||||
var commandAllocator = _commandAllocators[_backBufferIndex];
|
||||
commandAllocator.Reset();
|
||||
_commandList.Reset(commandAllocator, null);
|
||||
|
||||
_commandList.ResourceBarrierTransition(_renderTargets[_backBufferIndex], ResourceStates.Present, ResourceStates.RenderTarget);
|
||||
|
||||
return _commandBuffer;
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
}
|
||||
|
||||
public void EndRender()
|
||||
{
|
||||
_commandList.ResourceBarrierTransition(_renderTargets[_backBufferIndex], ResourceStates.RenderTarget, ResourceStates.Present);
|
||||
_commandList.Close();
|
||||
|
||||
_graphicsDevice.CommandQueue.ExecuteCommandLists(new[] { _commandList });
|
||||
|
||||
_swapChain.Present(1, PresentFlags.None).CheckError();
|
||||
|
||||
WaitNextFrame();
|
||||
}
|
||||
|
||||
public void WaitNextFrame()
|
||||
{
|
||||
var fenceValue = _fenceValues[_backBufferIndex];
|
||||
|
||||
if (_graphicsDevice.CommandQueue.Signal(_fence, fenceValue).Failure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_fence.CompletedValue < _fenceValues[_backBufferIndex]
|
||||
&& _fence.SetEventOnCompletion(_fenceValues[_backBufferIndex], _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
|
||||
{
|
||||
_fenceEvent.WaitOne();
|
||||
}
|
||||
|
||||
_fenceValues[_backBufferIndex]++;
|
||||
}
|
||||
|
||||
public void WaitIdle()
|
||||
{
|
||||
var fenceValue = _fenceValues[_backBufferIndex];
|
||||
if (_graphicsDevice.CommandQueue.Signal(_fence, fenceValue).Success
|
||||
&& _fence.SetEventOnCompletion(fenceValue, _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
|
||||
{
|
||||
_fenceEvent.WaitOne();
|
||||
_fenceValues[_backBufferIndex]++;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WaitIdle();
|
||||
|
||||
_swapChainPresenter.SwapChainPanelNative?.SetSwapChain(null);
|
||||
|
||||
foreach (var commandAllocator in _commandAllocators)
|
||||
{
|
||||
commandAllocator.Dispose();
|
||||
}
|
||||
_commandAllocators.AsSpan().Clear();
|
||||
|
||||
foreach (var renderTarget in _renderTargets)
|
||||
{
|
||||
renderTarget.Dispose();
|
||||
}
|
||||
_renderTargets.AsSpan().Clear();
|
||||
|
||||
_swapChain.Dispose();
|
||||
_commandList.Dispose();
|
||||
|
||||
_fence.Dispose();
|
||||
_fenceEvent.Dispose();
|
||||
|
||||
_rtvHeap.Dispose();
|
||||
|
||||
_backBufferIndex = 0;
|
||||
_fenceValues.AsSpan().Clear();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
324
Ghost.Graphics/DX12/DX12Renderer.cs
Normal file
324
Ghost.Graphics/DX12/DX12Renderer.cs
Normal file
@@ -0,0 +1,324 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.DX12.Utilities;
|
||||
using System.Collections.Immutable;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
using static Win32.Apis;
|
||||
|
||||
namespace Ghost.Graphics.DX12;
|
||||
|
||||
internal unsafe class DX12Renderer : IRenderer
|
||||
{
|
||||
private class FrameResource : IDisposable
|
||||
{
|
||||
public readonly ID3D12CommandAllocator commandAllocator;
|
||||
public readonly ID3D12GraphicsCommandList10 commandList;
|
||||
public readonly ICommandBuffer commandBuffer;
|
||||
public ID3D12Resource backBuffer;
|
||||
public uint backBufferDescriptorIndexes;
|
||||
public ulong fenceValue;
|
||||
|
||||
public FrameResource(DX12Renderer renderer, uint index)
|
||||
{
|
||||
commandAllocator = renderer._graphicsDevice.NativeDevice.CreateCommandAllocator(CommandListType.Direct);
|
||||
commandList = renderer._graphicsDevice.NativeDevice.CreateCommandList<ID3D12GraphicsCommandList10>(CommandListType.Direct, commandAllocator);
|
||||
commandBuffer = new DX12CommandBuffer(commandList);
|
||||
|
||||
renderer.CreateBackBufferResource(index, out backBuffer, out backBufferDescriptorIndexes);
|
||||
|
||||
commandList.Close();
|
||||
}
|
||||
|
||||
public void ResetCommandList()
|
||||
{
|
||||
commandAllocator.Reset();
|
||||
commandList.Reset(commandAllocator, null);
|
||||
}
|
||||
|
||||
public void IncrementFenceValue()
|
||||
{
|
||||
fenceValue++;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
commandAllocator.Dispose();
|
||||
commandList.Dispose();
|
||||
backBuffer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private const int _RENDER_TARGET_VIEW_HEAP_SIZE = 1024;
|
||||
private const int _DEPTH_STENCIL_VIEW_HEAP_SIZE = 256;
|
||||
|
||||
private readonly DX12GraphicsDevice _graphicsDevice;
|
||||
private readonly SwapChainPresenter _swapChainPresenter;
|
||||
|
||||
private readonly ComPtr<IDXGISwapChain4> _swapChain;
|
||||
private readonly FrameResource[] _frameResources;
|
||||
private uint _backBufferIndex;
|
||||
|
||||
private readonly ComPtr<ID3D12Fence1> _fence;
|
||||
private readonly AutoResetEvent _fenceEvent;
|
||||
|
||||
private readonly D3D12DescriptorAllocator _rtvHeap;
|
||||
|
||||
private ImmutableArray<IRenderPass> _renderPasses;
|
||||
|
||||
private readonly Lock _lock = new();
|
||||
private uint _pendingWidth;
|
||||
private uint _pendingHeight;
|
||||
private bool _resizeRequested;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ReadOnlySpan<IRenderPass> RenderPasses => _renderPasses.AsSpan();
|
||||
|
||||
public DX12Renderer(DX12GraphicsDevice graphicsDevice, in SwapChainPresenter swapChainSurface)
|
||||
{
|
||||
_graphicsDevice = graphicsDevice;
|
||||
_swapChainPresenter = swapChainSurface;
|
||||
|
||||
_rtvHeap = new D3D12DescriptorAllocator(_graphicsDevice.NativeDevice, DescriptorHeapType.Rtv, _RENDER_TARGET_VIEW_HEAP_SIZE);
|
||||
_fenceEvent = new(false);
|
||||
_renderPasses = ImmutableArray<IRenderPass>.Empty;
|
||||
|
||||
InitializeSwapChain();
|
||||
InitializeCommandObjects(out _frameResources, out _fence);
|
||||
}
|
||||
|
||||
private void InitializeSwapChain()
|
||||
{
|
||||
var swapChainDesc = new SwapChainDescription1
|
||||
{
|
||||
Width = _swapChainPresenter.Width,
|
||||
Height = _swapChainPresenter.Height,
|
||||
Format = Format.B8G8R8A8Unorm,
|
||||
Stereo = false,
|
||||
SampleDesc = new(1, 0),
|
||||
BufferUsage = Usage.BackBuffer | Usage.RenderTargetOutput,
|
||||
BufferCount = GraphicsPipeline._FRAME_COUNT,
|
||||
Scaling = Scaling.Stretch,
|
||||
SwapEffect = SwapEffect.FlipDiscard,
|
||||
AlphaMode = AlphaMode.Ignore,
|
||||
Flags = SwapChainFlags.AllowTearing
|
||||
};
|
||||
|
||||
using ComPtr<IDXGISwapChain1> tempSwapChain = default;
|
||||
switch (_swapChainPresenter.Type)
|
||||
{
|
||||
case SwapChainPresenter.TargetType.Composition:
|
||||
{
|
||||
_graphicsDevice.DXGIFactory.Ptr->
|
||||
CreateSwapChainForComposition(
|
||||
(IUnknown*)_graphicsDevice.CommandQueue.Ptr,
|
||||
&swapChainDesc, null,
|
||||
(IDXGISwapChain1**)&tempSwapChain);
|
||||
|
||||
fixed (void* swapChainPtr = &_swapChain)
|
||||
{
|
||||
tempSwapChain.Get()->QueryInterface(__uuidof<IDXGISwapChain4>(), &swapChainPtr);
|
||||
}
|
||||
|
||||
_swapChainPresenter.SwapChainPanelNative.SetSwapChain((IntPtr)_swapChain.Get());
|
||||
break;
|
||||
}
|
||||
case SwapChainPresenter.TargetType.Hwnd:
|
||||
{
|
||||
var swapChainFullscreenDesc = new SwapChainFullscreenDescription
|
||||
{
|
||||
Windowed = true,
|
||||
};
|
||||
|
||||
_graphicsDevice.DXGIFactory.Ptr->
|
||||
CreateSwapChainForHwnd(
|
||||
(IUnknown*)_graphicsDevice.CommandQueue.Ptr,
|
||||
_swapChainPresenter.Hwnd,
|
||||
&swapChainDesc,
|
||||
&swapChainFullscreenDesc,
|
||||
null,
|
||||
(IDXGISwapChain1**)&tempSwapChain);
|
||||
|
||||
fixed (void* swapChainPtr = &_swapChain)
|
||||
{
|
||||
tempSwapChain.Get()->QueryInterface(__uuidof<IDXGISwapChain4>(), &swapChainPtr);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Unsupported swap chain surface type.");
|
||||
}
|
||||
|
||||
_backBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
}
|
||||
|
||||
private void InitializeCommandObjects(out FrameResource[] frameResources)
|
||||
{
|
||||
frameResources = new FrameResource[GraphicsPipeline._FRAME_COUNT];
|
||||
for (var i = 0u; i < GraphicsPipeline._FRAME_COUNT; i++)
|
||||
{
|
||||
frameResources[i] = new FrameResource(this, i);
|
||||
}
|
||||
|
||||
fixed (void* fencePtr = &_fence)
|
||||
{
|
||||
_graphicsDevice.NativeDevice.Ptr->CreateFence(0, FenceFlags.None, __uuidof<ID3D12Fence1>(), &fencePtr);
|
||||
}
|
||||
|
||||
frameResources[0].IncrementFenceValue();
|
||||
}
|
||||
|
||||
public void RequestResize(uint width, uint height)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_pendingWidth == width && _pendingHeight == height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_resizeRequested = true;
|
||||
_pendingWidth = width;
|
||||
_pendingHeight = height;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateBackBufferResource(uint i, out ID3D12Resource backBuffer, out uint index)
|
||||
{
|
||||
backBuffer = _swapChain.GetBuffer<ID3D12Resource>(i);
|
||||
backBuffer.Name = $"BackBuffer_{i}";
|
||||
index = _rtvHeap.AllocateDescriptor();
|
||||
var rtvHandle = _rtvHeap.GetCpuHandle(index);
|
||||
_graphicsDevice.NativeDevice.CreateRenderTargetView(backBuffer, null, rtvHandle);
|
||||
}
|
||||
|
||||
public void ExecutePendingResize()
|
||||
{
|
||||
if (!_resizeRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint newWidth;
|
||||
uint newHeight;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
newWidth = _pendingWidth;
|
||||
newHeight = _pendingHeight;
|
||||
_resizeRequested = false;
|
||||
}
|
||||
|
||||
WaitIdle();
|
||||
|
||||
for (var i = 0; i < GraphicsPipeline._FRAME_COUNT; i++)
|
||||
{
|
||||
var backBuffer = _frameResources[i].backBuffer;
|
||||
if (backBuffer is not null)
|
||||
{
|
||||
backBuffer.Dispose();
|
||||
_rtvHeap.ReleaseDescriptor(_frameResources[i].backBufferDescriptorIndexes);
|
||||
}
|
||||
|
||||
_frameResources[i].fenceValue = _frameResources[_backBufferIndex].fenceValue;
|
||||
}
|
||||
|
||||
_swapChain.ResizeBuffers(GraphicsPipeline._FRAME_COUNT, newWidth, newHeight, Format.B8G8R8A8_UNorm, SwapChainFlags.AllowTearing).CheckError();
|
||||
|
||||
for (var i = 0u; i < GraphicsPipeline._FRAME_COUNT; i++)
|
||||
{
|
||||
CreateBackBufferResource(i, out var backBuffer, out var index);
|
||||
|
||||
_frameResources[i].backBuffer = backBuffer;
|
||||
_frameResources[i].backBufferDescriptorIndexes = index;
|
||||
}
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
|
||||
|
||||
var frameResource = _frameResources[_backBufferIndex];
|
||||
frameResource.ResetCommandList();
|
||||
frameResource.commandList.ResourceBarrierTransition(_frameResources[_backBufferIndex].backBuffer!, ResourceStates.Present, ResourceStates.RenderTarget);
|
||||
|
||||
foreach (var pass in _renderPasses)
|
||||
{
|
||||
pass.Execute(frameResource.commandBuffer);
|
||||
}
|
||||
|
||||
frameResource.commandList.ResourceBarrierTransition(_frameResources[_backBufferIndex].backBuffer!, ResourceStates.RenderTarget, ResourceStates.Present);
|
||||
frameResource.commandList.Close();
|
||||
|
||||
_graphicsDevice.CommandQueue.ExecuteCommandList(frameResource.commandList);
|
||||
|
||||
_swapChain.Present(1, PresentFlags.None).CheckError();
|
||||
|
||||
WaitNextFrame();
|
||||
}
|
||||
|
||||
public void WaitNextFrame()
|
||||
{
|
||||
var resource = _frameResources[_backBufferIndex];
|
||||
if (_graphicsDevice.CommandQueue.Signal(_fence, resource.fenceValue).Failure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_fence.CompletedValue < resource.fenceValue
|
||||
&& _fence.SetEventOnCompletion(resource.fenceValue, _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
|
||||
{
|
||||
_fenceEvent.WaitOne();
|
||||
}
|
||||
|
||||
resource.IncrementFenceValue();
|
||||
}
|
||||
|
||||
public void WaitIdle()
|
||||
{
|
||||
var resource = _frameResources[_backBufferIndex];
|
||||
if (_graphicsDevice.CommandQueue.Signal(_fence, resource.fenceValue).Success
|
||||
&& _fence.SetEventOnCompletion(resource.fenceValue, _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
|
||||
{
|
||||
_fenceEvent.WaitOne();
|
||||
resource.IncrementFenceValue();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WaitIdle();
|
||||
|
||||
_swapChainPresenter.SwapChainPanelNative?.SetSwapChain(null);
|
||||
|
||||
foreach (var pass in _renderPasses)
|
||||
{
|
||||
pass.Dispose();
|
||||
}
|
||||
|
||||
foreach (var frameResource in _frameResources)
|
||||
{
|
||||
frameResource.Dispose();
|
||||
}
|
||||
|
||||
_swapChain.Dispose();
|
||||
|
||||
_fence.Dispose();
|
||||
_fenceEvent.Dispose();
|
||||
|
||||
_rtvHeap.Dispose();
|
||||
|
||||
_backBufferIndex = 0;
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
37
Ghost.Graphics/DX12/DX12Resource.cs
Normal file
37
Ghost.Graphics/DX12/DX12Resource.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Vortice.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.DX12;
|
||||
|
||||
public unsafe class DX12Resource : IResource
|
||||
{
|
||||
private readonly ID3D12Resource _nativeResource;
|
||||
|
||||
internal ID3D12Resource NativeResource => _nativeResource;
|
||||
|
||||
public ulong GPUAddress => _nativeResource.GPUVirtualAddress;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _nativeResource.Name;
|
||||
set => _nativeResource.Name = value;
|
||||
}
|
||||
|
||||
public DX12Resource(ID3D12Resource nativeResource)
|
||||
{
|
||||
_nativeResource = nativeResource;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetData<T>(Span<T> data)
|
||||
where T : unmanaged
|
||||
{
|
||||
_nativeResource.WriteToSubresource(0, data, 0, 0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_nativeResource.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,20 @@
|
||||
using Ghost.Graphics.Utilities;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.DX12.Utilities;
|
||||
using Ghost.Graphics.Utilities;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Vortice.Direct3D12;
|
||||
using Vortice.DXGI;
|
||||
|
||||
namespace Ghost.Graphics.DX12.Utilities;
|
||||
namespace Ghost.Graphics.DX12;
|
||||
|
||||
internal unsafe class D3D12ResourceUtils
|
||||
internal unsafe class DX12ResourceAllocator : IResourceAllocator
|
||||
{
|
||||
private const ResourceStates _INITIALCOPYTARGETSTATE = ResourceStates.Common;
|
||||
private const ResourceStates _INITIALREADTARGETSTATE = ResourceStates.Common;
|
||||
private const ResourceStates _INITIALUAVTARGETSTATE = ResourceStates.Common;
|
||||
|
||||
private const uint _MAX_BYTES = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||
|
||||
public static ID3D12Resource CreateStaticBuffer<T>(
|
||||
ID3D12Device device,
|
||||
D3D12ResourceUploadBatch resourceUpload,
|
||||
@@ -31,10 +35,7 @@ internal unsafe class D3D12ResourceUtils
|
||||
where T : unmanaged
|
||||
{
|
||||
var sizeInBytes = (uint)(sizeof(T) * data.Length);
|
||||
|
||||
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||
|
||||
if (sizeInBytes > c_maxBytes)
|
||||
if (sizeInBytes > _MAX_BYTES)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
}
|
||||
@@ -92,9 +93,7 @@ internal unsafe class D3D12ResourceUtils
|
||||
void* data = default,
|
||||
ResourceFlags flags = ResourceFlags.None)
|
||||
{
|
||||
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||
|
||||
if (sizeInBytes > c_maxBytes)
|
||||
if (sizeInBytes > _MAX_BYTES)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
}
|
||||
@@ -122,8 +121,7 @@ internal unsafe class D3D12ResourceUtils
|
||||
uint sizeInBytes,
|
||||
ResourceFlags flags = ResourceFlags.None)
|
||||
{
|
||||
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||
if (sizeInBytes > c_maxBytes)
|
||||
if (sizeInBytes > _MAX_BYTES)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
}
|
||||
@@ -136,33 +134,11 @@ internal unsafe class D3D12ResourceUtils
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static ID3D12Resource CreateCPUDestinationBuffer(
|
||||
ID3D12Device device,
|
||||
uint sizeInBytes,
|
||||
ResourceFlags flags = ResourceFlags.None)
|
||||
{
|
||||
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||
if (sizeInBytes > c_maxBytes)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
}
|
||||
|
||||
var buffer = device.CreateCommittedResource(
|
||||
HeapType.Default,
|
||||
HeapFlags.None,
|
||||
ResourceDescription.Buffer(sizeInBytes, flags),
|
||||
ResourceStates.CopyDest
|
||||
);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static ID3D12Resource CreateUAVBuffer(ID3D12Device device, uint bufferSize,
|
||||
ResourceStates initialState = ResourceStates.Common,
|
||||
ResourceFlags flags = ResourceFlags.None)
|
||||
{
|
||||
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||
|
||||
if (bufferSize > c_maxBytes)
|
||||
if (bufferSize > _MAX_BYTES)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {bufferSize})");
|
||||
}
|
||||
@@ -187,7 +163,7 @@ internal unsafe class D3D12ResourceUtils
|
||||
ResourceFlags flags = ResourceFlags.None)
|
||||
where T : unmanaged
|
||||
{
|
||||
if ((width > D3D12.RequestTexture2DUOrVDimension) || (height > D3D12.RequestTexture2DUOrVDimension))
|
||||
if (width > D3D12.RequestTexture2DUOrVDimension || height > D3D12.RequestTexture2DUOrVDimension)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource dimensions too large for DirectX 12 (2D: size {width} by {height})");
|
||||
}
|
||||
@@ -231,4 +207,46 @@ internal unsafe class D3D12ResourceUtils
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
public static IResourceAllocator Create() => new DX12ResourceAllocator();
|
||||
|
||||
public IResource CreateUploadBuffer(uint sizeInBytes, ResourceFlags flags = ResourceFlags.None)
|
||||
{
|
||||
if (sizeInBytes > _MAX_BYTES)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
}
|
||||
|
||||
var device = GraphicsPipeline.GetRenderer<DX12GraphicsDevice>();
|
||||
var buffer = device.NativeDevice.CreateCommittedResource(
|
||||
HeapType.Upload,
|
||||
HeapFlags.None,
|
||||
ResourceDescription.Buffer(sizeInBytes, flags),
|
||||
ResourceStates.GenericRead
|
||||
);
|
||||
|
||||
return new DX12Resource(buffer);
|
||||
}
|
||||
|
||||
public IResource CreateCopyDestinationBuffer(uint sizeInBytes, ResourceFlags flags = ResourceFlags.None)
|
||||
{
|
||||
if (sizeInBytes > _MAX_BYTES)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
}
|
||||
|
||||
var device = GraphicsPipeline.GetRenderer<DX12GraphicsDevice>();
|
||||
var buffer = device.NativeDevice.CreateCommittedResource(
|
||||
HeapType.Default,
|
||||
HeapFlags.None,
|
||||
ResourceDescription.Buffer(sizeInBytes, flags),
|
||||
ResourceStates.CopyDest
|
||||
);
|
||||
|
||||
return new DX12Resource(buffer);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
5
Ghost.Graphics/DX12/Utilities/D3D12Utility.cs
Normal file
5
Ghost.Graphics/DX12/Utilities/D3D12Utility.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Ghost.Graphics.DX12.Utilities;
|
||||
|
||||
internal static class D3D12Utility
|
||||
{\
|
||||
}
|
||||
Reference in New Issue
Block a user