feat(d3d12): unify resource mgmt & add pooling system
Refactored D3D12 resource and command management with a new D3D12Object<T> base class for unified lifetime and naming of COM objects. Introduced pooled command buffer and resource management in D3D12GraphicsEngine and ResourceManager, using frame-based return queues for safe reuse. Updated RenderSystem to use pooled command buffers and render requests, and to properly dispose of per-frame resources. Changed frame synchronization and resource release logic to use ulong fence/frame values for improved robustness. Refactored swap chain to DXGISwapChain and improved error handling and code clarity. Removed renderer management from IGraphicsEngine. Changed ResourceDesc, TextureDesc, and BufferDesc to record structs with equality and hashing for pooling. BREAKING CHANGE: Renderer management APIs removed from IGraphicsEngine. Frame and resource synchronization now use ulong instead of uint. Resource pooling and command buffer pooling are now required for correct usage.
This commit is contained in:
@@ -1,33 +1,26 @@
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12CommandAllocator : ICommandAllocator
|
||||
internal unsafe class D3D12CommandAllocator : D3D12Object<ID3D12CommandAllocator>, ICommandAllocator
|
||||
{
|
||||
private UniquePtr<ID3D12CommandAllocator> _allocator;
|
||||
|
||||
public SharedPtr<ID3D12CommandAllocator> NativeAllocator => _allocator.Share();
|
||||
|
||||
public D3D12CommandAllocator(D3D12RenderDevice device, CommandBufferType type)
|
||||
private static ID3D12CommandAllocator* CreateCommandAllocator(ID3D12Device14* device, D3D12_COMMAND_LIST_TYPE type)
|
||||
{
|
||||
ID3D12CommandAllocator* pAllocator = default;
|
||||
var commandListType = D3D12Utility.ToCommandListType(type);
|
||||
ThrowIfFailed(device->CreateCommandAllocator(type, __uuidof(pAllocator), (void**)&pAllocator));
|
||||
return pAllocator;
|
||||
}
|
||||
|
||||
device.NativeDevice.Get()->CreateCommandAllocator(commandListType, __uuidof(pAllocator), (void**)&pAllocator);
|
||||
|
||||
_allocator.Attach(pAllocator);
|
||||
public D3D12CommandAllocator(D3D12RenderDevice device, CommandBufferType type)
|
||||
: base(CreateCommandAllocator(device.NativeObject, D3D12Utility.ToCommandListType(type)))
|
||||
{
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_allocator.Get()->Reset();
|
||||
AssertNotDisposed();
|
||||
pNativeObject->Reset();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_allocator.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,8 @@ using static TerraFX.Aliases.DXGI_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList10>, ICommandBuffer
|
||||
{
|
||||
private UniquePtr<ID3D12GraphicsCommandList10> _commandList;
|
||||
|
||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
private readonly D3D12ResourceAllocator _resourceAllocator;
|
||||
@@ -27,46 +25,29 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
private CommandError _lastError;
|
||||
private ushort _commandCount;
|
||||
private bool _isRecording;
|
||||
private bool _disposed;
|
||||
|
||||
public SharedPtr<ID3D12GraphicsCommandList10> NativeCommandList => _commandList.Get();
|
||||
|
||||
public CommandBufferType Type => _type;
|
||||
public bool IsEmpty => _commandCount == 0;
|
||||
|
||||
public string Name
|
||||
private static ID3D12GraphicsCommandList10* CreateCommandList(ID3D12Device14* device, D3D12_COMMAND_LIST_TYPE type)
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
if (field == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
field = value;
|
||||
_commandList.Get()->SetName(value);
|
||||
}
|
||||
} = string.Empty;
|
||||
ID3D12GraphicsCommandList10* pCommandList = default;
|
||||
ThrowIfFailed(device->CreateCommandList1(0u, type, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList));
|
||||
return pCommandList;
|
||||
}
|
||||
|
||||
public D3D12CommandBuffer(
|
||||
D3D12RenderDevice device,
|
||||
D3D12PipelineLibrary stateController,
|
||||
D3D12PipelineLibrary pipelineLibrary,
|
||||
D3D12ResourceDatabase resourceDatabase,
|
||||
D3D12ResourceAllocator resourceAllocator,
|
||||
D3D12DescriptorAllocator descriptorAllocator,
|
||||
CommandBufferType type)
|
||||
:base (CreateCommandList(device.NativeObject, D3D12Utility.ToCommandListType(type)))
|
||||
{
|
||||
_type = type;
|
||||
|
||||
ID3D12GraphicsCommandList10* pCommandList = default;
|
||||
var commandListType = D3D12Utility.ToCommandListType(type);
|
||||
|
||||
device.NativeDevice.Get()->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList);
|
||||
|
||||
_commandList.Attach(pCommandList);
|
||||
|
||||
_pipelineLibrary = stateController;
|
||||
_pipelineLibrary = pipelineLibrary;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
_resourceAllocator = resourceAllocator;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
@@ -74,18 +55,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
_isRecording = false;
|
||||
}
|
||||
|
||||
~D3D12CommandBuffer()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfRecording()
|
||||
{
|
||||
@@ -95,6 +65,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfNotRecording()
|
||||
{
|
||||
@@ -133,7 +104,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
throw new ArgumentException("Invalid command allocator type", nameof(allocator));
|
||||
}
|
||||
|
||||
ThrowIfFailed(_commandList.Get()->Reset(d3d12Allocator.NativeAllocator, null));
|
||||
ThrowIfFailed(pNativeObject->Reset(d3d12Allocator.NativeObject, null));
|
||||
|
||||
if (Type == CommandBufferType.Graphics || Type == CommandBufferType.Compute)
|
||||
{
|
||||
@@ -142,7 +113,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
var heaps = stackalloc ID3D12DescriptorHeap*[2];
|
||||
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource Heap
|
||||
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler Heap
|
||||
_commandList.Get()->SetDescriptorHeaps(2, heaps);
|
||||
pNativeObject->SetDescriptorHeaps(2, heaps);
|
||||
}
|
||||
|
||||
_commandCount = 0;
|
||||
@@ -154,7 +125,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
_commandList.Get()->Close();
|
||||
pNativeObject->Close();
|
||||
_isRecording = false;
|
||||
|
||||
#if !DEBUG
|
||||
@@ -169,7 +140,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
|
||||
public void SetScissorRect(RectDesc rect)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -180,12 +151,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
IncrementCommandCount();
|
||||
|
||||
var d3d12Rect = new RECT((int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom);
|
||||
_commandList.Get()->RSSetScissorRects(1, &d3d12Rect);
|
||||
pNativeObject->RSSetScissorRects(1, &d3d12Rect);
|
||||
}
|
||||
|
||||
public void ResourceBarrier(params ReadOnlySpan<BarrierDesc> barrierDescs)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -333,12 +304,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
groupCount++;
|
||||
}
|
||||
|
||||
_commandList.Get()->Barrier(groupCount, groups);
|
||||
pNativeObject->Barrier(groupCount, groups);
|
||||
}
|
||||
|
||||
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -386,12 +357,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(viewGroup.dsv);
|
||||
}
|
||||
|
||||
_commandList.Get()->OMSetRenderTargets(rtvCount, pRtvHandles, FALSE, pDsvHandle);
|
||||
pNativeObject->OMSetRenderTargets(rtvCount, pRtvHandles, FALSE, pDsvHandle);
|
||||
}
|
||||
|
||||
public void ClearRenderTargetView(Handle<Texture> renderTarget, Color128 clearColor)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -411,12 +382,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
ref var record = ref recordResult.Value;
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.rtv);
|
||||
|
||||
_commandList.Get()->ClearRenderTargetView(cpuHandle, (float*)&clearColor, 0, null);
|
||||
pNativeObject->ClearRenderTargetView(cpuHandle, (float*)&clearColor, 0, null);
|
||||
}
|
||||
|
||||
public void ClearDepthStencilView(Handle<Texture> depthStencil, bool inlcudeDepth, bool includeStencil, float clearDepth = 1.0f, byte clearStencil = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -437,7 +408,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.dsv);
|
||||
var flag = (inlcudeDepth ? D3D12_CLEAR_FLAG_DEPTH : 0) | (includeStencil ? D3D12_CLEAR_FLAG_STENCIL : 0);
|
||||
|
||||
_commandList.Get()->ClearDepthStencilView(cpuHandle,
|
||||
pNativeObject->ClearDepthStencilView(cpuHandle,
|
||||
flag,
|
||||
clearDepth,
|
||||
clearStencil,
|
||||
@@ -447,7 +418,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
|
||||
public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -602,13 +573,13 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
pDsvDesc[0] = desc;
|
||||
}
|
||||
|
||||
_commandList.Get()->BeginRenderPass((uint)rtDescs.Length, pRtvDescs, pDsvDesc,
|
||||
pNativeObject->BeginRenderPass((uint)rtDescs.Length, pRtvDescs, pDsvDesc,
|
||||
allowUAVWrites ? D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES : D3D12_RENDER_PASS_FLAG_NONE);
|
||||
}
|
||||
|
||||
public void EndRenderPass()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -618,12 +589,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
_commandList.Get()->EndRenderPass();
|
||||
pNativeObject->EndRenderPass();
|
||||
}
|
||||
|
||||
public void SetViewport(ViewportDesc viewport)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -634,12 +605,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
IncrementCommandCount();
|
||||
|
||||
var d3d12Viewport = new D3D12_VIEWPORT(viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
|
||||
_commandList.Get()->RSSetViewports(1, &d3d12Viewport);
|
||||
pNativeObject->RSSetViewports(1, &d3d12Viewport);
|
||||
}
|
||||
|
||||
public void SetPipelineState(Key128<GraphicsPipeline> pipelineKey)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -656,13 +627,13 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
return;
|
||||
}
|
||||
|
||||
_commandList.Get()->SetGraphicsRootSignature(_pipelineLibrary.DefaultRootSignature);
|
||||
_commandList.Get()->SetPipelineState(psor.Value);
|
||||
pNativeObject->SetGraphicsRootSignature(_pipelineLibrary.DefaultRootSignature);
|
||||
pNativeObject->SetPipelineState(psor.Value);
|
||||
}
|
||||
|
||||
public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -673,12 +644,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
IncrementCommandCount();
|
||||
|
||||
var resource = _resourceDatabase.GetResource(buffer.AsResource());
|
||||
_commandList.Get()->SetGraphicsRootConstantBufferView(slot, resource.Get()->GetGPUVirtualAddress());
|
||||
pNativeObject->SetGraphicsRootConstantBufferView(slot, resource.Get()->GetGPUVirtualAddress());
|
||||
}
|
||||
|
||||
public void SetVertexBuffer(uint slot, Handle<GraphicsBuffer> buffer, ulong offset = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -703,12 +674,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
StrideInBytes = record.desc.BufferDescription.Stride
|
||||
};
|
||||
|
||||
_commandList.Get()->IASetVertexBuffers(slot, 1, &vbView);
|
||||
pNativeObject->IASetVertexBuffers(slot, 1, &vbView);
|
||||
}
|
||||
|
||||
public void SetIndexBuffer(Handle<GraphicsBuffer> buffer, IndexType type, ulong offset = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -726,12 +697,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
Format = type == IndexType.UInt16 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT
|
||||
};
|
||||
|
||||
_commandList.Get()->IASetIndexBuffer(&ibView);
|
||||
pNativeObject->IASetIndexBuffer(&ibView);
|
||||
}
|
||||
|
||||
public void SetPrimitiveTopology(PrimitiveTopology topology)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -749,12 +720,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
_ => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST
|
||||
};
|
||||
|
||||
_commandList.Get()->IASetPrimitiveTopology(d3d12Topology);
|
||||
pNativeObject->IASetPrimitiveTopology(d3d12Topology);
|
||||
}
|
||||
|
||||
public void SetGraphicsRoot32Constants(uint rootIndex, ReadOnlySpan<uint> constantBuffer, uint offsetIn32Bits = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -766,13 +737,13 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
|
||||
fixed (uint* pConstants = constantBuffer)
|
||||
{
|
||||
_commandList.Get()->SetGraphicsRoot32BitConstants(rootIndex, (uint)constantBuffer.Length, pConstants, offsetIn32Bits);
|
||||
pNativeObject->SetGraphicsRoot32BitConstants(rootIndex, (uint)constantBuffer.Length, pConstants, offsetIn32Bits);
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(uint vertexCount, uint instanceCount = 1, uint startVertex = 0, uint startInstance = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -782,12 +753,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
_commandList.Get()->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
|
||||
pNativeObject->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
|
||||
}
|
||||
|
||||
public void DrawIndexed(uint indexCount, uint instanceCount = 1, uint startIndex = 0, int baseVertex = 0, uint startInstance = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -797,12 +768,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
_commandList.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
|
||||
pNativeObject->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
|
||||
}
|
||||
|
||||
public void DispatchCompute(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -812,12 +783,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||
pNativeObject->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||
}
|
||||
|
||||
public void DispatchMesh(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -827,14 +798,14 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
_commandList.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||
pNativeObject->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||
}
|
||||
|
||||
public void DispatchRay()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
// ThrowIfDisposed();
|
||||
// AssertNotDisposed();
|
||||
// ThrowIfNotRecording();
|
||||
// IncrementCommandCount();
|
||||
|
||||
@@ -850,7 +821,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -865,7 +836,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
var countResource = _resourceDatabase.GetResource(countBuffer.AsResource());
|
||||
|
||||
// TODO
|
||||
_commandList.Get()->ExecuteIndirect(null, 0,
|
||||
pNativeObject->ExecuteIndirect(null, 0,
|
||||
resource, argumentOffset, countResource, countBufferOffset);
|
||||
|
||||
}
|
||||
@@ -873,7 +844,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
public void UploadBuffer<T>(Handle<GraphicsBuffer> buffer, params ReadOnlySpan<T> data)
|
||||
where T : unmanaged
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -897,12 +868,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
uploadResource.Get()->Unmap(0, null);
|
||||
|
||||
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
|
||||
_commandList.Get()->CopyBufferRegion(pResource, 0, uploadResource, offset, sizeInBytes);
|
||||
pNativeObject->CopyBufferRegion(pResource, 0, uploadResource, offset, sizeInBytes);
|
||||
}
|
||||
|
||||
public void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -932,7 +903,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
}
|
||||
|
||||
UpdateSubresources(
|
||||
(ID3D12GraphicsCommandList*)_commandList.Get(),
|
||||
(ID3D12GraphicsCommandList*)pNativeObject,
|
||||
resource,
|
||||
pUploadResource,
|
||||
offset,
|
||||
@@ -943,7 +914,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
|
||||
public void CopyBuffer(Handle<GraphicsBuffer> dst, Handle<GraphicsBuffer> src, ulong dstOffset = 0, ulong srcOffset = 0, ulong numBytes = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -968,11 +939,11 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
|
||||
if (numBytes == 0)
|
||||
{
|
||||
_commandList.Get()->CopyResource(pDstResource, pSrcResource);
|
||||
pNativeObject->CopyResource(pDstResource, pSrcResource);
|
||||
}
|
||||
else
|
||||
{
|
||||
_commandList.Get()->CopyBufferRegion(pDstResource, dstOffset, pSrcResource, srcOffset, numBytes);
|
||||
pNativeObject->CopyBufferRegion(pDstResource, dstOffset, pSrcResource, srcOffset, numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1002,7 +973,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
|
||||
public void CopyTexture(Handle<Texture> dst, TextureRegion? dstRegion, Handle<Texture> src, TextureRegion? srcRegion)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
AssertNotDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
@@ -1033,7 +1004,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
return;
|
||||
}
|
||||
|
||||
_commandList.Get()->CopyResource(pDstResource, pSrcResource);
|
||||
pNativeObject->CopyResource(pDstResource, pSrcResource);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1052,25 +1023,6 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
back = srcRegionV.Z + srcRegionV.Depth
|
||||
};
|
||||
|
||||
_commandList.Get()->CopyTextureRegion(&dstLocation, dstRegionV.X, dstRegionV.Y, dstRegionV.Z, &srcLocation, &srcBoc);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isRecording)
|
||||
{
|
||||
throw new InvalidOperationException("Command buffer is still recording");
|
||||
}
|
||||
|
||||
_commandList.Dispose();
|
||||
_commandCount = 0;
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
pNativeObject->CopyTextureRegion(&dstLocation, dstRegionV.X, dstRegionV.Y, dstRegionV.Z, &srcLocation, &srcBoc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,28 +10,20 @@ namespace Ghost.Graphics.D3D12;
|
||||
/// <summary>
|
||||
/// D3D12 implementation of command queue interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue1>, ICommandQueue
|
||||
{
|
||||
private UniquePtr<ID3D12CommandQueue> _commandQueue;
|
||||
private UniquePtr<ID3D12Fence1> _fence;
|
||||
|
||||
private readonly AutoResetEvent _fenceEvent;
|
||||
private ulong _fenceValue;
|
||||
private bool _disposed;
|
||||
|
||||
public CommandQueueType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public SharedPtr<ID3D12CommandQueue> NativeQueue => _commandQueue.Get();
|
||||
|
||||
public D3D12CommandQueue(ID3D12Device14* pDevice, CommandQueueType type)
|
||||
private static ID3D12CommandQueue1* CreateCommandQueue(ID3D12Device14* device, CommandQueueType type)
|
||||
{
|
||||
Type = type;
|
||||
_fenceEvent = new AutoResetEvent(false);
|
||||
_fenceValue = 0;
|
||||
|
||||
var queueDesc = new D3D12_COMMAND_QUEUE_DESC
|
||||
{
|
||||
Type = ConvertCommandQueueType(type),
|
||||
@@ -39,18 +31,22 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
Flags = D3D12_COMMAND_QUEUE_FLAGS.D3D12_COMMAND_QUEUE_FLAG_NONE,
|
||||
};
|
||||
|
||||
ID3D12CommandQueue* pQueue = default;
|
||||
ID3D12Fence1* pFence = default;
|
||||
ThrowIfFailed(pDevice->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue));
|
||||
ThrowIfFailed(pDevice->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
|
||||
|
||||
_commandQueue.Attach(pQueue);
|
||||
_fence.Attach(pFence);
|
||||
ID3D12CommandQueue1* pQueue = default;
|
||||
ThrowIfFailed(device->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue));
|
||||
return pQueue;
|
||||
}
|
||||
|
||||
~D3D12CommandQueue()
|
||||
public D3D12CommandQueue(D3D12RenderDevice device, CommandQueueType type)
|
||||
:base(CreateCommandQueue(device.NativeObject, type))
|
||||
{
|
||||
Dispose();
|
||||
Type = type;
|
||||
_fenceEvent = new AutoResetEvent(false);
|
||||
_fenceValue = 0;
|
||||
|
||||
ID3D12Fence1* pFence = default;
|
||||
ThrowIfFailed(device.NativeObject.Get()->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
|
||||
|
||||
_fence.Attach(pFence);
|
||||
}
|
||||
|
||||
private static D3D12_COMMAND_LIST_TYPE ConvertCommandQueueType(CommandQueueType type)
|
||||
@@ -66,7 +62,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public void Submit(ICommandBuffer commandBuffer)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
AssertNotDisposed();
|
||||
|
||||
if (commandBuffer.IsEmpty)
|
||||
{
|
||||
@@ -75,9 +71,9 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
if (commandBuffer is D3D12CommandBuffer d3d12CommandBuffer)
|
||||
{
|
||||
var commandList = d3d12CommandBuffer.NativeCommandList;
|
||||
var commandList = d3d12CommandBuffer.NativeObject;
|
||||
var commandListPtr = (ID3D12CommandList*)commandList.Get();
|
||||
_commandQueue.Get()->ExecuteCommandLists(1, &commandListPtr);
|
||||
pNativeObject->ExecuteCommandLists(1, &commandListPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -87,7 +83,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
AssertNotDisposed();
|
||||
|
||||
Span<int> executableIndices = stackalloc int[commandBuffers.Length];
|
||||
executableIndices.Fill(-1);
|
||||
@@ -115,7 +111,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
if (commandBuffers[cmdIndex] is D3D12CommandBuffer d3d12CommandBuffer)
|
||||
{
|
||||
ppCommandLists[currentIndex] = (ID3D12CommandList*)d3d12CommandBuffer.NativeCommandList.Get();
|
||||
ppCommandLists[currentIndex] = (ID3D12CommandList*)d3d12CommandBuffer.NativeObject.Get();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -125,21 +121,21 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
_commandQueue.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
|
||||
pNativeObject->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
|
||||
}
|
||||
|
||||
public ulong Signal(ulong value)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
AssertNotDisposed();
|
||||
|
||||
_fenceValue = value;
|
||||
ThrowIfFailed(_commandQueue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue));
|
||||
ThrowIfFailed(pNativeObject->Signal((ID3D12Fence*)_fence.Get(), _fenceValue));
|
||||
return _fenceValue;
|
||||
}
|
||||
|
||||
public void WaitForValue(ulong value)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
AssertNotDisposed();
|
||||
|
||||
if (_fence.Get()->GetCompletedValue() < value)
|
||||
{
|
||||
@@ -153,30 +149,21 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public ulong GetCompletedValue()
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
AssertNotDisposed();
|
||||
return _fence.Get()->GetCompletedValue();
|
||||
}
|
||||
|
||||
public void WaitIdle()
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
AssertNotDisposed();
|
||||
|
||||
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
|
||||
WaitForValue(fenceValue);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_commandQueue.Dispose();
|
||||
_fence.Dispose();
|
||||
_fenceEvent?.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
|
||||
HeapType = type;
|
||||
NumDescriptors = numDescriptors;
|
||||
ShaderVisible = type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
|
||||
Stride = device.NativeDevice.Get()->GetDescriptorHandleIncrementSize(type);
|
||||
Stride = device.NativeObject.Get()->GetDescriptorHandleIncrementSize(type);
|
||||
|
||||
var success = AllocateResources(numDescriptors);
|
||||
Debug.Assert(success);
|
||||
@@ -210,7 +210,7 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
|
||||
|
||||
public void CopyToShaderVisibleHeap(int index, int count = 1)
|
||||
{
|
||||
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
|
||||
_device.NativeObject.Get()->CopyDescriptorsSimple((uint)count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
|
||||
}
|
||||
|
||||
private bool AllocateResources(int numDescriptors)
|
||||
@@ -228,7 +228,7 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
|
||||
};
|
||||
|
||||
ID3D12DescriptorHeap* pHeap = default;
|
||||
var hr = _device.NativeDevice.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(pHeap), (void**)&pHeap);
|
||||
var hr = _device.NativeObject.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(pHeap), (void**)&pHeap);
|
||||
if (hr.FAILED)
|
||||
{
|
||||
return false;
|
||||
@@ -252,7 +252,7 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
|
||||
ID3D12DescriptorHeap* pShaderVisibleHeap = default;
|
||||
|
||||
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
||||
hr = _device.NativeDevice.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(pShaderVisibleHeap), (void**)&pShaderVisibleHeap);
|
||||
hr = _device.NativeObject.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(pShaderVisibleHeap), (void**)&pShaderVisibleHeap);
|
||||
if (hr.FAILED)
|
||||
{
|
||||
return false;
|
||||
@@ -281,11 +281,11 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
|
||||
return false;
|
||||
}
|
||||
|
||||
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)oldSize, _startCpuHandle, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
_device.NativeObject.Get()->CopyDescriptorsSimple((uint)oldSize, _startCpuHandle, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
|
||||
if (_shaderVisibleHeap.Get() != null)
|
||||
{
|
||||
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)oldSize, _startCpuHandleShaderVisible, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
_device.NativeObject.Get()->CopyDescriptorsSimple((uint)oldSize, _startCpuHandleShaderVisible, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -305,4 +305,4 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
|
||||
_shaderVisibleHeap.Dispose();
|
||||
_allocatedDescriptors.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@@ -22,6 +21,18 @@ public static class D3D12GraphicsEngineFactory
|
||||
|
||||
internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
{
|
||||
private readonly struct CommandBufferReturnEntry
|
||||
{
|
||||
public readonly ICommandBuffer commandBuffer;
|
||||
public readonly ulong returnFrame;
|
||||
|
||||
public CommandBufferReturnEntry(ICommandBuffer commandBuffer, ulong returnFrame)
|
||||
{
|
||||
this.commandBuffer = commandBuffer;
|
||||
this.returnFrame = returnFrame;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly GraphicsEngineDesc _desc;
|
||||
|
||||
#if ENABLE_DEBUG_LAYER
|
||||
@@ -34,8 +45,10 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||
private readonly D3D12ResourceAllocator _resourceAllocator;
|
||||
|
||||
private ImmutableArray<IRenderer> _renderers;
|
||||
private readonly Queue<ICommandBuffer> _commandBufferPool;
|
||||
private readonly Queue<CommandBufferReturnEntry> _commandBufferReturnQueue;
|
||||
|
||||
private ulong _currentFrame;
|
||||
private bool _disposed;
|
||||
|
||||
public IRenderDevice Device => _device;
|
||||
@@ -59,9 +72,8 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
_pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase);
|
||||
_resourceAllocator = new D3D12ResourceAllocator(_device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
|
||||
|
||||
_renderers = ImmutableArray<IRenderer>.Empty;
|
||||
|
||||
_pipelineLibrary.InitializeLibrary(null);
|
||||
_commandBufferPool = new Queue<ICommandBuffer>(4);
|
||||
_commandBufferReturnQueue = new Queue<CommandBufferReturnEntry>(4);
|
||||
}
|
||||
|
||||
~D3D12GraphicsEngine()
|
||||
@@ -76,27 +88,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
}
|
||||
|
||||
public IRenderer CreateRenderer()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var renderer = new D3D12Renderer(this);
|
||||
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Add(renderer));
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public void RemoveRenderer(IRenderer renderer)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Remove(renderer));
|
||||
}
|
||||
|
||||
public void ClearRenderers()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Clear());
|
||||
}
|
||||
|
||||
public ICommandAllocator CreateCommandAllocator(CommandBufferType type = CommandBufferType.Graphics)
|
||||
{
|
||||
return new D3D12CommandAllocator(_device, type);
|
||||
@@ -115,25 +106,51 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
type);
|
||||
}
|
||||
|
||||
public ICommandBuffer GetPooledCommandBuffer(CommandBufferType type = CommandBufferType.Graphics)
|
||||
{
|
||||
if (_commandBufferPool.TryDequeue(out var commandBuffer))
|
||||
{
|
||||
return commandBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CreateCommandBuffer(type);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReturnPooledCommandBuffer(ICommandBuffer commandBuffer)
|
||||
{
|
||||
_commandBufferReturnQueue.Enqueue(new CommandBufferReturnEntry(commandBuffer, _currentFrame));
|
||||
}
|
||||
|
||||
|
||||
public ISwapChain CreateSwapChain(SwapChainDesc desc)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _desc.FrameBufferCount);
|
||||
return new DXGISwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _desc.FrameBufferCount);
|
||||
}
|
||||
|
||||
public Result BeginFrame(uint cpuFenceValue, uint gpuFenceValue)
|
||||
public Result BeginFrame(ulong currentFrame)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
_resourceDatabase.BeginFrame(cpuFenceValue);
|
||||
_currentFrame = currentFrame;
|
||||
_resourceDatabase.BeginFrame(currentFrame);
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public Result EndFrame(uint cpuFenceValue, uint gpuFenceValue)
|
||||
public Result EndFrame(ulong completedFrame)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
_resourceDatabase.EndFrame(gpuFenceValue);
|
||||
_resourceDatabase.EndFrame(completedFrame);
|
||||
|
||||
while (_commandBufferReturnQueue.TryPeek(out var entry) && entry.returnFrame <= completedFrame)
|
||||
{
|
||||
_commandBufferPool.Enqueue(entry.commandBuffer);
|
||||
_commandBufferReturnQueue.Dequeue();
|
||||
}
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
@@ -144,11 +161,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
renderer.Dispose();
|
||||
}
|
||||
|
||||
_resourceDatabase.ReleaseAllResourcesImmediately();
|
||||
|
||||
_resourceAllocator.Dispose();
|
||||
|
||||
75
src/Runtime/Ghost.Graphics.D3D12/D3D12Object.cs
Normal file
75
src/Runtime/Ghost.Graphics.D3D12/D3D12Object.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
public unsafe abstract class D3D12Object<T>: IRHIObject
|
||||
where T : unmanaged, ID3D12Object.Interface
|
||||
{
|
||||
private UniquePtr<T> _nativeObject;
|
||||
private string _name = string.Empty;
|
||||
|
||||
protected T* pNativeObject => _nativeObject.Get();
|
||||
public SharedPtr<T> NativeObject => _nativeObject.Share();
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set
|
||||
{
|
||||
if (string.Equals(_name, value, StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_name = value;
|
||||
_nativeObject.Get()->SetName(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected D3D12Object(T* nativeObject)
|
||||
{
|
||||
_nativeObject.Attach(nativeObject);
|
||||
}
|
||||
|
||||
~D3D12Object()
|
||||
{
|
||||
Dispose(disposing: false);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void ThrowIfDisposed()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_nativeObject.Get() == null, this);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void AssertNotDisposed()
|
||||
{
|
||||
Debug.Assert(_nativeObject.Get() != null, "Object has been disposed.");
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_nativeObject.Get() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Dispose(disposing: true);
|
||||
|
||||
_nativeObject.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,29 +26,48 @@ internal struct D3D12PipelineState : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>, IPipelineLibrary
|
||||
{
|
||||
private readonly D3D12RenderDevice _device;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
|
||||
private UniquePtr<ID3D12PipelineLibrary1> _library;
|
||||
private UniquePtr<ID3D12RootSignature> _defaultRootSignature;
|
||||
|
||||
private readonly Dictionary<Key128<GraphicsPipeline>, D3D12PipelineState> _pipelineCache;
|
||||
private UnsafeHashMap<Key128<GraphicsPipeline>, D3D12PipelineState> _pipelineCache;
|
||||
|
||||
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
|
||||
|
||||
private static ID3D12PipelineLibrary1* CreateLibrary(D3D12RenderDevice device, string? filePath)
|
||||
{
|
||||
ID3D12PipelineLibrary1* pLibrary = default;
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
var fileBytes = File.ReadAllBytes(filePath);
|
||||
fixed (byte* pFileBytes = fileBytes)
|
||||
{
|
||||
ThrowIfFailed(device.NativeObject.Get()->CreatePipelineLibrary(pFileBytes, (nuint)fileBytes.Length, __uuidof(pLibrary), (void**)&pLibrary));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowIfFailed(device.NativeObject.Get()->CreatePipelineLibrary(null, 0, __uuidof(pLibrary), (void**)&pLibrary));
|
||||
}
|
||||
|
||||
return pLibrary;
|
||||
}
|
||||
|
||||
public D3D12PipelineLibrary(D3D12RenderDevice device, D3D12ResourceDatabase resourceDatabase)
|
||||
:base(CreateLibrary(device, null)) // TODO: we need to path to load the existing library from disk.
|
||||
{
|
||||
_device = device;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_pipelineCache = new Dictionary<Key128<GraphicsPipeline>, D3D12PipelineState>();
|
||||
_pipelineCache = new UnsafeHashMap<Key128<GraphicsPipeline>, D3D12PipelineState>(32, Allocator.Persistent);
|
||||
|
||||
CreateDefaultRootSignature().ThrowIfFailed();
|
||||
}
|
||||
|
||||
// TODO: Maybe we don't need 4 root signature. We can use bindless for global, per-view, and per-object buffers as well.
|
||||
private Result CreateDefaultRootSignature()
|
||||
{
|
||||
_defaultRootSignature = default;
|
||||
@@ -96,7 +115,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
}
|
||||
|
||||
ID3D12RootSignature* pRootSignature = default;
|
||||
ThrowIfFailed(_device.NativeDevice.Get()->CreateRootSignature(0, pSignature.Get()->GetBufferPointer(), pSignature.Get()->GetBufferSize(),
|
||||
ThrowIfFailed(_device.NativeObject.Get()->CreateRootSignature(0, pSignature.Get()->GetBufferPointer(), pSignature.Get()->GetBufferSize(),
|
||||
__uuidof(pRootSignature), (void**)&pRootSignature));
|
||||
|
||||
_defaultRootSignature.Attach(pRootSignature);
|
||||
@@ -104,26 +123,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public void InitializeLibrary(string? filePath)
|
||||
{
|
||||
ID3D12PipelineLibrary1* pLibrary = default;
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
var fileBytes = File.ReadAllBytes(filePath);
|
||||
fixed (byte* pFileBytes = fileBytes)
|
||||
{
|
||||
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineLibrary(pFileBytes, (nuint)fileBytes.Length, __uuidof(pLibrary), (void**)&pLibrary));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineLibrary(null, 0, __uuidof(pLibrary), (void**)&pLibrary));
|
||||
}
|
||||
|
||||
_library.Attach(pLibrary);
|
||||
}
|
||||
|
||||
public void SaveLibraryToDisk(string filePath)
|
||||
{
|
||||
var dir = Path.GetDirectoryName(filePath);
|
||||
@@ -132,10 +131,10 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
throw new InvalidOperationException($"Directory does not exist: {dir}");
|
||||
}
|
||||
|
||||
var size = _library.Get()->GetSerializedSize();
|
||||
var size = pNativeObject->GetSerializedSize();
|
||||
using var buffer = new UnsafeArray<byte>((int)size, Allocator.Persistent); // We use persistent Heap allocation instead of stack allocation to avoid stack overflow for large pipeline libraries.
|
||||
|
||||
ThrowIfFailed(_library.Get()->Serialize(buffer.GetUnsafePtr(), size));
|
||||
ThrowIfFailed(pNativeObject->Serialize(buffer.GetUnsafePtr(), size));
|
||||
|
||||
using var fs = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
fs.Write(buffer.AsSpan());
|
||||
@@ -225,6 +224,8 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
return psr.Value;
|
||||
}
|
||||
|
||||
AssertNotDisposed();
|
||||
|
||||
if (descriptor.RtvFormats.Length > D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT)
|
||||
{
|
||||
return Result.Failure($"RTV format count exceeds the maximum supported render target count of {D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT}.");
|
||||
@@ -300,12 +301,12 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
return Result.Failure("Failed to convert pipeline key to string.");
|
||||
}
|
||||
|
||||
var hr = _library.Get()->LoadPipeline(pKeyStr, &streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState);
|
||||
var hr = pNativeObject->LoadPipeline(pKeyStr, &streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState);
|
||||
if (hr == E.E_INVALIDARG)
|
||||
{
|
||||
// Pipeline not found in the library, create a new one.
|
||||
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
|
||||
ThrowIfFailed(_library.Get()->StorePipeline(pKeyStr, pPipelineState));
|
||||
ThrowIfFailed(_device.NativeObject.Get()->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
|
||||
ThrowIfFailed(pNativeObject->StorePipeline(pKeyStr, pPipelineState));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -325,11 +326,13 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
|
||||
public bool HasPipeline(Key128<GraphicsPipeline> key)
|
||||
{
|
||||
AssertNotDisposed();
|
||||
return _pipelineCache.ContainsKey(key);
|
||||
}
|
||||
|
||||
public Result<SharedPtr<ID3D12PipelineState>, Error> GetGraphicsPSO(Key128<GraphicsPipeline> key)
|
||||
{
|
||||
AssertNotDisposed();
|
||||
if (_pipelineCache.TryGetValue(key, out var cacheEntry))
|
||||
{
|
||||
return cacheEntry.pso.Share();
|
||||
@@ -338,16 +341,14 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
foreach (var kvp in _pipelineCache)
|
||||
{
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
|
||||
_pipelineCache.Clear();
|
||||
|
||||
_pipelineCache.Dispose();
|
||||
_defaultRootSignature.Dispose();
|
||||
_library.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,8 @@ using static TerraFX.Aliases.DXGI_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of the render device interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDevice
|
||||
{
|
||||
private UniquePtr<ID3D12Device14> _device;
|
||||
private UniquePtr<IDXGIFactory7> _dxgiFactory;
|
||||
private UniquePtr<IDXGIAdapter1> _adapter;
|
||||
|
||||
@@ -22,8 +18,6 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
private readonly D3D12CommandQueue _computeQueue;
|
||||
private readonly D3D12CommandQueue _copyQueue;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ICommandQueue GraphicsQueue => _graphicsQueue;
|
||||
public ICommandQueue ComputeQueue => _computeQueue;
|
||||
public ICommandQueue CopyQueue => _copyQueue;
|
||||
@@ -34,14 +28,28 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
}
|
||||
|
||||
public SharedPtr<IDXGIFactory7> DXGIFactory => _dxgiFactory.Share();
|
||||
public SharedPtr<ID3D12Device14> NativeDevice => _device.Share();
|
||||
public SharedPtr<IDXGIAdapter1> Adapter => _adapter.Share();
|
||||
public SharedPtr<ID3D12CommandQueue> NativeGraphicsQueue => _graphicsQueue.NativeQueue;
|
||||
public SharedPtr<ID3D12CommandQueue> NativeComputeQueue => _computeQueue.NativeQueue;
|
||||
public SharedPtr<ID3D12CommandQueue> NativeCopyQueue => _copyQueue.NativeQueue;
|
||||
public SharedPtr<ID3D12CommandQueue1> NativeGraphicsQueue => _graphicsQueue.NativeObject;
|
||||
public SharedPtr<ID3D12CommandQueue1> NativeComputeQueue => _computeQueue.NativeObject;
|
||||
public SharedPtr<ID3D12CommandQueue1> NativeCopyQueue => _copyQueue.NativeObject;
|
||||
|
||||
public D3D12RenderDevice()
|
||||
:base(CreateDevice(out var dxgiFactory, out var adapter))
|
||||
{
|
||||
_dxgiFactory.Attach(dxgiFactory);
|
||||
_adapter.Attach(adapter);
|
||||
|
||||
_graphicsQueue = new D3D12CommandQueue(this, CommandQueueType.Graphics);
|
||||
_computeQueue = new D3D12CommandQueue(this, CommandQueueType.Compute);
|
||||
_copyQueue = new D3D12CommandQueue(this, CommandQueueType.Copy);
|
||||
|
||||
FeatureSupport = GetFeatureSupport();
|
||||
}
|
||||
|
||||
private static ID3D12Device14* CreateDevice(out IDXGIFactory7* dxgiFactory, out IDXGIAdapter1* adapter)
|
||||
{
|
||||
adapter = default;
|
||||
|
||||
IDXGIFactory7* pFactory = default;
|
||||
#if DEBUG
|
||||
ThrowIfFailed(CreateDXGIFactory2(TRUE, __uuidof(pFactory), (void**)&pFactory));
|
||||
@@ -49,29 +57,13 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
ThrowIfFailed(CreateDXGIFactory2(FALSE, __uuidof(pFactory), (void**)&pFactory));
|
||||
#endif
|
||||
|
||||
_dxgiFactory.Attach(pFactory);
|
||||
dxgiFactory = pFactory;
|
||||
|
||||
InitializeDevice();
|
||||
|
||||
_graphicsQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Graphics);
|
||||
_computeQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Compute);
|
||||
_copyQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Copy);
|
||||
|
||||
FeatureSupport = GetFeatureSupport();
|
||||
}
|
||||
|
||||
~D3D12RenderDevice()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void InitializeDevice()
|
||||
{
|
||||
ID3D12Device14* pDevice = default;
|
||||
IDXGIAdapter1* pAdapter = default;
|
||||
|
||||
for (uint adapterIndex = 0;
|
||||
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, __uuidof(pAdapter), (void**)&pAdapter).SUCCEEDED;
|
||||
pFactory->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, __uuidof(pAdapter), (void**)&pAdapter).SUCCEEDED;
|
||||
adapterIndex++)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc = default;
|
||||
@@ -85,7 +77,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
|
||||
if (D3D12CreateDevice((IUnknown*)pAdapter, D3D_FEATURE_LEVEL_12_0, __uuidof(pDevice), (void**)&pDevice).SUCCEEDED)
|
||||
{
|
||||
_adapter.Attach(pAdapter);
|
||||
adapter = pAdapter;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -96,20 +88,21 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
if (pDevice == null)
|
||||
{
|
||||
pAdapter->Release(); // Dispose the last adapter we tried.
|
||||
pFactory->Release(); // Dispose the factory before throwing.
|
||||
throw new PlatformNotSupportedException("Cannot create ID3D12Device with feature level 12.0");
|
||||
}
|
||||
|
||||
_device.Attach(pDevice);
|
||||
return pDevice;
|
||||
}
|
||||
|
||||
private FeatureSupport GetFeatureSupport()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
ThrowIfDisposed();
|
||||
|
||||
var support = FeatureSupport.None;
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS options = default;
|
||||
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)).SUCCEEDED)
|
||||
if (pNativeObject->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)).SUCCEEDED)
|
||||
{
|
||||
if (options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_3)
|
||||
{
|
||||
@@ -123,7 +116,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
}
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS5 options5 = default;
|
||||
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options5, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS5)).SUCCEEDED)
|
||||
if (pNativeObject->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options5, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS5)).SUCCEEDED)
|
||||
{
|
||||
if (options5.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED)
|
||||
{
|
||||
@@ -132,7 +125,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
}
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS6 options6 = default;
|
||||
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS6)).SUCCEEDED)
|
||||
if (pNativeObject->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS6)).SUCCEEDED)
|
||||
{
|
||||
if (options6.VariableShadingRateTier != D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED)
|
||||
{
|
||||
@@ -141,7 +134,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
}
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = default;
|
||||
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS7)).SUCCEEDED)
|
||||
if (pNativeObject->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS7)).SUCCEEDED)
|
||||
{
|
||||
if (options7.MeshShaderTier != D3D12_MESH_SHADER_TIER_NOT_SUPPORTED)
|
||||
{
|
||||
@@ -155,7 +148,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
}
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS21 options9 = default;
|
||||
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS21, &options9, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS8)).SUCCEEDED)
|
||||
if (pNativeObject->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS21, &options9, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS8)).SUCCEEDED)
|
||||
{
|
||||
if (options9.WorkGraphsTier != D3D12_WORK_GRAPHS_TIER.D3D12_WORK_GRAPHS_TIER_NOT_SUPPORTED)
|
||||
{
|
||||
@@ -166,22 +159,13 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
return support;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_graphicsQueue.Dispose();
|
||||
_computeQueue.Dispose();
|
||||
_copyQueue.Dispose();
|
||||
|
||||
_device.Dispose();
|
||||
_dxgiFactory.Dispose();
|
||||
_adapter.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +468,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
var desc = new D3D12MA_ALLOCATOR_DESC
|
||||
{
|
||||
pAdapter = (IDXGIAdapter*)device.Adapter.Get(),
|
||||
pDevice = (ID3D12Device*)device.NativeDevice.Get(),
|
||||
pDevice = (ID3D12Device*)device.NativeObject.Get(),
|
||||
Flags = D3D12MA_ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED | D3D12MA_ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED,
|
||||
};
|
||||
|
||||
@@ -546,7 +546,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
d3d12Desc = desc.BufferDescription.ToD3D12ResourceDesc();
|
||||
}
|
||||
|
||||
var info = _device.NativeDevice.Get()->GetResourceAllocationInfo(0, 1, &d3d12Desc);
|
||||
var info = _device.NativeObject.Get()->GetResourceAllocationInfo(0, 1, &d3d12Desc);
|
||||
return new ResourceSizeInfo
|
||||
{
|
||||
Size = info.SizeInBytes,
|
||||
@@ -646,7 +646,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
var isCubeMap = desc.Dimension == TextureDimension.TextureCube || desc.Dimension == TextureDimension.TextureCubeArray;
|
||||
var srvDesc = CreateTextureSrvDesc(pResource, resourceDesc.MipLevels, resourceDesc.DepthOrArraySize, isCubeMap);
|
||||
|
||||
_device.NativeDevice.Get()->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
|
||||
_device.NativeObject.Get()->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.srv);
|
||||
}
|
||||
|
||||
@@ -656,7 +656,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.rtv);
|
||||
var rtvDesc = CreateRtvDesc(pResource);
|
||||
|
||||
_device.NativeDevice.Get()->CreateRenderTargetView(pResource, &rtvDesc, cpuHandle);
|
||||
_device.NativeObject.Get()->CreateRenderTargetView(pResource, &rtvDesc, cpuHandle);
|
||||
}
|
||||
|
||||
if (desc.Usage.HasFlag(TextureUsage.DepthStencil))
|
||||
@@ -665,7 +665,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.dsv);
|
||||
var dsvDesc = CreateDsvDesc(pResource);
|
||||
|
||||
_device.NativeDevice.Get()->CreateDepthStencilView(pResource, &dsvDesc, cpuHandle);
|
||||
_device.NativeObject.Get()->CreateDepthStencilView(pResource, &dsvDesc, cpuHandle);
|
||||
}
|
||||
|
||||
if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess))
|
||||
@@ -674,7 +674,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
|
||||
var uavDesc = CreateTextureUavDesc(pResource);
|
||||
|
||||
_device.NativeDevice.Get()->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
|
||||
_device.NativeObject.Get()->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.uav);
|
||||
}
|
||||
|
||||
@@ -764,7 +764,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
SizeInBytes = (uint)resourceDesc.Width,
|
||||
};
|
||||
|
||||
_device.NativeDevice.Get()->CreateConstantBufferView(&cbvDesc, cpuHandle);
|
||||
_device.NativeObject.Get()->CreateConstantBufferView(&cbvDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.cbv);
|
||||
}
|
||||
|
||||
@@ -774,7 +774,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
|
||||
var srvDesc = CreateBufferSrvDesc(pResource, desc.Stride, isRaw);
|
||||
|
||||
_device.NativeDevice.Get()->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
|
||||
_device.NativeObject.Get()->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.srv);
|
||||
}
|
||||
|
||||
@@ -784,7 +784,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
|
||||
var uavDesc = CreateBufferUavDesc(pResource, desc.Stride, isRaw);
|
||||
|
||||
_device.NativeDevice.Get()->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
|
||||
_device.NativeObject.Get()->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.uav);
|
||||
}
|
||||
|
||||
@@ -862,7 +862,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
var samplerDescriptor = _descriptorAllocator.AllocateSampler();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(samplerDescriptor);
|
||||
_device.NativeDevice.Get()->CreateSampler(&samplerDesc, cpuHandle);
|
||||
_device.NativeObject.Get()->CreateSampler(&samplerDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(samplerDescriptor);
|
||||
|
||||
return _resourceDatabase.AddSampler(in desc, samplerDescriptor.Value);
|
||||
|
||||
@@ -88,9 +88,9 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
private readonly struct ReleaseEntry
|
||||
{
|
||||
public readonly ResourceRecord record;
|
||||
public readonly uint fenceValue;
|
||||
public readonly ulong fenceValue;
|
||||
|
||||
public ReleaseEntry(ResourceRecord record, uint fenceValue)
|
||||
public ReleaseEntry(ResourceRecord record, ulong fenceValue)
|
||||
{
|
||||
this.record = record;
|
||||
this.fenceValue = fenceValue;
|
||||
@@ -108,7 +108,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
private UnsafeQueue<ReleaseEntry> _releaseQueue;
|
||||
|
||||
private uint _currentFrameFenceValue;
|
||||
private ulong _currentFrame;
|
||||
private bool _disposed;
|
||||
|
||||
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
|
||||
@@ -136,7 +136,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
if (pResource == null)
|
||||
{
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debugger.Break();
|
||||
Debugger.Break();
|
||||
#endif
|
||||
return Handle<GPUResource>.Invalid;
|
||||
}
|
||||
@@ -155,13 +155,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
return handle;
|
||||
}
|
||||
|
||||
public unsafe Handle<GPUResource> AddAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData initialBarrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
|
||||
internal unsafe Handle<GPUResource> AddAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData initialBarrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
if (allocation == null)
|
||||
{
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debugger.Break();
|
||||
Debugger.Break();
|
||||
#endif
|
||||
return Handle<GPUResource>.Invalid;
|
||||
}
|
||||
@@ -288,7 +288,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
return;
|
||||
}
|
||||
|
||||
var entry = new ReleaseEntry(record, _currentFrameFenceValue);
|
||||
var entry = new ReleaseEntry(record, _currentFrame);
|
||||
|
||||
_releaseQueue.Enqueue(entry);
|
||||
_resources.Remove(handle.ID, handle.Generation);
|
||||
@@ -359,20 +359,20 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
public void BeginFrame(uint currentFrameFenceValue)
|
||||
public void BeginFrame(ulong currentFrame)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
_currentFrameFenceValue = currentFrameFenceValue;
|
||||
_currentFrame = currentFrame;
|
||||
}
|
||||
|
||||
public void EndFrame(uint completedFenceValue)
|
||||
public void EndFrame(ulong completedFrame)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
while (_releaseQueue.Count > 0)
|
||||
{
|
||||
var toRelease = _releaseQueue.Peek();
|
||||
if (toRelease.fenceValue > completedFenceValue)
|
||||
if (toRelease.fenceValue > completedFrame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -12,14 +12,11 @@ using static TerraFX.Aliases.DXGI_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of swap chain interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12SwapChain : ISwapChain
|
||||
internal unsafe class DXGISwapChain : ISwapChain
|
||||
{
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
private readonly D3D12RenderDevice _renderDevice;
|
||||
private readonly D3D12RenderDevice _device;
|
||||
|
||||
private UniquePtr<IDXGISwapChain4> _swapChain;
|
||||
private UnsafeArray<Handle<Texture>> _backBuffers;
|
||||
@@ -48,37 +45,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc, uint bufferCount)
|
||||
{
|
||||
Debug.Assert(bufferCount >= 2);
|
||||
|
||||
_resourceDatabase = resourceDatabase;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
_renderDevice = device;
|
||||
|
||||
_backBuffers = new UnsafeArray<Handle<Texture>>((int)bufferCount, Allocator.Persistent);
|
||||
|
||||
Width = desc.Width;
|
||||
Height = desc.Height;
|
||||
|
||||
var pSwapChian = CreateSwapChain(desc, bufferCount);
|
||||
_swapChain.Attach(pSwapChian);
|
||||
|
||||
CreateBackBuffers();
|
||||
SetScale(desc.ScaleX, desc.ScaleY);
|
||||
|
||||
if (desc.Target.Type == SwapChainTargetType.Composition)
|
||||
{
|
||||
_compositionSurface = desc.Target.CompositionSurface;
|
||||
}
|
||||
}
|
||||
|
||||
~D3D12SwapChain()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private IDXGISwapChain4* CreateSwapChain(SwapChainDesc desc, uint bufferCount)
|
||||
private static IDXGISwapChain4* CreateSwapChain(D3D12RenderDevice device, SwapChainDesc desc, uint bufferCount)
|
||||
{
|
||||
var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1
|
||||
{
|
||||
@@ -97,8 +64,8 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
|
||||
IDXGISwapChain1* pTempSwapChain = default;
|
||||
|
||||
var pFactory = _renderDevice.DXGIFactory.Get();
|
||||
var pCommandQueue = _renderDevice.NativeGraphicsQueue.Get();
|
||||
var pFactory = device.DXGIFactory.Get();
|
||||
var pCommandQueue = device.NativeGraphicsQueue.Get();
|
||||
|
||||
switch (desc.Target.Type)
|
||||
{
|
||||
@@ -139,6 +106,36 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
return pSwapChain;
|
||||
}
|
||||
|
||||
public DXGISwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc, uint bufferCount)
|
||||
{
|
||||
Debug.Assert(bufferCount >= 2);
|
||||
|
||||
_resourceDatabase = resourceDatabase;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
_device = device;
|
||||
|
||||
var pSfwapChain = CreateSwapChain(device, desc, bufferCount);
|
||||
_swapChain.Attach(pSfwapChain);
|
||||
|
||||
_backBuffers = new UnsafeArray<Handle<Texture>>((int)bufferCount, Allocator.Persistent);
|
||||
|
||||
Width = desc.Width;
|
||||
Height = desc.Height;
|
||||
|
||||
CreateBackBuffers();
|
||||
SetScale(desc.ScaleX, desc.ScaleY);
|
||||
|
||||
if (desc.Target.Type == SwapChainTargetType.Composition)
|
||||
{
|
||||
_compositionSurface = desc.Target.CompositionSurface;
|
||||
}
|
||||
}
|
||||
|
||||
~DXGISwapChain()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void CreateBackBuffers()
|
||||
{
|
||||
for (uint i = 0; i < _backBuffers.Count; i++)
|
||||
@@ -149,7 +146,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
|
||||
var rtv = _descriptorAllocator.AllocateRTV();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(rtv);
|
||||
_renderDevice.NativeDevice.Get()->CreateRenderTargetView(pBackBuffer, null, cpuHandle);
|
||||
_device.NativeObject.Get()->CreateRenderTargetView(pBackBuffer, null, cpuHandle);
|
||||
|
||||
var view = ResourceViewGroup.Invalid with
|
||||
{
|
||||
@@ -68,7 +68,7 @@ internal static unsafe class D3D12Utility
|
||||
if (ptr != null)
|
||||
{
|
||||
var refCount = ptr->Release();
|
||||
Debug.Assert((refCount != 0));
|
||||
Debug.Assert(refCount != 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user