Enhance graphics engine and code organization
Added `InternalsVisibleTo` attribute for "Ghost.Graphics" and "Ghost.Editor" in `AssemblyInfo.cs`. Added a new `EngineAssemblyAttribute` in `EngineAssemblyAttribute.cs`. Added a reference to `Misaki.HighPerformance.Unsafe` in `Ghost.Core.csproj`. Added a new `Bounds` struct to represent axis-aligned bounding boxes in `Bounds.cs`. Added new `Color32` and `Color128` structs for color representation in `Color.cs`. Changed the namespace from `Ghost.Editor.Controls` to `Ghost.Editor.Core.Controls` in multiple files. Changed the implicit conversion operator in `ConstPtr<T>` to use a more descriptive parameter name in `ConstPtr.cs`. Changed the `Mesh` class to use `Color128` instead of `Color32` for color representation. Enhanced the `TypeCache` class to load types from assemblies marked with `EngineAssemblyAttribute`. Enhanced the `ProjectService` class to improve the `GetAllProjectAsync` method by filtering out bad projects. Enhanced the `GraphicsPipeline` class to support both DX12 and D3D12 graphics APIs. Enhanced the `Shader` class to include methods for compiling HLSL shaders and managing root signatures. Enhanced the `MeshRenderPass` class to utilize the new shader compilation methods. Refactored the `AppStateMachine` class to use private fields instead of static fields for state management. Refactored the `ComponentDataView` class to use the new namespace and improve organization. Refactored project references in `Ghost.Graphics.csproj` to include new dependencies and remove outdated ones. Made various adjustments to ensure consistency and improve code quality across multiple files.
This commit is contained in:
42
Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
Normal file
42
Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Data;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
{
|
||||
private readonly ConstPtr<ID3D12GraphicsCommandList10> _commandList;
|
||||
|
||||
internal ConstPtr<ID3D12GraphicsCommandList10> CommandList => _commandList;
|
||||
|
||||
public D3D12CommandBuffer(ID3D12GraphicsCommandList10* commandList)
|
||||
{
|
||||
_commandList = commandList;
|
||||
}
|
||||
|
||||
public void DrawMesh(Mesh mesh)
|
||||
{
|
||||
_commandList.Ptr->IASetPrimitiveTopology(PrimitiveTopology.TriangleList);
|
||||
_commandList.Ptr->IASetVertexBuffers(0, 1, mesh.VertexBufferView);
|
||||
_commandList.Ptr->IASetIndexBuffer(mesh.IndexBufferView);
|
||||
|
||||
_commandList.Ptr->DrawIndexedInstanced(mesh.IndexCount, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
public void CopyResource(IResource dstResource, uint dstOffset, IResource srcResource, uint srcOffset, uint size)
|
||||
{
|
||||
var dstDXResource = (D3D12Resource)dstResource;
|
||||
var srcDXResource = (D3D12Resource)srcResource;
|
||||
|
||||
_commandList.Ptr->CopyBufferRegion(dstDXResource.NativeResource, dstOffset, srcDXResource.NativeResource, srcOffset, size);
|
||||
}
|
||||
|
||||
public void BarrierTransition(IResource resource, ResourceStates beforeState, ResourceStates afterState)
|
||||
{
|
||||
var dxResource = (D3D12Resource)resource;
|
||||
_commandList.Ptr->ResourceBarrierTransition(dxResource.NativeResource.Ptr, beforeState, afterState);
|
||||
}
|
||||
}
|
||||
35
Ghost.Graphics/D3D12/D3D12DebugLayer.cs
Normal file
35
Ghost.Graphics/D3D12/D3D12DebugLayer.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12DebugLayer : IDebugLayer
|
||||
{
|
||||
private readonly ComPtr<ID3D12Debug6> _d3d12Debug;
|
||||
private readonly ComPtr<IDXGIDebug1> _dxgiDebug;
|
||||
private readonly ComPtr<IDXGIInfoQueue> _dxgiInfoQueue;
|
||||
|
||||
public D3D12DebugLayer()
|
||||
{
|
||||
D3D12GetDebugInterface(__uuidof<ID3D12Debug6>(), _d3d12Debug.GetVoidAddressOf());
|
||||
_d3d12Debug.Get()->EnableDebugLayer();
|
||||
|
||||
DXGIGetDebugInterface1(0u, __uuidof<IDXGIDebug1>(), _dxgiDebug.GetVoidAddressOf());
|
||||
_dxgiDebug.Get()->EnableLeakTrackingForThread();
|
||||
|
||||
DXGIGetDebugInterface1(0u, __uuidof<IDXGIInfoQueue>(), _dxgiInfoQueue.GetVoidAddressOf());
|
||||
_dxgiInfoQueue.Get()->SetBreakOnSeverity(DXGI_DEBUG_ALL, InfoQueueMessageSeverity.Error, true);
|
||||
_dxgiInfoQueue.Get()->SetBreakOnSeverity(DXGI_DEBUG_ALL, InfoQueueMessageSeverity.Corruption, true);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_dxgiDebug.Get()->ReportLiveObjects(DXGI_DEBUG_ALL, ReportLiveObjectFlags.Detail | ReportLiveObjectFlags.IgnoreInternal);
|
||||
|
||||
_d3d12Debug.Dispose();
|
||||
_dxgiDebug.Dispose();
|
||||
_dxgiInfoQueue.Dispose();
|
||||
}
|
||||
}
|
||||
165
Ghost.Graphics/D3D12/D3D12GraphicsDevice.cs
Normal file
165
Ghost.Graphics/D3D12/D3D12GraphicsDevice.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Data;
|
||||
using System.Collections.Immutable;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12GraphicsDevice : IGraphicsDevice
|
||||
{
|
||||
#if DEBUG
|
||||
private readonly D3D12DebugLayer _debugLayer;
|
||||
#endif
|
||||
private ComPtr<IDXGIFactory7> _dxgiFactory;
|
||||
private ComPtr<ID3D12Device14> _device;
|
||||
private ComPtr<ID3D12CommandQueue> _commandQueue;
|
||||
|
||||
private ImmutableArray<IRenderer> _initializeQueue;
|
||||
private ImmutableArray<IRenderer> _renderers;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public static GraphicsAPI TargetAPI => GraphicsAPI.D3D12;
|
||||
public ReadOnlySpan<IRenderer> InitializeQueue => _initializeQueue.AsSpan();
|
||||
public ReadOnlySpan<IRenderer> Renderers => _renderers.AsSpan();
|
||||
|
||||
public ConstPtr<ID3D12Device14> NativeDevice => new(_device.Get());
|
||||
public ConstPtr<IDXGIFactory7> DXGIFactory => new(_dxgiFactory.Get());
|
||||
public ConstPtr<ID3D12CommandQueue> CommandQueue => new(_commandQueue.Get());
|
||||
|
||||
public D3D12GraphicsDevice()
|
||||
{
|
||||
#if DEBUG
|
||||
_debugLayer = new D3D12DebugLayer();
|
||||
#endif
|
||||
|
||||
InitializeDevice();
|
||||
InitializeCommandQueue();
|
||||
|
||||
_initializeQueue = ImmutableArray<IRenderer>.Empty;
|
||||
_renderers = ImmutableArray<IRenderer>.Empty;
|
||||
}
|
||||
|
||||
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_11_0, __uuidof<ID3D12Device14>(), _device.GetVoidAddressOf()).Success)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_device.Get() == null)
|
||||
{
|
||||
throw new PlatformNotSupportedException("Cannot create ID3D12Device");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeCommandQueue()
|
||||
{
|
||||
var queueDesc = new CommandQueueDescription
|
||||
{
|
||||
Type = CommandListType.Direct,
|
||||
Priority = (int)CommandQueuePriority.High,
|
||||
Flags = CommandQueueFlags.None,
|
||||
};
|
||||
|
||||
fixed (void* queuePtr = &_commandQueue)
|
||||
{
|
||||
_device.Get()->CreateCommandQueue(&queueDesc, __uuidof<ID3D12CommandQueue>(), (void**)queuePtr);
|
||||
}
|
||||
}
|
||||
|
||||
public IRenderer CreateRenderer(in SwapChainPresenter presenter)
|
||||
{
|
||||
var renderView = new D3D12Renderer(this, in presenter);
|
||||
ImmutableInterlocked.Update(ref _initializeQueue, old => old.Add(renderView));
|
||||
|
||||
return renderView;
|
||||
}
|
||||
|
||||
public void RemoveRenderer(IRenderer renderer)
|
||||
{
|
||||
if (renderer is D3D12Renderer 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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
renderer.Dispose();
|
||||
}
|
||||
|
||||
foreach (var renderView in _renderers)
|
||||
{
|
||||
renderView.Dispose();
|
||||
}
|
||||
|
||||
_commandQueue.Dispose();
|
||||
_device.Dispose();
|
||||
_dxgiFactory.Dispose();
|
||||
|
||||
#if DEBUG
|
||||
_debugLayer.Dispose();
|
||||
#endif
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
359
Ghost.Graphics/D3D12/D3D12Renderer.cs
Normal file
359
Ghost.Graphics/D3D12/D3D12Renderer.cs
Normal file
@@ -0,0 +1,359 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RenderPasses;
|
||||
using System.Collections.Immutable;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
using Win32.Numerics;
|
||||
|
||||
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.
|
||||
internal unsafe class D3D12Renderer : IRenderer
|
||||
{
|
||||
private struct FrameResource : IDisposable
|
||||
{
|
||||
public ComPtr<ID3D12CommandAllocator> commandAllocator;
|
||||
public ComPtr<ID3D12GraphicsCommandList10> commandList;
|
||||
public ComPtr<ID3D12Resource> backBuffer;
|
||||
|
||||
public ICommandBuffer commandBuffer;
|
||||
public uint backBufferDescriptorIndexes;
|
||||
public ulong fenceValue;
|
||||
|
||||
public FrameResource(D3D12Renderer renderer, uint index)
|
||||
{
|
||||
renderer._graphicsDevice.NativeDevice.Ptr->CreateCommandAllocator(CommandListType.Direct, __uuidof<ID3D12CommandAllocator>(), commandAllocator.GetVoidAddressOf());
|
||||
renderer._graphicsDevice.NativeDevice.Ptr->CreateCommandList(0u, CommandListType.Direct, commandAllocator.Get(), null, __uuidof<ID3D12GraphicsCommandList10>(), commandList.GetVoidAddressOf());
|
||||
|
||||
commandBuffer = new D3D12CommandBuffer(commandList.Get());
|
||||
backBufferDescriptorIndexes = renderer.CreateBackBufferResource(index, backBuffer.GetAddressOf());
|
||||
}
|
||||
|
||||
public readonly void ResetCommandBuffer()
|
||||
{
|
||||
commandAllocator.Get()->Reset();
|
||||
commandList.Get()->Reset(commandAllocator.Get(), null);
|
||||
}
|
||||
|
||||
public readonly void ExecuteCommandBuffer(ID3D12CommandQueue* queue)
|
||||
{
|
||||
commandList.Get()->Close();
|
||||
var commandListPtr = (ID3D12CommandList*)commandList.Get();
|
||||
queue->ExecuteCommandLists(1, &commandListPtr);
|
||||
}
|
||||
|
||||
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 D3D12GraphicsDevice _graphicsDevice;
|
||||
private readonly SwapChainPresenter _swapChainPresenter;
|
||||
|
||||
private ComPtr<IDXGISwapChain4> _swapChain = default;
|
||||
private ComPtr<ID3D12Fence1> _fence = default;
|
||||
private uint _backBufferIndex;
|
||||
|
||||
private readonly FrameResource[] _frameResources;
|
||||
private readonly AutoResetEvent _fenceEvent;
|
||||
|
||||
private D3D12DescriptorAllocator _rtvHeap;
|
||||
|
||||
private ImmutableArray<IRenderPass> _renderPasses;
|
||||
|
||||
private readonly Lock _lock = new();
|
||||
private uint _viewPortWidth;
|
||||
private uint _viewPortHeight;
|
||||
private uint _pendingWidth;
|
||||
private uint _pendingHeight;
|
||||
private bool _resizeRequested;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ReadOnlySpan<IRenderPass> RenderPasses => _renderPasses.AsSpan();
|
||||
|
||||
public D3D12Renderer(D3D12GraphicsDevice graphicsDevice, in SwapChainPresenter swapChainSurface)
|
||||
{
|
||||
_graphicsDevice = graphicsDevice;
|
||||
_swapChainPresenter = swapChainSurface;
|
||||
_viewPortWidth = swapChainSurface.Width;
|
||||
_viewPortHeight = swapChainSurface.Height;
|
||||
|
||||
_rtvHeap = new(_graphicsDevice.NativeDevice, DescriptorHeapType.Rtv, _RENDER_TARGET_VIEW_HEAP_SIZE);
|
||||
_fenceEvent = new(false);
|
||||
_renderPasses = [new MeshRenderPass()];
|
||||
|
||||
InitializeSwapChain();
|
||||
InitializeFrameResource(out _frameResources);
|
||||
}
|
||||
|
||||
private void InitializeSwapChain()
|
||||
{
|
||||
var swapChainDesc = new SwapChainDescription1
|
||||
{
|
||||
Width = _swapChainPresenter.Width,
|
||||
Height = _swapChainPresenter.Height,
|
||||
Format = D3D12PipelineResource.SWAP_CHAIN_BACK_BUFFER_FORMAT,
|
||||
SampleDesc = 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,
|
||||
Stereo = false,
|
||||
};
|
||||
|
||||
using ComPtr<IDXGISwapChain1> tempSwapChain = default;
|
||||
switch (_swapChainPresenter.Type)
|
||||
{
|
||||
case SwapChainPresenter.TargetType.Composition:
|
||||
_graphicsDevice.DXGIFactory.Ptr->CreateSwapChainForComposition((IUnknown*)_graphicsDevice.CommandQueue.Ptr, &swapChainDesc, null, tempSwapChain.GetAddressOf());
|
||||
break;
|
||||
case SwapChainPresenter.TargetType.Hwnd:
|
||||
var swapChainFullscreenDesc = new SwapChainFullscreenDescription
|
||||
{
|
||||
Windowed = false,
|
||||
};
|
||||
|
||||
_graphicsDevice.DXGIFactory.Ptr->CreateSwapChainForHwnd(
|
||||
(IUnknown*)_graphicsDevice.CommandQueue.Ptr,
|
||||
_swapChainPresenter.Hwnd,
|
||||
&swapChainDesc,
|
||||
&swapChainFullscreenDesc,
|
||||
null,
|
||||
tempSwapChain.GetAddressOf());
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unsupported swap chain surface type.");
|
||||
}
|
||||
|
||||
if (tempSwapChain.Get()->QueryInterface(__uuidof<IDXGISwapChain4>(), _swapChain.GetVoidAddressOf()).Failure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to create IDXGISwapChain4 interface.");
|
||||
}
|
||||
|
||||
_swapChainPresenter.SwapChainPanelNative.SetSwapChain((IntPtr)_swapChain.Get());
|
||||
_backBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
}
|
||||
|
||||
private void InitializeFrameResource(out FrameResource[] frameResources)
|
||||
{
|
||||
frameResources = new FrameResource[GraphicsPipeline._FRAME_COUNT];
|
||||
for (var i = 0u; i < GraphicsPipeline._FRAME_COUNT; i++)
|
||||
{
|
||||
frameResources[i] = new FrameResource(this, i);
|
||||
}
|
||||
|
||||
for (var i = 1u; i < GraphicsPipeline._FRAME_COUNT; i++)
|
||||
{
|
||||
ref var frameResource = ref frameResources[i];
|
||||
frameResource.commandList.Get()->Close();
|
||||
}
|
||||
|
||||
_graphicsDevice.NativeDevice.Ptr->CreateFence(0, FenceFlags.None, __uuidof<ID3D12Fence1>(), _fence.GetVoidAddressOf());
|
||||
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 uint CreateBackBufferResource(uint i, ID3D12Resource** backBuffer)
|
||||
{
|
||||
_swapChain.Get()->GetBuffer(i, __uuidof<ID3D12Resource>(), (void**)backBuffer);
|
||||
(*backBuffer)->SetName($"BackBuffer_{i}");
|
||||
var index = _rtvHeap.AllocateDescriptor();
|
||||
var rtvHandle = _rtvHeap.GetCpuHandle(index);
|
||||
_graphicsDevice.NativeDevice.Ptr->CreateRenderTargetView(*backBuffer, null, rtvHandle);
|
||||
return index;
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
ref var frameResource = ref _frameResources[i];
|
||||
if (frameResource.backBuffer.Get() is not null)
|
||||
{
|
||||
frameResource.backBuffer.Reset();
|
||||
_rtvHeap.ReleaseDescriptor(frameResource.backBufferDescriptorIndexes);
|
||||
}
|
||||
|
||||
frameResource.fenceValue = _frameResources[_backBufferIndex].fenceValue;
|
||||
}
|
||||
|
||||
if (_swapChain.Get()->ResizeBuffers(GraphicsPipeline._FRAME_COUNT, newWidth, newHeight, Format.B8G8R8A8Unorm, SwapChainFlags.AllowTearing).Failure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to resize swap chain buffers.");
|
||||
}
|
||||
|
||||
for (var i = 0u; i < GraphicsPipeline._FRAME_COUNT; i++)
|
||||
{
|
||||
var index = CreateBackBufferResource(i, _frameResources[i].backBuffer.GetAddressOf());
|
||||
_frameResources[i].backBufferDescriptorIndexes = index;
|
||||
}
|
||||
|
||||
_backBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
_viewPortWidth = newWidth;
|
||||
_viewPortHeight = newHeight;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
ref var frameResource = ref _frameResources[_backBufferIndex];
|
||||
|
||||
foreach (var pass in _renderPasses)
|
||||
{
|
||||
pass.Initialize(frameResource.commandBuffer);
|
||||
}
|
||||
|
||||
frameResource.ExecuteCommandBuffer(_graphicsDevice.CommandQueue);
|
||||
WaitIdle();
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
_backBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||
|
||||
ref var frameResource = ref _frameResources[_backBufferIndex];
|
||||
var cpuHandle = _rtvHeap.GetCpuHandle(frameResource.backBufferDescriptorIndexes);
|
||||
|
||||
frameResource.ResetCommandBuffer();
|
||||
frameResource.commandList.Get()->ResourceBarrierTransition(frameResource.backBuffer.Get(), ResourceStates.Present, ResourceStates.RenderTarget);
|
||||
|
||||
var clearColor = stackalloc float[4] { 1.0f, 0.0f, 1.0f, 1.0f };
|
||||
frameResource.commandList.Get()->ClearRenderTargetView(cpuHandle, clearColor, 0, null);
|
||||
|
||||
var viewPort = new Viewport(_viewPortWidth, _viewPortHeight);
|
||||
var rect = new Rect(0, 0, (int)_viewPortWidth, (int)_viewPortHeight);
|
||||
frameResource.commandList.Get()->RSSetViewports(1, &viewPort);
|
||||
frameResource.commandList.Get()->RSSetScissorRects(1, &rect);
|
||||
|
||||
frameResource.commandList.Get()->OMSetRenderTargets(1, &cpuHandle, false, null);
|
||||
|
||||
foreach (var pass in _renderPasses)
|
||||
{
|
||||
pass.Execute(frameResource.commandBuffer);
|
||||
}
|
||||
|
||||
frameResource.commandList.Get()->ResourceBarrierTransition(frameResource.backBuffer.Get(), ResourceStates.RenderTarget, ResourceStates.Present);
|
||||
|
||||
frameResource.ExecuteCommandBuffer(_graphicsDevice.CommandQueue.Ptr);
|
||||
if (_swapChain.Get()->Present(1, PresentFlags.None).Failure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to present swap chain.");
|
||||
}
|
||||
|
||||
WaitNextFrame();
|
||||
}
|
||||
|
||||
public void WaitNextFrame()
|
||||
{
|
||||
ref var resource = ref _frameResources[_backBufferIndex];
|
||||
if (_graphicsDevice.CommandQueue.Ptr->Signal((ID3D12Fence*)_fence.Get(), resource.fenceValue).Failure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var handle = new Handle((void*)_fenceEvent.SafeWaitHandle.DangerousGetHandle());
|
||||
if (_fence.Get()->GetCompletedValue() < resource.fenceValue
|
||||
&& _fence.Get()->SetEventOnCompletion(resource.fenceValue, handle).Success)
|
||||
{
|
||||
_fenceEvent.WaitOne();
|
||||
}
|
||||
|
||||
resource.IncrementFenceValue();
|
||||
}
|
||||
|
||||
public void WaitIdle()
|
||||
{
|
||||
ref var resource = ref _frameResources[_backBufferIndex];
|
||||
_graphicsDevice.CommandQueue.Ptr->Signal((ID3D12Fence*)_fence.Get(), resource.fenceValue);
|
||||
|
||||
var handle = new Handle((void*)_fenceEvent.SafeWaitHandle.DangerousGetHandle());
|
||||
if (_fence.Get()->SetEventOnCompletion(resource.fenceValue, handle).Success)
|
||||
{
|
||||
_fenceEvent.WaitOne();
|
||||
resource.IncrementFenceValue();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WaitIdle();
|
||||
|
||||
_swapChainPresenter.SwapChainPanelNative.SetSwapChain(IntPtr.Zero);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
74
Ghost.Graphics/D3D12/D3D12Resource.cs
Normal file
74
Ghost.Graphics/D3D12/D3D12Resource.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
public unsafe class D3D12Resource : IResource
|
||||
{
|
||||
private ComPtr<ID3D12Resource> _nativeResource
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
private string _name = string.Empty;
|
||||
|
||||
internal ConstPtr<ID3D12Resource> NativeResource => new(_nativeResource.Get());
|
||||
|
||||
public ulong GPUAddress => _nativeResource.Get()->GetGPUVirtualAddress();
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set
|
||||
{
|
||||
_name = value;
|
||||
_nativeResource.Get()->SetName(_name);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TempResource
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
internal D3D12Resource(ComPtr<ID3D12Resource> nativeResource, bool temp)
|
||||
{
|
||||
_nativeResource = nativeResource;
|
||||
TempResource = temp;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetData<T>(Span<T> data)
|
||||
where T : unmanaged
|
||||
{
|
||||
var size = (uint)(data.Length * sizeof(T));
|
||||
var range = new Win32.Graphics.Direct3D12.Range(0, size);
|
||||
|
||||
fixed (T* ptr = data)
|
||||
{
|
||||
var hr = _nativeResource.Get()->Map(0, &range, (void**)&ptr);
|
||||
if (hr.Failure)
|
||||
{
|
||||
var message = hr.ToString();
|
||||
throw new InvalidOperationException($"Failed to map resource: {message}");
|
||||
}
|
||||
_nativeResource.Get()->Unmap(0, &range);
|
||||
}
|
||||
}
|
||||
|
||||
internal void DisposeInternal()
|
||||
{
|
||||
var c = _nativeResource.Reset();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!TempResource)
|
||||
{
|
||||
DisposeInternal();
|
||||
}
|
||||
}
|
||||
}
|
||||
308
Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs
Normal file
308
Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs
Normal file
@@ -0,0 +1,308 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12ResourceAllocator : IResourceAllocator
|
||||
{
|
||||
private readonly struct TempResourceAllocInfo
|
||||
{
|
||||
public readonly D3D12Resource resource;
|
||||
public readonly uint cpuFenceValue;
|
||||
|
||||
public TempResourceAllocInfo(D3D12Resource resource, uint cpuFenceValue)
|
||||
{
|
||||
this.resource = resource;
|
||||
this.cpuFenceValue = cpuFenceValue;
|
||||
}
|
||||
|
||||
public TempResourceAllocInfo(D3D12Resource resource)
|
||||
: this(resource, GraphicsPipeline.CPUFenceValue + 1)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private const ResourceStates _INITIALCOPYTARGETSTATE = ResourceStates.Common;
|
||||
private const ResourceStates _INITIALREADTARGETSTATE = ResourceStates.Common;
|
||||
private const ResourceStates _INITIALUAVTARGETSTATE = ResourceStates.Common;
|
||||
|
||||
private const uint _MAX_BYTES = D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u;
|
||||
|
||||
private readonly Queue<TempResourceAllocInfo> _temResources = new();
|
||||
|
||||
//public static ID3D12Resource CreateStaticBuffer<T>(
|
||||
// ID3D12Device device,
|
||||
// D3D12ResourceUploadBatch resourceUpload,
|
||||
// T[] data, ResourceStates afterState,
|
||||
// ResourceFlags flags = ResourceFlags.None)
|
||||
// where T : unmanaged
|
||||
//{
|
||||
// Span<T> span = data;
|
||||
// return CreateStaticBuffer(device, resourceUpload, span, afterState, flags);
|
||||
//}
|
||||
|
||||
//public static ID3D12Resource CreateStaticBuffer<T>(
|
||||
// ID3D12Device device,
|
||||
// D3D12ResourceUploadBatch resourceUpload,
|
||||
// Span<T> data,
|
||||
// ResourceStates afterState,
|
||||
// ResourceFlags flags = ResourceFlags.None)
|
||||
// where T : unmanaged
|
||||
//{
|
||||
// var sizeInBytes = (uint)(sizeof(T) * data.Length);
|
||||
// if (sizeInBytes > _MAX_BYTES)
|
||||
// {
|
||||
// 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),
|
||||
// _INITIALCOPYTARGETSTATE
|
||||
// );
|
||||
|
||||
// fixed (T* dataPtr = data)
|
||||
// {
|
||||
// SubresourceData initData = new()
|
||||
// {
|
||||
// pData = dataPtr,
|
||||
// };
|
||||
|
||||
// resourceUpload.Upload(buffer, 0, &initData, 1);
|
||||
// resourceUpload.Transition(buffer, ResourceStates.CopyDest, afterState);
|
||||
|
||||
// return buffer;
|
||||
// }
|
||||
//}
|
||||
|
||||
//public static ID3D12Resource CreateUploadBuffer<T>(
|
||||
// ID3D12Device device,
|
||||
// T[] data,
|
||||
// ResourceFlags flags = ResourceFlags.None)
|
||||
// where T : unmanaged
|
||||
//{
|
||||
// var sizeInBytes = (uint)(sizeof(T) * data.Length);
|
||||
// fixed (T* dataPtr = data)
|
||||
// {
|
||||
// return CreateUploadBuffer(device, sizeInBytes, dataPtr, flags);
|
||||
// }
|
||||
//}
|
||||
|
||||
//public static ID3D12Resource CreateUploadBuffer<T>(
|
||||
// ID3D12Device device,
|
||||
// Span<T> data,
|
||||
// ResourceFlags flags = ResourceFlags.None)
|
||||
// where T : unmanaged
|
||||
//{
|
||||
// var sizeInBytes = (uint)(sizeof(T) * data.Length);
|
||||
// fixed (T* dataPtr = data)
|
||||
// {
|
||||
// return CreateUploadBuffer(device, sizeInBytes, dataPtr, flags);
|
||||
// }
|
||||
//}
|
||||
|
||||
//public static ID3D12Resource CreateUploadBuffer(
|
||||
// ID3D12Device device,
|
||||
// uint sizeInBytes,
|
||||
// void* data = default,
|
||||
// ResourceFlags flags = ResourceFlags.None)
|
||||
//{
|
||||
// if (sizeInBytes > _MAX_BYTES)
|
||||
// {
|
||||
// throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
// }
|
||||
|
||||
// var buffer = device.CreateCommittedResource(
|
||||
// HeapType.Upload,
|
||||
// HeapFlags.None,
|
||||
// ResourceDescription.Buffer(sizeInBytes, flags),
|
||||
// ResourceStates.GenericRead
|
||||
// );
|
||||
|
||||
// if (data is not null)
|
||||
// {
|
||||
// void* mappedPtr = default;
|
||||
// buffer.Map(0, null, &mappedPtr).CheckError();
|
||||
// Unsafe.CopyBlock(data, mappedPtr, sizeInBytes);
|
||||
// buffer.Unmap(0, null);
|
||||
// }
|
||||
|
||||
// return buffer;
|
||||
//}
|
||||
|
||||
//public static ID3D12Resource CreateReadbackBuffer(
|
||||
// ID3D12Device device,
|
||||
// uint sizeInBytes,
|
||||
// ResourceFlags flags = ResourceFlags.None)
|
||||
//{
|
||||
// if (sizeInBytes > _MAX_BYTES)
|
||||
// {
|
||||
// throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
// }
|
||||
// var buffer = device.CreateCommittedResource(
|
||||
// HeapType.Readback,
|
||||
// HeapFlags.None,
|
||||
// ResourceDescription.Buffer(sizeInBytes, flags),
|
||||
// _INITIALREADTARGETSTATE
|
||||
// );
|
||||
// return buffer;
|
||||
//}
|
||||
|
||||
//public static ID3D12Resource CreateUAVBuffer(ID3D12Device device, uint bufferSize,
|
||||
// ResourceStates initialState = ResourceStates.Common,
|
||||
// ResourceFlags flags = ResourceFlags.None)
|
||||
//{
|
||||
// if (bufferSize > _MAX_BYTES)
|
||||
// {
|
||||
// throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {bufferSize})");
|
||||
// }
|
||||
|
||||
// var buffer = device.CreateCommittedResource(
|
||||
// HeapType.Default,
|
||||
// HeapFlags.None,
|
||||
// ResourceDescription.Buffer(bufferSize, ResourceFlags.AllowUnorderedAccess | flags),
|
||||
// _INITIALCOPYTARGETSTATE
|
||||
// );
|
||||
|
||||
// return buffer;
|
||||
//}
|
||||
|
||||
//public static ID3D12Resource CreateTexture2D<T>(
|
||||
// ID3D12Device device,
|
||||
// D3D12ResourceUploadBatch resourceUpload,
|
||||
// uint width, uint height, Format format,
|
||||
// Span<T> data,
|
||||
// bool generateMips = false,
|
||||
// ResourceStates afterState = ResourceStates.PixelShaderResource,
|
||||
// ResourceFlags flags = ResourceFlags.None)
|
||||
// where T : unmanaged
|
||||
//{
|
||||
// if (width > D3D12.RequestTexture2DUOrVDimension || height > D3D12.RequestTexture2DUOrVDimension)
|
||||
// {
|
||||
// throw new InvalidOperationException($"ERROR: Resource dimensions too large for DirectX 12 (2D: size {width} by {height})");
|
||||
// }
|
||||
|
||||
// ushort mipLevels = 1;
|
||||
// if (generateMips)
|
||||
// {
|
||||
// generateMips = resourceUpload.IsSupportedForGenerateMips(format);
|
||||
// if (generateMips)
|
||||
// {
|
||||
// mipLevels = (ushort)TextureUtility.CountMips(width, height);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// var texture = device.CreateCommittedResource(
|
||||
// HeapType.Default,
|
||||
// HeapFlags.None,
|
||||
// ResourceDescription.Texture2D(format, width, height, 1, mipLevels, 1, 0, flags),
|
||||
// _INITIALCOPYTARGETSTATE
|
||||
// );
|
||||
|
||||
// fixed (T* dataPtr = data)
|
||||
// {
|
||||
// FormatHelper.GetSurfaceInfo(format, width, height, out var rowPitch, out var slicePitch);
|
||||
// SubresourceData initData = new()
|
||||
// {
|
||||
// pData = dataPtr,
|
||||
// RowPitch = (nint)rowPitch,
|
||||
// SlicePitch = (nint)slicePitch
|
||||
// };
|
||||
|
||||
// resourceUpload.Upload(texture, 0, &initData, 1);
|
||||
// resourceUpload.Transition(texture, ResourceStates.CopyDest, afterState);
|
||||
|
||||
// if (generateMips)
|
||||
// {
|
||||
// resourceUpload.GenerateMips(texture);
|
||||
// }
|
||||
|
||||
// return texture;
|
||||
// }
|
||||
//}
|
||||
|
||||
public IResource CreateUploadBuffer(uint sizeInBytes, bool tempResource = false, ResourceFlags flags = ResourceFlags.None)
|
||||
{
|
||||
if (sizeInBytes > _MAX_BYTES)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
}
|
||||
|
||||
var device = GraphicsPipeline.GetGraphicsDevice<D3D12GraphicsDevice>();
|
||||
var heapProperties = new HeapProperties(HeapType.Upload);
|
||||
var resourceDescription = ResourceDescription.Buffer(sizeInBytes, flags);
|
||||
|
||||
ComPtr<ID3D12Resource> buffer = default;
|
||||
device.NativeDevice.Ptr->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
HeapFlags.None,
|
||||
&resourceDescription,
|
||||
ResourceStates.Common,
|
||||
null,
|
||||
__uuidof<ID3D12Resource>(),
|
||||
buffer.GetVoidAddressOf()
|
||||
);
|
||||
|
||||
var resource = new D3D12Resource(buffer.Move(), tempResource);
|
||||
if (tempResource)
|
||||
{
|
||||
_temResources.Enqueue(new(resource));
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
public IResource CreateCopyDestinationBuffer(uint sizeInBytes, bool tempResource = false, ResourceFlags flags = ResourceFlags.None)
|
||||
{
|
||||
if (sizeInBytes > _MAX_BYTES)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
}
|
||||
|
||||
var device = GraphicsPipeline.GetGraphicsDevice<D3D12GraphicsDevice>();
|
||||
var heapProperties = new HeapProperties(HeapType.Default);
|
||||
var resourceDescription = ResourceDescription.Buffer(sizeInBytes, flags);
|
||||
|
||||
ComPtr<ID3D12Resource> buffer = default;
|
||||
device.NativeDevice.Ptr->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
HeapFlags.None,
|
||||
&resourceDescription,
|
||||
ResourceStates.Common,
|
||||
null,
|
||||
__uuidof<ID3D12Resource>(),
|
||||
buffer.GetVoidAddressOf()
|
||||
);
|
||||
|
||||
var resource = new D3D12Resource(buffer.Move(), tempResource);
|
||||
if (tempResource)
|
||||
{
|
||||
_temResources.Enqueue(new(resource));
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void ReleaseTempResource()
|
||||
{
|
||||
while (_temResources.Count > 0)
|
||||
{
|
||||
var info = _temResources.Peek();
|
||||
if (info.cpuFenceValue > GraphicsPipeline.GPUFenceValue)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
info.resource.DisposeInternal();
|
||||
_temResources.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ReleaseTempResource();
|
||||
}
|
||||
}
|
||||
246
Ghost.Graphics/D3D12/Utilities/D3D12DescriptorAllocator.cs
Normal file
246
Ghost.Graphics/D3D12/Utilities/D3D12DescriptorAllocator.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
using Ghost.Core;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using DescriptorIndex = System.UInt32;
|
||||
|
||||
namespace Ghost.Graphics.D3D12.Utilities;
|
||||
|
||||
internal unsafe struct D3D12DescriptorAllocator : IDisposable
|
||||
{
|
||||
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
|
||||
|
||||
private readonly ConstPtr<ID3D12Device14> _device;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private ComPtr<ID3D12DescriptorHeap> _heap;
|
||||
private ComPtr<ID3D12DescriptorHeap> _shaderVisibleHeap;
|
||||
private CpuDescriptorHandle _startCpuHandle;
|
||||
private CpuDescriptorHandle _startCpuHandleShaderVisible;
|
||||
private GpuDescriptorHandle _startGpuHandleShaderVisible;
|
||||
private DescriptorIndex _searchStart;
|
||||
private bool[] _allocatedDescriptors = [];
|
||||
|
||||
public DescriptorHeapType HeapType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public uint NumDescriptors
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public uint NumAllocatedDescriptors
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public bool ShaderVisible
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public uint Stride
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly ConstPtr<ID3D12DescriptorHeap> Heap => new(_heap.Get());
|
||||
public readonly ConstPtr<ID3D12DescriptorHeap> ShaderVisibleHeap => new(_shaderVisibleHeap.Get());
|
||||
|
||||
public D3D12DescriptorAllocator(ConstPtr<ID3D12Device14> device, DescriptorHeapType type, uint numDescriptors)
|
||||
{
|
||||
_device = device;
|
||||
HeapType = type;
|
||||
NumDescriptors = numDescriptors;
|
||||
ShaderVisible = type == DescriptorHeapType.CbvSrvUav || type == DescriptorHeapType.Sampler;
|
||||
Stride = device.Ptr->GetDescriptorHandleIncrementSize(type);
|
||||
|
||||
var success = AllocateResources(numDescriptors);
|
||||
Debug.Assert(success);
|
||||
}
|
||||
|
||||
public DescriptorIndex AllocateDescriptor() => AllocateDescriptors(1);
|
||||
|
||||
public DescriptorIndex AllocateDescriptors(uint count)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
DescriptorIndex foundIndex = 0;
|
||||
uint freeCount = 0;
|
||||
var found = false;
|
||||
|
||||
// Find a contiguous range of 'count' indices for which _allocatedDescriptors[index] is false
|
||||
for (var index = _searchStart; index < NumDescriptors; index++)
|
||||
{
|
||||
if (_allocatedDescriptors[index])
|
||||
{
|
||||
freeCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
freeCount += 1;
|
||||
}
|
||||
|
||||
if (freeCount >= count)
|
||||
{
|
||||
foundIndex = index > 0 ? index - count + 1 : 0;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
foundIndex = NumDescriptors;
|
||||
|
||||
if (!Grow(NumDescriptors + count))
|
||||
{
|
||||
Debug.WriteLine("ERROR: Failed to grow a descriptor heap!");
|
||||
return _INVALID_DESCRIPTOR_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
for (var index = foundIndex; index < foundIndex + count; index++)
|
||||
{
|
||||
_allocatedDescriptors[index] = true;
|
||||
}
|
||||
|
||||
NumAllocatedDescriptors += count;
|
||||
_searchStart = foundIndex + count;
|
||||
return foundIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseDescriptor(DescriptorIndex index) => ReleaseDescriptors(index, 1);
|
||||
|
||||
public void ReleaseDescriptors(DescriptorIndex baseIndex, uint count = 1)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
for (var index = baseIndex; index < baseIndex + count; index++)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!_allocatedDescriptors[index])
|
||||
{
|
||||
Debug.WriteLine("Error: Attempted to release an un-allocated descriptor");
|
||||
}
|
||||
#endif
|
||||
|
||||
_allocatedDescriptors[index] = false;
|
||||
}
|
||||
|
||||
NumAllocatedDescriptors -= count;
|
||||
|
||||
if (_searchStart > baseIndex)
|
||||
{
|
||||
_searchStart = baseIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CpuDescriptorHandle GetCpuHandle(DescriptorIndex index)
|
||||
{
|
||||
var handle = _startCpuHandle;
|
||||
return handle.Offset((int)index, Stride);
|
||||
}
|
||||
|
||||
public CpuDescriptorHandle GetCpuHandleShaderVisible(DescriptorIndex index)
|
||||
{
|
||||
var handle = _startCpuHandleShaderVisible;
|
||||
return handle.Offset((int)index, Stride);
|
||||
}
|
||||
|
||||
public GpuDescriptorHandle GetGpuHandle(DescriptorIndex index)
|
||||
{
|
||||
var handle = _startGpuHandleShaderVisible;
|
||||
return handle.Offset((int)index, Stride);
|
||||
}
|
||||
|
||||
public void CopyToShaderVisibleHeap(DescriptorIndex index, uint count = 1)
|
||||
{
|
||||
_device.Ptr->CopyDescriptorsSimple(count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
|
||||
}
|
||||
|
||||
private bool AllocateResources(uint numDescriptors)
|
||||
{
|
||||
NumDescriptors = numDescriptors;
|
||||
_heap.Dispose();
|
||||
_shaderVisibleHeap.Dispose();
|
||||
|
||||
DescriptorHeapDescription heapDesc = new()
|
||||
{
|
||||
Type = HeapType,
|
||||
NumDescriptors = numDescriptors,
|
||||
Flags = DescriptorHeapFlags.None,
|
||||
NodeMask = 0
|
||||
};
|
||||
|
||||
fixed (void* heapPtr = &_heap)
|
||||
{
|
||||
var hr = _device.Ptr->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||
if (hr.Failure)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_startCpuHandle = _heap.Get()->GetCPUDescriptorHandleForHeapStart();
|
||||
Array.Resize(ref _allocatedDescriptors, (int)numDescriptors);
|
||||
|
||||
if (ShaderVisible)
|
||||
{
|
||||
heapDesc.Flags = DescriptorHeapFlags.ShaderVisible;
|
||||
|
||||
fixed (void* heapPtr = &_shaderVisibleHeap)
|
||||
{
|
||||
var hr = _device.Ptr->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||
if (hr.Failure)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_startCpuHandleShaderVisible = _shaderVisibleHeap.Get()->GetCPUDescriptorHandleForHeapStart();
|
||||
_startGpuHandleShaderVisible = _shaderVisibleHeap.Get()->GetGPUDescriptorHandleForHeapStart();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Grow(uint minRequiredSize)
|
||||
{
|
||||
var oldSize = NumDescriptors;
|
||||
var newSize = BitOperations.RoundUpToPowerOf2(minRequiredSize);
|
||||
|
||||
var oldHeap = _heap;
|
||||
|
||||
if (!AllocateResources(newSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_device.Ptr->CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
|
||||
if (_shaderVisibleHeap.Get() is not null)
|
||||
{
|
||||
_device.Ptr->CopyDescriptorsSimple(oldSize, _startCpuHandleShaderVisible, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_heap.Dispose();
|
||||
_shaderVisibleHeap.Dispose();
|
||||
}
|
||||
}
|
||||
25
Ghost.Graphics/D3D12/Utilities/D3D12PipelineResource.cs
Normal file
25
Ghost.Graphics/D3D12/Utilities/D3D12PipelineResource.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Ghost.Graphics.Data;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.D3D12.Utilities;
|
||||
|
||||
internal unsafe static class D3D12PipelineResource
|
||||
{
|
||||
private readonly static InputElementDescription[] s_inputElementDescs = [
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.PositionName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.NormalName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.TangentName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 32u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.ColorName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 48u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||
new InputElementDescription{ SemanticName = Vertex.Semantic.UVName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 64u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 }
|
||||
];
|
||||
|
||||
public const Format SWAP_CHAIN_BACK_BUFFER_FORMAT = Format.B8G8R8A8Unorm;
|
||||
|
||||
public static InputLayoutDescription InputLayoutDescription => new()
|
||||
{
|
||||
pInputElementDescs = (InputElementDescription*)Unsafe.AsPointer(ref s_inputElementDescs[0]),
|
||||
NumElements = (uint)s_inputElementDescs.Length
|
||||
};
|
||||
}
|
||||
5
Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs
Normal file
5
Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Ghost.Graphics.D3D12.Utilities;
|
||||
|
||||
internal static class D3D12Utility
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user