diff --git a/src/Runtime/Ghost.Engine/Systems/RenderExtractionSystem.cs b/src/Runtime/Ghost.Engine/Systems/RenderExtractionSystem.cs index 210fe08..6941944 100644 --- a/src/Runtime/Ghost.Engine/Systems/RenderExtractionSystem.cs +++ b/src/Runtime/Ghost.Engine/Systems/RenderExtractionSystem.cs @@ -5,7 +5,6 @@ using Ghost.Graphics; using Ghost.Graphics.Core; using Ghost.Graphics.RHI; using Misaki.HighPerformance.LowLevel.Buffer; -using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics.Geometry; @@ -79,8 +78,6 @@ public class RenderExtractionSystem : ISystem ref var cameraQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_cameraQueryID); ref var meshQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_meshQueryID); - var renderRequests = new UnsafeList(cameraQuery.CalculateEntityCount(), Allocator.Temp); - // TODO: We should extract the render record for each camera because different cameras may have different culling results. foreach (var (cam, camLtw) in cameraQuery.GetComponentIterator()) { @@ -240,10 +237,8 @@ public class RenderExtractionSystem : ISystem }, }; - renderRequests.Add(request); + _renderSystem.AddRenderRequest(request); } - - _renderPipeline.Render(new RenderContext(), renderRequests.AsSpan()); } public void Cleanup(ref readonly SystemAPI systemAPI) diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandAllocator.cs b/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandAllocator.cs index 318e5ea..bf84c44 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandAllocator.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandAllocator.cs @@ -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, ICommandAllocator { - private UniquePtr _allocator; - - public SharedPtr 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(); - } -} \ No newline at end of file +} diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandBuffer.cs b/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandBuffer.cs index 777b4e4..1aac2d4 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandBuffer.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandBuffer.cs @@ -14,10 +14,8 @@ using static TerraFX.Aliases.DXGI_Alias; namespace Ghost.Graphics.D3D12; -internal unsafe class D3D12CommandBuffer : ICommandBuffer +internal unsafe class D3D12CommandBuffer : D3D12Object, ICommandBuffer { - private UniquePtr _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 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 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> renderTargets, Handle 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 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 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 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 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 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 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 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 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(Handle buffer, params ReadOnlySpan 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, params ReadOnlySpan 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 dst, Handle 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 dst, TextureRegion? dstRegion, Handle 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); } } diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandQueue.cs b/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandQueue.cs index 13b35d5..e4ba11d 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandQueue.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/D3D12CommandQueue.cs @@ -10,28 +10,20 @@ namespace Ghost.Graphics.D3D12; /// /// D3D12 implementation of command queue interface /// -internal unsafe class D3D12CommandQueue : ICommandQueue +internal unsafe class D3D12CommandQueue : D3D12Object, ICommandQueue { - private UniquePtr _commandQueue; private UniquePtr _fence; private readonly AutoResetEvent _fenceEvent; private ulong _fenceValue; - private bool _disposed; public CommandQueueType Type { get; } - public SharedPtr 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 commandBuffers) { - Debug.Assert(!_disposed); + AssertNotDisposed(); Span 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); } } diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12DescriptorHeap.cs b/src/Runtime/Ghost.Graphics.D3D12/D3D12DescriptorHeap.cs index 129e493..0268131 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/D3D12DescriptorHeap.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/D3D12DescriptorHeap.cs @@ -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(); } -} \ No newline at end of file +} diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12GraphicsEngine.cs b/src/Runtime/Ghost.Graphics.D3D12/D3D12GraphicsEngine.cs index 489063a..7ab0bde 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/D3D12GraphicsEngine.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/D3D12GraphicsEngine.cs @@ -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 _renderers; + private readonly Queue _commandBufferPool; + private readonly Queue _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.Empty; - - _pipelineLibrary.InitializeLibrary(null); + _commandBufferPool = new Queue(4); + _commandBufferReturnQueue = new Queue(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(); diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12Object.cs b/src/Runtime/Ghost.Graphics.D3D12/D3D12Object.cs new file mode 100644 index 0000000..90b3338 --- /dev/null +++ b/src/Runtime/Ghost.Graphics.D3D12/D3D12Object.cs @@ -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: IRHIObject + where T : unmanaged, ID3D12Object.Interface +{ + private UniquePtr _nativeObject; + private string _name = string.Empty; + + protected T* pNativeObject => _nativeObject.Get(); + public SharedPtr 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); + } +} + diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12PipelineLibrary.cs b/src/Runtime/Ghost.Graphics.D3D12/D3D12PipelineLibrary.cs index 290c590..87dded6 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/D3D12PipelineLibrary.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/D3D12PipelineLibrary.cs @@ -26,29 +26,48 @@ internal struct D3D12PipelineState : IDisposable } } -internal unsafe class D3D12PipelineLibrary : IPipelineLibrary +internal unsafe class D3D12PipelineLibrary : D3D12Object, IPipelineLibrary { private readonly D3D12RenderDevice _device; private readonly D3D12ResourceDatabase _resourceDatabase; - private UniquePtr _library; private UniquePtr _defaultRootSignature; - private readonly Dictionary, D3D12PipelineState> _pipelineCache; + private UnsafeHashMap, 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, D3D12PipelineState>(); + _pipelineCache = new UnsafeHashMap, 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((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 key) { + AssertNotDisposed(); return _pipelineCache.ContainsKey(key); } public Result, Error> GetGraphicsPSO(Key128 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(); } } diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12RenderDevice.cs b/src/Runtime/Ghost.Graphics.D3D12/D3D12RenderDevice.cs index 8a43785..f58780e 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/D3D12RenderDevice.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/D3D12RenderDevice.cs @@ -9,12 +9,8 @@ using static TerraFX.Aliases.DXGI_Alias; namespace Ghost.Graphics.D3D12; -/// -/// D3D12 implementation of the render device interface -/// -internal unsafe class D3D12RenderDevice : IRenderDevice +internal unsafe class D3D12RenderDevice : D3D12Object, IRenderDevice { - private UniquePtr _device; private UniquePtr _dxgiFactory; private UniquePtr _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 DXGIFactory => _dxgiFactory.Share(); - public SharedPtr NativeDevice => _device.Share(); public SharedPtr Adapter => _adapter.Share(); - public SharedPtr NativeGraphicsQueue => _graphicsQueue.NativeQueue; - public SharedPtr NativeComputeQueue => _computeQueue.NativeQueue; - public SharedPtr NativeCopyQueue => _copyQueue.NativeQueue; + public SharedPtr NativeGraphicsQueue => _graphicsQueue.NativeObject; + public SharedPtr NativeComputeQueue => _computeQueue.NativeObject; + public SharedPtr 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); } } diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12ResourceAllocator.cs b/src/Runtime/Ghost.Graphics.D3D12/D3D12ResourceAllocator.cs index a212010..a21c322 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/D3D12ResourceAllocator.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/D3D12ResourceAllocator.cs @@ -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); diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12ResourceDatabase.cs b/src/Runtime/Ghost.Graphics.D3D12/D3D12ResourceDatabase.cs index 421a167..6aed453 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/D3D12ResourceDatabase.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/D3D12ResourceDatabase.cs @@ -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 _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.Invalid; } @@ -155,13 +155,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase return handle; } - public unsafe Handle AddAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData initialBarrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null) + internal unsafe Handle 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.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; } diff --git a/src/Runtime/Ghost.Graphics.D3D12/D3D12SwapChain.cs b/src/Runtime/Ghost.Graphics.D3D12/DXGISwapChain.cs similarity index 89% rename from src/Runtime/Ghost.Graphics.D3D12/D3D12SwapChain.cs rename to src/Runtime/Ghost.Graphics.D3D12/DXGISwapChain.cs index 4e84cae..03df9cb 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/D3D12SwapChain.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/DXGISwapChain.cs @@ -12,14 +12,11 @@ using static TerraFX.Aliases.DXGI_Alias; namespace Ghost.Graphics.D3D12; -/// -/// D3D12 implementation of swap chain interface -/// -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 _swapChain; private UnsafeArray> _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>((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>((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 { diff --git a/src/Runtime/Ghost.Graphics.D3D12/Utilities/D3D12Utility.cs b/src/Runtime/Ghost.Graphics.D3D12/Utilities/D3D12Utility.cs index 6e5052a..5672ecb 100644 --- a/src/Runtime/Ghost.Graphics.D3D12/Utilities/D3D12Utility.cs +++ b/src/Runtime/Ghost.Graphics.D3D12/Utilities/D3D12Utility.cs @@ -68,7 +68,7 @@ internal static unsafe class D3D12Utility if (ptr != null) { var refCount = ptr->Release(); - Debug.Assert((refCount != 0)); + Debug.Assert(refCount != 0); } } diff --git a/src/Runtime/Ghost.Graphics.RHI/Common.cs b/src/Runtime/Ghost.Graphics.RHI/Common.cs index 74c9289..2d06944 100644 --- a/src/Runtime/Ghost.Graphics.RHI/Common.cs +++ b/src/Runtime/Ghost.Graphics.RHI/Common.cs @@ -627,7 +627,7 @@ public struct BarrierDesc } } -public struct ResourceDesc +public record struct ResourceDesc { [StructLayout(LayoutKind.Explicit)] internal struct resource_union @@ -638,7 +638,7 @@ public struct ResourceDesc public BufferDesc bufferDescription; } - internal resource_union _desc; + private resource_union _desc; public ResourceType Type { @@ -682,6 +682,31 @@ public struct ResourceDesc TextureDescription = desc }; } + + public bool Equals(ResourceDesc other) + { + if (Type != other.Type) + { + return false; + } + + return Type switch + { + ResourceType.Texture => TextureDescription.Equals(other.TextureDescription), + ResourceType.Buffer => BufferDescription.Equals(other.BufferDescription), + _ => throw new InvalidOperationException($"Unknown resource type: {Type}") + }; + } + + public override int GetHashCode() + { + return Type switch + { + ResourceType.Texture => HashCode.Combine(Type, TextureDescription), + ResourceType.Buffer => HashCode.Combine(Type, BufferDescription), + _ => throw new InvalidOperationException($"Unknown resource type: {Type}") + }; + } } /// @@ -831,7 +856,7 @@ public struct RenderTargetDesc /// /// Texture description /// -public struct TextureDesc +public record struct TextureDesc { /// /// Width of the texture @@ -939,7 +964,7 @@ public record struct SamplerDesc } } -public struct BufferDesc +public record struct BufferDesc { public ulong Size { diff --git a/src/Runtime/Ghost.Graphics.RHI/IGraphicsEngine.cs b/src/Runtime/Ghost.Graphics.RHI/IGraphicsEngine.cs index eeb3e61..35e61d4 100644 --- a/src/Runtime/Ghost.Graphics.RHI/IGraphicsEngine.cs +++ b/src/Runtime/Ghost.Graphics.RHI/IGraphicsEngine.cs @@ -37,25 +37,6 @@ public interface IGraphicsEngine : IDisposable get; } - /// - /// Creates a new instance of a renderer for drawing graphical content. - /// - /// An object that implements the IRenderer interface, which can be used to render graphics. - IRenderer CreateRenderer(); - - /// - /// Removes the specified renderer from the collection of active renderers. - /// - /// The renderer instance to remove. - void RemoveRenderer(IRenderer renderer); - - /// - /// Removes all registered renderers from the collection. - /// - /// Call this method to reset the renderer collection to an empty state. After calling this - /// method, no renderers will be available until new ones are added. - void ClearRenderers(); - /// /// Creates a new command allocator for the specified command buffer space. /// @@ -70,6 +51,19 @@ public interface IGraphicsEngine : IDisposable /// A new command buffer instance ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics); + /// + /// Gets a command buffer from the pool for recording rendering commands. + /// + /// Type of command buffer to get from the pool + /// A command buffer instance from the pool + ICommandBuffer GetPooledCommandBuffer(CommandBufferType type = CommandBufferType.Graphics); + + /// + /// Returns a command buffer to the pool after use. + /// + /// The command buffer to return to the pool + void ReturnPooledCommandBuffer(ICommandBuffer commandBuffer); + /// /// Creates a swap chain for presentation /// @@ -80,16 +74,14 @@ public interface IGraphicsEngine : IDisposable /// /// Begin the current frame. /// - /// CPU fence value for synchronization - /// GPU fence value for synchronization + /// CPU fence value for synchronization /// Result of the begin frame operation - Result BeginFrame(uint cpuFenceValue, uint gpuFenceValue); + Result BeginFrame(ulong currentFrame); /// /// End the current frame. /// - /// CPU fence value for synchronization - /// GPU fence value for synchronization + /// GPU fence value for synchronization /// Result of the end frame operation - Result EndFrame(uint cpuFenceValue, uint gpuFenceValue); + Result EndFrame(ulong completedFrame); } diff --git a/src/Runtime/Ghost.Graphics.RHI/IPipelineLibrary.cs b/src/Runtime/Ghost.Graphics.RHI/IPipelineLibrary.cs index 1daaf39..0932189 100644 --- a/src/Runtime/Ghost.Graphics.RHI/IPipelineLibrary.cs +++ b/src/Runtime/Ghost.Graphics.RHI/IPipelineLibrary.cs @@ -4,11 +4,6 @@ namespace Ghost.Graphics.RHI; public interface IPipelineLibrary : IDisposable { - /// - /// Load pipeline library from disk. - /// - /// File path. If null, load default library. - void InitializeLibrary(string? filePath); void SaveLibraryToDisk(string filePath); bool HasPipeline(Key128 key); Result> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled); diff --git a/src/Runtime/Ghost.Graphics.RHI/IRHIObject.cs b/src/Runtime/Ghost.Graphics.RHI/IRHIObject.cs new file mode 100644 index 0000000..4df1ad0 --- /dev/null +++ b/src/Runtime/Ghost.Graphics.RHI/IRHIObject.cs @@ -0,0 +1,6 @@ +namespace Ghost.Graphics.RHI; + +public interface IRHIObject: IDisposable +{ + string Name { get; set; } +} diff --git a/src/Runtime/Ghost.Graphics.RHI/IRenderer.cs b/src/Runtime/Ghost.Graphics.RHI/IRenderer.cs index 406dda3..0bee8a0 100644 --- a/src/Runtime/Ghost.Graphics.RHI/IRenderer.cs +++ b/src/Runtime/Ghost.Graphics.RHI/IRenderer.cs @@ -5,6 +5,9 @@ namespace Ghost.Graphics.RHI; public readonly struct RenderContext { public ICommandBuffer CommandBuffer { get; init; } + public ICommandQueue GraphicsQueue { get; init; } + public ICommandQueue ComputeQueue { get; init; } + public ICommandQueue CopyQueue { get; init; } } // TODO: We may don't need this anymore. We Use RenderExtractionSystem to extract render data from entities and pass them to IRenderPipeline to render. diff --git a/src/Runtime/Ghost.Graphics/Core/RenderRequest.cs b/src/Runtime/Ghost.Graphics/Core/RenderRequest.cs index c63b992..4bc568a 100644 --- a/src/Runtime/Ghost.Graphics/Core/RenderRequest.cs +++ b/src/Runtime/Ghost.Graphics/Core/RenderRequest.cs @@ -34,12 +34,12 @@ public struct Frustum public static void CalculateFrustumPlanes(float4x4 finalMatrix, ref plane_array outPlanes) { - const int kPlaneFrustumLeft = 0; - const int kPlaneFrustumRight = 1; - const int kPlaneFrustumBottom = 2; - const int kPlaneFrustumTop = 3; - const int kPlaneFrustumNear = 4; - const int kPlaneFrustumFar = 5; + const int planeFrustumLeft = 0; + const int planeFrustumRight = 1; + const int planeFrustumBottom = 2; + const int planeFrustumTop = 3; + const int planeFrustumNear = 4; + const int planeFrustumFar = 5; float4 tmpVec = default; float4 otherVec = default; @@ -66,8 +66,8 @@ public struct Frustum leftNormalY *= leftInvMagnitude; leftNormalZ *= leftInvMagnitude; leftDistance *= leftInvMagnitude; - outPlanes[kPlaneFrustumLeft].xyz = new float3(leftNormalX, leftNormalY, leftNormalZ); - outPlanes[kPlaneFrustumLeft].w = leftDistance; + outPlanes[planeFrustumLeft].xyz = new float3(leftNormalX, leftNormalY, leftNormalZ); + outPlanes[planeFrustumLeft].w = leftDistance; var rightNormalX = -otherVec[0] + tmpVec[0]; var rightNormalY = -otherVec[1] + tmpVec[1]; @@ -80,8 +80,8 @@ public struct Frustum rightNormalY *= rightInvMagnitude; rightNormalZ *= rightInvMagnitude; rightDistance *= rightInvMagnitude; - outPlanes[kPlaneFrustumRight].xyz = new float3(rightNormalX, rightNormalY, rightNormalZ); - outPlanes[kPlaneFrustumRight].w = rightDistance; + outPlanes[planeFrustumRight].xyz = new float3(rightNormalX, rightNormalY, rightNormalZ); + outPlanes[planeFrustumRight].w = rightDistance; // bottom & top otherVec[0] = finalMatrix[0][1]; @@ -100,8 +100,8 @@ public struct Frustum bottomNormalY *= bottomInvMagnitude; bottomNormalZ *= bottomInvMagnitude; bottomDistance *= bottomInvMagnitude; - outPlanes[kPlaneFrustumBottom].xyz = new float3(bottomNormalX, bottomNormalY, bottomNormalZ); - outPlanes[kPlaneFrustumBottom].w = bottomDistance; + outPlanes[planeFrustumBottom].xyz = new float3(bottomNormalX, bottomNormalY, bottomNormalZ); + outPlanes[planeFrustumBottom].w = bottomDistance; var topNormalX = -otherVec[0] + tmpVec[0]; var topNormalY = -otherVec[1] + tmpVec[1]; @@ -114,8 +114,8 @@ public struct Frustum topNormalY *= topInvMagnitude; topNormalZ *= topInvMagnitude; topDistance *= topInvMagnitude; - outPlanes[kPlaneFrustumTop].xyz = new float3(topNormalX, topNormalY, topNormalZ); - outPlanes[kPlaneFrustumTop].w = topDistance; + outPlanes[planeFrustumTop].xyz = new float3(topNormalX, topNormalY, topNormalZ); + outPlanes[planeFrustumTop].w = topDistance; // near & far otherVec[0] = finalMatrix[0][2]; @@ -134,8 +134,8 @@ public struct Frustum nearNormalY *= nearInvMagnitude; nearNormalZ *= nearInvMagnitude; nearDistance *= nearInvMagnitude; - outPlanes[kPlaneFrustumNear].xyz = new float3(nearNormalX, nearNormalY, nearNormalZ); - outPlanes[kPlaneFrustumNear].w = nearDistance; + outPlanes[planeFrustumNear].xyz = new float3(nearNormalX, nearNormalY, nearNormalZ); + outPlanes[planeFrustumNear].w = nearDistance; var farNormalX = -otherVec[0] + tmpVec[0]; var farNormalY = -otherVec[1] + tmpVec[1]; @@ -148,8 +148,8 @@ public struct Frustum farNormalY *= farInvMagnitude; farNormalZ *= farInvMagnitude; farDistance *= farInvMagnitude; - outPlanes[kPlaneFrustumFar].xyz = new float3(farNormalX, farNormalY, farNormalZ); - outPlanes[kPlaneFrustumFar].w = farDistance; + outPlanes[planeFrustumFar].xyz = new float3(farNormalX, farNormalY, farNormalZ); + outPlanes[planeFrustumFar].w = farDistance; } } @@ -177,7 +177,7 @@ public struct RenderView public RenderingLayerMask renderingLayerMask; } -public unsafe struct RenderRequest +public unsafe struct RenderRequest: IDisposable { public RenderView view; @@ -189,4 +189,11 @@ public unsafe struct RenderRequest public RenderList shadowCasterRenderList; public delegate* renderFunc; + + public void Dispose() + { + opaqueRenderList.Dispose(); + transparentRenderList.Dispose(); + shadowCasterRenderList.Dispose(); + } } diff --git a/src/Runtime/Ghost.Graphics/RenderPipeline/GhostRenderPipeline.Test.cs b/src/Runtime/Ghost.Graphics/RenderPipeline/GhostRenderPipeline.Test.cs index a2c489f..b5c5850 100644 --- a/src/Runtime/Ghost.Graphics/RenderPipeline/GhostRenderPipeline.Test.cs +++ b/src/Runtime/Ghost.Graphics/RenderPipeline/GhostRenderPipeline.Test.cs @@ -47,7 +47,7 @@ public partial class GhostRenderPipeline private readonly uint _padding1; private readonly uint _padding2; } - +#if flase private void RenderTest(RenderGraph graph, Identifier backbuffer) { Identifier renderTarget; @@ -107,4 +107,5 @@ public partial class GhostRenderPipeline }); } } +# endif } diff --git a/src/Runtime/Ghost.Graphics/RenderSystem.cs b/src/Runtime/Ghost.Graphics/RenderSystem.cs index a5fc82a..0c277d0 100644 --- a/src/Runtime/Ghost.Graphics/RenderSystem.cs +++ b/src/Runtime/Ghost.Graphics/RenderSystem.cs @@ -1,7 +1,10 @@ using Ghost.Core; +using Ghost.Graphics.Core; using Ghost.Graphics.D3D12; using Ghost.Graphics.RenderPipeline; using Ghost.Graphics.RHI; +using Misaki.HighPerformance.LowLevel.Buffer; +using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.Mathematics; using System.Collections.Concurrent; using System.Diagnostics; @@ -70,14 +73,16 @@ public class RenderSystem : IDisposable private readonly FrameResource[] _frameResources; private readonly Thread _renderThread; private readonly AutoResetEvent _shutdownEvent; + + private UnsafeArray> _renderRequests; private readonly ConcurrentDictionary _resizeRequest; private IRenderPipelineSettings _renderPipelineSettings; private IRenderPipeline _renderPipeline; private uint _frameIndex; - private uint _cpuFenceValue; - private uint _gpuFenceValue; + private ulong _cpuFenceValue; + private ulong _gpuFenceValue; private bool _isRunning; private bool _disposed; @@ -86,8 +91,8 @@ public class RenderSystem : IDisposable public ResourceManager ResourceManager => _resourceManager; public bool IsRunning => _isRunning; - public uint CPUFenceValue => _cpuFenceValue; - public uint GPUFenceValue => _gpuFenceValue; + public ulong CPUFenceValue => _cpuFenceValue; + public ulong GPUFenceValue => _gpuFenceValue; public uint FrameIndex => _frameIndex; public uint MaxFrameLatency => _config.FrameBufferCount; @@ -160,6 +165,11 @@ public class RenderSystem : IDisposable _shutdownEvent = new AutoResetEvent(false); _resizeRequest = new ConcurrentDictionary(); + _renderRequests = new UnsafeArray>((int)desc.FrameBufferCount, Allocator.Persistent); + for (var i = 0; i < desc.FrameBufferCount; i++) + { + _renderRequests[i] = new UnsafeList(2, Allocator.Persistent); + } _renderPipelineSettings = new GhostRenderPipelineSettings(); _renderPipeline = _renderPipelineSettings.CreatePipeline(this); @@ -191,7 +201,7 @@ public class RenderSystem : IDisposable while (_isRunning) { - _frameIndex = _gpuFenceValue % _config.FrameBufferCount; + _frameIndex = (uint)(_gpuFenceValue % _config.FrameBufferCount); ref var frameResource = ref _frameResources[_frameIndex]; // Wait for either CPU ready signal or shutdown signal @@ -214,7 +224,6 @@ public class RenderSystem : IDisposable if (!_resizeRequest.IsEmpty) { - //WaitIdle(); _gpuFenceValue++; var flushFence = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue); _graphicsEngine.Device.GraphicsQueue.WaitForValue(flushFence); @@ -241,28 +250,65 @@ public class RenderSystem : IDisposable continue; // Skip rendering this frame since we just resized and may have invalid render targets } + // Begin rendering for this frame frameResource.CommandAllocator.Reset(); - var r = _graphicsEngine.BeginFrame(_cpuFenceValue, _gpuFenceValue); + _resourceManager.BeginFrame(_cpuFenceValue); + var r = _graphicsEngine.BeginFrame(_cpuFenceValue); + if (r.IsFailure) { StopRenderLoop(r); + break; } - var cmd = _graphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics); + // Start recording commands + + // TODO: How can we support async compute and async copy? + var cmd = _graphicsEngine.GetPooledCommandBuffer(CommandBufferType.Graphics); + cmd.Begin(frameResource.CommandAllocator); + var renderCtx = new RenderContext { - CommandBuffer = cmd, + CommandBuffer = cmd }; - _renderPipeline.Render(renderCtx, default); + ref var renderRequests = ref _renderRequests[_frameIndex]; + _renderPipeline.Render(renderCtx, renderRequests.AsSpan()); + + // End recording commands and submit + r = cmd.End(); + if (r.IsFailure) + { + _graphicsEngine.ReturnPooledCommandBuffer(cmd); + StopRenderLoop(r); + break; + } + + _graphicsEngine.Device.GraphicsQueue.Submit(cmd); + _graphicsEngine.ReturnPooledCommandBuffer(cmd); + + // End the frame and present + _resourceManager.EndFrame(_cpuFenceValue); + r = _graphicsEngine.EndFrame(_gpuFenceValue); - r = _graphicsEngine.EndFrame(_cpuFenceValue, _gpuFenceValue); if (r.IsFailure) { StopRenderLoop(r); + break; } + // TODO: Present here. + + + // Prepare for the next frame. + for (var i = 0; i < renderRequests.Count; i++) + { + renderRequests[i].Dispose(); + } + + renderRequests.Clear(); + _gpuFenceValue++; frameResource.GpuReadyEvent.Set(); @@ -312,6 +358,14 @@ public class RenderSystem : IDisposable _resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize); } + public void AddRenderRequest(in RenderRequest request) + { + Debug.Assert(!_disposed, "Cannot add render request to a disposed RenderSystem."); + + var frameIndex = (int)(_cpuFenceValue % _config.FrameBufferCount); + _renderRequests[frameIndex].Add(request); + } + public bool WaitForGPUReady(int timeOut = -1) { Debug.Assert(!_disposed, "Cannot wait for GPU ready on a disposed RenderSystem."); @@ -346,6 +400,16 @@ public class RenderSystem : IDisposable frameResource.Dispose(); } + foreach (ref var renderRequestList in _renderRequests) + { + foreach (ref var request in renderRequestList) + { + request.Dispose(); + } + + renderRequestList.Dispose(); + } + _graphicsEngine.Dispose(); _shutdownEvent.Dispose(); diff --git a/src/Runtime/Ghost.Graphics/ResourceManager.cs b/src/Runtime/Ghost.Graphics/ResourceManager.cs index eee6138..f3e36d6 100644 --- a/src/Runtime/Ghost.Graphics/ResourceManager.cs +++ b/src/Runtime/Ghost.Graphics/ResourceManager.cs @@ -4,11 +4,24 @@ using Ghost.Graphics.Core; using Ghost.Graphics.RHI; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; +using System.Diagnostics; namespace Ghost.Graphics; public sealed class ResourceManager : IDisposable { + private readonly struct ResourceReturnEntry + { + public readonly Handle handle; + public readonly ulong returnFrame; + + public ResourceReturnEntry(Handle handle, ulong returnFrame) + { + this.handle = handle; + this.returnFrame = returnFrame; + } + } + private readonly IResourceAllocator _resourceAllocator; private readonly IResourceDatabase _resourceDatabase; @@ -18,6 +31,11 @@ public sealed class ResourceManager : IDisposable private readonly MaterialPaletteStore _materialPalettes; + private ulong _currentFrame; + + private UnsafeHashMap>> _resourceCache; + private UnsafeQueue _resourceReturnQueue; + private bool _disposed; public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase) @@ -28,7 +46,11 @@ public sealed class ResourceManager : IDisposable _meshes = new UnsafeSlotMap(64, Allocator.Persistent); _materials = new UnsafeSlotMap(64, Allocator.Persistent); _shaders = new UnsafeList(16, Allocator.Persistent); + _materialPalettes = new MaterialPaletteStore(); + + _resourceCache = new UnsafeHashMap>>(32, Allocator.Persistent); + _resourceReturnQueue = new UnsafeQueue(32, Allocator.Persistent); } ~ResourceManager() @@ -36,6 +58,32 @@ public sealed class ResourceManager : IDisposable Dispose(); } + internal void BeginFrame(ulong currentFrame) + { + Debug.Assert(!_disposed); + _currentFrame = currentFrame; + } + + internal void EndFrame(ulong completedFrame) + { + Debug.Assert(!_disposed); + + while (_resourceReturnQueue.TryPeek(out var entry) && entry.returnFrame <= completedFrame) + { + _resourceReturnQueue.Dequeue(); + var result = _resourceDatabase.GetResourceDescription(entry.handle); + Debug.Assert(result.IsSuccess); + + ref var queue = ref _resourceCache.GetValueRefOrAddDefault(result.Value, out var exist); + if (!exist) + { + queue = new UnsafeQueue>(4, Allocator.Persistent); + } + + queue.Enqueue(entry.handle); + } + } + /// /// Creates a new mesh from the specified vertex and index data. /// @@ -44,7 +92,7 @@ public sealed class ResourceManager : IDisposable /// An representing the newly created mesh. public unsafe Handle CreateMesh(UnsafeList vertices, UnsafeList indices) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); var vertexBufferDesc = new BufferDesc { @@ -94,7 +142,7 @@ public sealed class ResourceManager : IDisposable /// An representing the newly created material. public Handle CreateMaterial(Identifier shader) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); var material = new Material(); if (material.SetShader(shader, this, _resourceDatabase, _resourceAllocator) != Error.None) @@ -113,7 +161,7 @@ public sealed class ResourceManager : IDisposable /// The viewGroup containing the shader's properties and passes. public Identifier CreateGraphicsShader(ShaderDescriptor descriptor) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); var shader = new Shader(descriptor); @@ -129,7 +177,7 @@ public sealed class ResourceManager : IDisposable /// true if a mesh with the specified Handle exists; otherwise, false. public bool HasMesh(Handle handle) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); return _meshes.Contains(handle.ID, handle.Generation); } @@ -155,7 +203,7 @@ public sealed class ResourceManager : IDisposable /// The handle of the mesh to release. Must refer to a mesh that was previously created and not already released. public void ReleaseMesh(Handle handle) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); if (!_meshes.TryGetElementAt(handle.ID, handle.Generation, out var mesh)) { @@ -173,7 +221,7 @@ public sealed class ResourceManager : IDisposable /// true if a material with the specified handle exists; otherwise, false. public bool HasMaterial(Handle handle) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); return _materials.Contains(handle.ID, handle.Generation); } @@ -199,7 +247,7 @@ public sealed class ResourceManager : IDisposable /// The handle of the material to release. Must refer to a material that has been previously acquired. public void ReleaseMaterial(Handle handle) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); var material = _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist); if (!exist) @@ -218,7 +266,7 @@ public sealed class ResourceManager : IDisposable /// The palette index. Index 0 represents an empty palette. public int GetOrCreateMaterialPalette(ReadOnlySpan> materials) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); foreach (var material in materials) { @@ -237,7 +285,7 @@ public sealed class ResourceManager : IDisposable /// The palette index to validate. public bool HasMaterialPalette(Identifier paletteID) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); return _materialPalettes.IsValid(paletteID); } @@ -247,7 +295,7 @@ public sealed class ResourceManager : IDisposable /// The palette index to query. public MaterialPalette GetMaterialPaletteInfo(Identifier paletteID) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); return _materialPalettes.GetInfo(paletteID); } @@ -258,7 +306,7 @@ public sealed class ResourceManager : IDisposable /// The material slot inside the palette. public Handle GetMaterialPaletteMaterial(Identifier paletteID, int localMaterialIndex) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); return _materialPalettes.GetMaterial(paletteID, localMaterialIndex); } @@ -268,7 +316,7 @@ public sealed class ResourceManager : IDisposable /// The palette index to release. public void ReleaseMaterialPalette(Identifier paletteID) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); _materialPalettes.Release(paletteID); } @@ -279,7 +327,7 @@ public sealed class ResourceManager : IDisposable /// true if a shader with the specified identifier exists; otherwise, false. public bool HasShader(Identifier id) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); return id.Value >= 0 && id.Value < _shaders.Count; } @@ -304,7 +352,7 @@ public sealed class ResourceManager : IDisposable /// The identifier of the shader to release. Must refer to a valid, previously created shader. public void ReleaseShader(Identifier id) { - ObjectDisposedException.ThrowIf(_disposed, this); + Debug.Assert(!_disposed); if (!HasShader(id)) { @@ -315,6 +363,38 @@ public sealed class ResourceManager : IDisposable shader.ReleaseResource(_resourceDatabase); } + public Handle GetPooledResource(in ResourceDesc desc) + { + Debug.Assert(!_disposed); + + ref var queue = ref _resourceCache.GetValueRef(desc, out var exist); + if (exist && queue.TryDequeue(out Handle handle)) + { + return handle; + } + + handle = desc.Type switch + { + ResourceType.Buffer => _resourceAllocator.CreateBuffer(in desc.BufferDescription, "PooledBuffer").AsResource(), + ResourceType.Texture => _resourceAllocator.CreateTexture(in desc.TextureDescription, "PooledTexture").AsResource(), + _ => throw new ArgumentException("Invalid resource type.", nameof(desc)), + }; + + return handle; + } + + public void ReturnPooledResource(Handle handle) + { + Debug.Assert(!_disposed); + + if (handle.IsInvalid) + { + return; + } + + _resourceReturnQueue.Enqueue(new ResourceReturnEntry(handle, _currentFrame)); + } + public void Dispose() { if (_disposed) @@ -342,6 +422,20 @@ public sealed class ResourceManager : IDisposable _shaders.Dispose(); _materialPalettes.Dispose(); + foreach (var kvp in _resourceCache) + { + var queue = kvp.Value; + while (queue.TryDequeue(out var handle)) + { + _resourceDatabase.ReleaseResource(handle); + } + + queue.Dispose(); + } + + _resourceCache.Dispose(); + _resourceReturnQueue.Dispose(); + _disposed = true; GC.SuppressFinalize(this); }