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:
2025-06-30 13:50:06 +09:00
parent 8fd1222780
commit 300ae7251b
27 changed files with 765 additions and 486 deletions

View File

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

View File

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

View File

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

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

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

View File

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

View File

@@ -0,0 +1,5 @@
namespace Ghost.Graphics.DX12.Utilities;
internal static class D3D12Utility
{\
}