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:
2026-03-23 20:48:08 +09:00
parent 2b3bf21a74
commit d44ec0be31
22 changed files with 623 additions and 440 deletions

View File

@@ -5,7 +5,6 @@ using Ghost.Graphics;
using Ghost.Graphics.Core; using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
using Misaki.HighPerformance.Mathematics.Geometry; using Misaki.HighPerformance.Mathematics.Geometry;
@@ -79,8 +78,6 @@ public class RenderExtractionSystem : ISystem
ref var cameraQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_cameraQueryID); ref var cameraQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_cameraQueryID);
ref var meshQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_meshQueryID); ref var meshQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_meshQueryID);
var renderRequests = new UnsafeList<RenderRequest>(cameraQuery.CalculateEntityCount(), Allocator.Temp);
// TODO: We should extract the render record for each camera because different cameras may have different culling results. // 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<Camera, LocalToWorld>()) foreach (var (cam, camLtw) in cameraQuery.GetComponentIterator<Camera, LocalToWorld>())
{ {
@@ -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) public void Cleanup(ref readonly SystemAPI systemAPI)

View File

@@ -1,33 +1,26 @@
using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
internal unsafe class D3D12CommandAllocator : ICommandAllocator internal unsafe class D3D12CommandAllocator : D3D12Object<ID3D12CommandAllocator>, ICommandAllocator
{ {
private UniquePtr<ID3D12CommandAllocator> _allocator; private static ID3D12CommandAllocator* CreateCommandAllocator(ID3D12Device14* device, D3D12_COMMAND_LIST_TYPE type)
public SharedPtr<ID3D12CommandAllocator> NativeAllocator => _allocator.Share();
public D3D12CommandAllocator(D3D12RenderDevice device, CommandBufferType type)
{ {
ID3D12CommandAllocator* pAllocator = default; 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); public D3D12CommandAllocator(D3D12RenderDevice device, CommandBufferType type)
: base(CreateCommandAllocator(device.NativeObject, D3D12Utility.ToCommandListType(type)))
_allocator.Attach(pAllocator); {
} }
public void Reset() public void Reset()
{ {
_allocator.Get()->Reset(); AssertNotDisposed();
pNativeObject->Reset();
} }
}
public void Dispose()
{
_allocator.Dispose();
}
}

View File

@@ -14,10 +14,8 @@ using static TerraFX.Aliases.DXGI_Alias;
namespace Ghost.Graphics.D3D12; 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 D3D12PipelineLibrary _pipelineLibrary;
private readonly D3D12ResourceDatabase _resourceDatabase; private readonly D3D12ResourceDatabase _resourceDatabase;
private readonly D3D12ResourceAllocator _resourceAllocator; private readonly D3D12ResourceAllocator _resourceAllocator;
@@ -27,46 +25,29 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
private CommandError _lastError; private CommandError _lastError;
private ushort _commandCount; private ushort _commandCount;
private bool _isRecording; private bool _isRecording;
private bool _disposed;
public SharedPtr<ID3D12GraphicsCommandList10> NativeCommandList => _commandList.Get();
public CommandBufferType Type => _type; public CommandBufferType Type => _type;
public bool IsEmpty => _commandCount == 0; public bool IsEmpty => _commandCount == 0;
public string Name private static ID3D12GraphicsCommandList10* CreateCommandList(ID3D12Device14* device, D3D12_COMMAND_LIST_TYPE type)
{ {
get => field; ID3D12GraphicsCommandList10* pCommandList = default;
set ThrowIfFailed(device->CreateCommandList1(0u, type, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList));
{ return pCommandList;
if (field == value) }
{
return;
}
field = value;
_commandList.Get()->SetName(value);
}
} = string.Empty;
public D3D12CommandBuffer( public D3D12CommandBuffer(
D3D12RenderDevice device, D3D12RenderDevice device,
D3D12PipelineLibrary stateController, D3D12PipelineLibrary pipelineLibrary,
D3D12ResourceDatabase resourceDatabase, D3D12ResourceDatabase resourceDatabase,
D3D12ResourceAllocator resourceAllocator, D3D12ResourceAllocator resourceAllocator,
D3D12DescriptorAllocator descriptorAllocator, D3D12DescriptorAllocator descriptorAllocator,
CommandBufferType type) CommandBufferType type)
:base (CreateCommandList(device.NativeObject, D3D12Utility.ToCommandListType(type)))
{ {
_type = type; _type = type;
ID3D12GraphicsCommandList10* pCommandList = default; _pipelineLibrary = pipelineLibrary;
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;
_resourceDatabase = resourceDatabase; _resourceDatabase = resourceDatabase;
_resourceAllocator = resourceAllocator; _resourceAllocator = resourceAllocator;
_descriptorAllocator = descriptorAllocator; _descriptorAllocator = descriptorAllocator;
@@ -74,18 +55,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_isRecording = false; _isRecording = false;
} }
~D3D12CommandBuffer()
{
Dispose();
}
[Conditional("DEBUG")] [Conditional("DEBUG")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfRecording() private void ThrowIfRecording()
{ {
@@ -95,6 +65,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
} }
} }
[Conditional("DEBUG")]
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfNotRecording() private void ThrowIfNotRecording()
{ {
@@ -133,7 +104,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
throw new ArgumentException("Invalid command allocator type", nameof(allocator)); 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) if (Type == CommandBufferType.Graphics || Type == CommandBufferType.Compute)
{ {
@@ -142,7 +113,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var heaps = stackalloc ID3D12DescriptorHeap*[2]; var heaps = stackalloc ID3D12DescriptorHeap*[2];
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource Heap heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource Heap
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler Heap heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler Heap
_commandList.Get()->SetDescriptorHeaps(2, heaps); pNativeObject->SetDescriptorHeaps(2, heaps);
} }
_commandCount = 0; _commandCount = 0;
@@ -154,7 +125,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
_commandList.Get()->Close(); pNativeObject->Close();
_isRecording = false; _isRecording = false;
#if !DEBUG #if !DEBUG
@@ -169,7 +140,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
public void SetScissorRect(RectDesc rect) public void SetScissorRect(RectDesc rect)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -180,12 +151,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
IncrementCommandCount(); IncrementCommandCount();
var d3d12Rect = new RECT((int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom); 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) public void ResourceBarrier(params ReadOnlySpan<BarrierDesc> barrierDescs)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -333,12 +304,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
groupCount++; groupCount++;
} }
_commandList.Get()->Barrier(groupCount, groups); pNativeObject->Barrier(groupCount, groups);
} }
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget) public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -386,12 +357,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(viewGroup.dsv); 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) public void ClearRenderTargetView(Handle<Texture> renderTarget, Color128 clearColor)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -411,12 +382,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ref var record = ref recordResult.Value; ref var record = ref recordResult.Value;
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.rtv); 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) public void ClearDepthStencilView(Handle<Texture> depthStencil, bool inlcudeDepth, bool includeStencil, float clearDepth = 1.0f, byte clearStencil = 0)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -437,7 +408,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.dsv); var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.dsv);
var flag = (inlcudeDepth ? D3D12_CLEAR_FLAG_DEPTH : 0) | (includeStencil ? D3D12_CLEAR_FLAG_STENCIL : 0); var flag = (inlcudeDepth ? D3D12_CLEAR_FLAG_DEPTH : 0) | (includeStencil ? D3D12_CLEAR_FLAG_STENCIL : 0);
_commandList.Get()->ClearDepthStencilView(cpuHandle, pNativeObject->ClearDepthStencilView(cpuHandle,
flag, flag,
clearDepth, clearDepth,
clearStencil, clearStencil,
@@ -447,7 +418,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false) public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -602,13 +573,13 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
pDsvDesc[0] = desc; 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); allowUAVWrites ? D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES : D3D12_RENDER_PASS_FLAG_NONE);
} }
public void EndRenderPass() public void EndRenderPass()
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -618,12 +589,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
#endif #endif
IncrementCommandCount(); IncrementCommandCount();
_commandList.Get()->EndRenderPass(); pNativeObject->EndRenderPass();
} }
public void SetViewport(ViewportDesc viewport) public void SetViewport(ViewportDesc viewport)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -634,12 +605,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
IncrementCommandCount(); IncrementCommandCount();
var d3d12Viewport = new D3D12_VIEWPORT(viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth); 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) public void SetPipelineState(Key128<GraphicsPipeline> pipelineKey)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -656,13 +627,13 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
return; return;
} }
_commandList.Get()->SetGraphicsRootSignature(_pipelineLibrary.DefaultRootSignature); pNativeObject->SetGraphicsRootSignature(_pipelineLibrary.DefaultRootSignature);
_commandList.Get()->SetPipelineState(psor.Value); pNativeObject->SetPipelineState(psor.Value);
} }
public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer) public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -673,12 +644,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
IncrementCommandCount(); IncrementCommandCount();
var resource = _resourceDatabase.GetResource(buffer.AsResource()); 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) public void SetVertexBuffer(uint slot, Handle<GraphicsBuffer> buffer, ulong offset = 0)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -703,12 +674,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
StrideInBytes = record.desc.BufferDescription.Stride 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) public void SetIndexBuffer(Handle<GraphicsBuffer> buffer, IndexType type, ulong offset = 0)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) 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 Format = type == IndexType.UInt16 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT
}; };
_commandList.Get()->IASetIndexBuffer(&ibView); pNativeObject->IASetIndexBuffer(&ibView);
} }
public void SetPrimitiveTopology(PrimitiveTopology topology) public void SetPrimitiveTopology(PrimitiveTopology topology)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -749,12 +720,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_ => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST _ => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST
}; };
_commandList.Get()->IASetPrimitiveTopology(d3d12Topology); pNativeObject->IASetPrimitiveTopology(d3d12Topology);
} }
public void SetGraphicsRoot32Constants(uint rootIndex, ReadOnlySpan<uint> constantBuffer, uint offsetIn32Bits = 0) public void SetGraphicsRoot32Constants(uint rootIndex, ReadOnlySpan<uint> constantBuffer, uint offsetIn32Bits = 0)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -766,13 +737,13 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
fixed (uint* pConstants = constantBuffer) 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) public void Draw(uint vertexCount, uint instanceCount = 1, uint startVertex = 0, uint startInstance = 0)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -782,12 +753,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
#endif #endif
IncrementCommandCount(); 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) public void DrawIndexed(uint indexCount, uint instanceCount = 1, uint startIndex = 0, int baseVertex = 0, uint startInstance = 0)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -797,12 +768,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
#endif #endif
IncrementCommandCount(); 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) public void DispatchCompute(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -812,12 +783,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
#endif #endif
IncrementCommandCount(); IncrementCommandCount();
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ); pNativeObject->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
} }
public void DispatchMesh(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ) public void DispatchMesh(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -827,14 +798,14 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
#endif #endif
IncrementCommandCount(); IncrementCommandCount();
_commandList.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ); pNativeObject->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
} }
public void DispatchRay() public void DispatchRay()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
// ThrowIfDisposed(); // AssertNotDisposed();
// ThrowIfNotRecording(); // ThrowIfNotRecording();
// IncrementCommandCount(); // IncrementCommandCount();
@@ -850,7 +821,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
{ {
throw new NotImplementedException(); throw new NotImplementedException();
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -865,7 +836,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var countResource = _resourceDatabase.GetResource(countBuffer.AsResource()); var countResource = _resourceDatabase.GetResource(countBuffer.AsResource());
// TODO // TODO
_commandList.Get()->ExecuteIndirect(null, 0, pNativeObject->ExecuteIndirect(null, 0,
resource, argumentOffset, countResource, countBufferOffset); resource, argumentOffset, countResource, countBufferOffset);
} }
@@ -873,7 +844,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
public void UploadBuffer<T>(Handle<GraphicsBuffer> buffer, params ReadOnlySpan<T> data) public void UploadBuffer<T>(Handle<GraphicsBuffer> buffer, params ReadOnlySpan<T> data)
where T : unmanaged where T : unmanaged
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -897,12 +868,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
uploadResource.Get()->Unmap(0, null); uploadResource.Get()->Unmap(0, null);
var pResource = _resourceDatabase.GetResource(buffer.AsResource()); 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) public void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -932,7 +903,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
} }
UpdateSubresources( UpdateSubresources(
(ID3D12GraphicsCommandList*)_commandList.Get(), (ID3D12GraphicsCommandList*)pNativeObject,
resource, resource,
pUploadResource, pUploadResource,
offset, 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) public void CopyBuffer(Handle<GraphicsBuffer> dst, Handle<GraphicsBuffer> src, ulong dstOffset = 0, ulong srcOffset = 0, ulong numBytes = 0)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -968,11 +939,11 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
if (numBytes == 0) if (numBytes == 0)
{ {
_commandList.Get()->CopyResource(pDstResource, pSrcResource); pNativeObject->CopyResource(pDstResource, pSrcResource);
} }
else 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) public void CopyTexture(Handle<Texture> dst, TextureRegion? dstRegion, Handle<Texture> src, TextureRegion? srcRegion)
{ {
ThrowIfDisposed(); AssertNotDisposed();
ThrowIfNotRecording(); ThrowIfNotRecording();
#if !DEBUG #if !DEBUG
if (_lastError.Status != Error.None) if (_lastError.Status != Error.None)
@@ -1033,7 +1004,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
return; return;
} }
_commandList.Get()->CopyResource(pDstResource, pSrcResource); pNativeObject->CopyResource(pDstResource, pSrcResource);
return; return;
} }
@@ -1052,25 +1023,6 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
back = srcRegionV.Z + srcRegionV.Depth back = srcRegionV.Z + srcRegionV.Depth
}; };
_commandList.Get()->CopyTextureRegion(&dstLocation, dstRegionV.X, dstRegionV.Y, dstRegionV.Z, &srcLocation, &srcBoc); pNativeObject->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);
} }
} }

View File

@@ -10,28 +10,20 @@ namespace Ghost.Graphics.D3D12;
/// <summary> /// <summary>
/// D3D12 implementation of command queue interface /// D3D12 implementation of command queue interface
/// </summary> /// </summary>
internal unsafe class D3D12CommandQueue : ICommandQueue internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue1>, ICommandQueue
{ {
private UniquePtr<ID3D12CommandQueue> _commandQueue;
private UniquePtr<ID3D12Fence1> _fence; private UniquePtr<ID3D12Fence1> _fence;
private readonly AutoResetEvent _fenceEvent; private readonly AutoResetEvent _fenceEvent;
private ulong _fenceValue; private ulong _fenceValue;
private bool _disposed;
public CommandQueueType Type public CommandQueueType Type
{ {
get; get;
} }
public SharedPtr<ID3D12CommandQueue> NativeQueue => _commandQueue.Get(); private static ID3D12CommandQueue1* CreateCommandQueue(ID3D12Device14* device, CommandQueueType type)
public D3D12CommandQueue(ID3D12Device14* pDevice, CommandQueueType type)
{ {
Type = type;
_fenceEvent = new AutoResetEvent(false);
_fenceValue = 0;
var queueDesc = new D3D12_COMMAND_QUEUE_DESC var queueDesc = new D3D12_COMMAND_QUEUE_DESC
{ {
Type = ConvertCommandQueueType(type), Type = ConvertCommandQueueType(type),
@@ -39,18 +31,22 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
Flags = D3D12_COMMAND_QUEUE_FLAGS.D3D12_COMMAND_QUEUE_FLAG_NONE, Flags = D3D12_COMMAND_QUEUE_FLAGS.D3D12_COMMAND_QUEUE_FLAG_NONE,
}; };
ID3D12CommandQueue* pQueue = default; ID3D12CommandQueue1* pQueue = default;
ID3D12Fence1* pFence = default; ThrowIfFailed(device->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue));
ThrowIfFailed(pDevice->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue)); return pQueue;
ThrowIfFailed(pDevice->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
_commandQueue.Attach(pQueue);
_fence.Attach(pFence);
} }
~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) private static D3D12_COMMAND_LIST_TYPE ConvertCommandQueueType(CommandQueueType type)
@@ -66,7 +62,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
public void Submit(ICommandBuffer commandBuffer) public void Submit(ICommandBuffer commandBuffer)
{ {
Debug.Assert(!_disposed); AssertNotDisposed();
if (commandBuffer.IsEmpty) if (commandBuffer.IsEmpty)
{ {
@@ -75,9 +71,9 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
if (commandBuffer is D3D12CommandBuffer d3d12CommandBuffer) if (commandBuffer is D3D12CommandBuffer d3d12CommandBuffer)
{ {
var commandList = d3d12CommandBuffer.NativeCommandList; var commandList = d3d12CommandBuffer.NativeObject;
var commandListPtr = (ID3D12CommandList*)commandList.Get(); var commandListPtr = (ID3D12CommandList*)commandList.Get();
_commandQueue.Get()->ExecuteCommandLists(1, &commandListPtr); pNativeObject->ExecuteCommandLists(1, &commandListPtr);
} }
else else
{ {
@@ -87,7 +83,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers) public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
{ {
Debug.Assert(!_disposed); AssertNotDisposed();
Span<int> executableIndices = stackalloc int[commandBuffers.Length]; Span<int> executableIndices = stackalloc int[commandBuffers.Length];
executableIndices.Fill(-1); executableIndices.Fill(-1);
@@ -115,7 +111,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
if (commandBuffers[cmdIndex] is D3D12CommandBuffer d3d12CommandBuffer) if (commandBuffers[cmdIndex] is D3D12CommandBuffer d3d12CommandBuffer)
{ {
ppCommandLists[currentIndex] = (ID3D12CommandList*)d3d12CommandBuffer.NativeCommandList.Get(); ppCommandLists[currentIndex] = (ID3D12CommandList*)d3d12CommandBuffer.NativeObject.Get();
} }
else else
{ {
@@ -125,21 +121,21 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
currentIndex++; currentIndex++;
} }
_commandQueue.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists); pNativeObject->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
} }
public ulong Signal(ulong value) public ulong Signal(ulong value)
{ {
Debug.Assert(!_disposed); AssertNotDisposed();
_fenceValue = value; _fenceValue = value;
ThrowIfFailed(_commandQueue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue)); ThrowIfFailed(pNativeObject->Signal((ID3D12Fence*)_fence.Get(), _fenceValue));
return _fenceValue; return _fenceValue;
} }
public void WaitForValue(ulong value) public void WaitForValue(ulong value)
{ {
Debug.Assert(!_disposed); AssertNotDisposed();
if (_fence.Get()->GetCompletedValue() < value) if (_fence.Get()->GetCompletedValue() < value)
{ {
@@ -153,30 +149,21 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
public ulong GetCompletedValue() public ulong GetCompletedValue()
{ {
Debug.Assert(!_disposed); AssertNotDisposed();
return _fence.Get()->GetCompletedValue(); return _fence.Get()->GetCompletedValue();
} }
public void WaitIdle() public void WaitIdle()
{ {
Debug.Assert(!_disposed); AssertNotDisposed();
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue)); var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
WaitForValue(fenceValue); WaitForValue(fenceValue);
} }
public void Dispose() protected override void Dispose(bool disposing)
{ {
if (_disposed)
{
return;
}
_commandQueue.Dispose();
_fence.Dispose(); _fence.Dispose();
_fenceEvent?.Dispose(); _fenceEvent?.Dispose();
_disposed = true;
GC.SuppressFinalize(this);
} }
} }

View File

@@ -64,7 +64,7 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
HeapType = type; HeapType = type;
NumDescriptors = numDescriptors; NumDescriptors = numDescriptors;
ShaderVisible = type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; 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); var success = AllocateResources(numDescriptors);
Debug.Assert(success); Debug.Assert(success);
@@ -210,7 +210,7 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
public void CopyToShaderVisibleHeap(int index, int count = 1) 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) private bool AllocateResources(int numDescriptors)
@@ -228,7 +228,7 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
}; };
ID3D12DescriptorHeap* pHeap = default; 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) if (hr.FAILED)
{ {
return false; return false;
@@ -252,7 +252,7 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
ID3D12DescriptorHeap* pShaderVisibleHeap = default; ID3D12DescriptorHeap* pShaderVisibleHeap = default;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; 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) if (hr.FAILED)
{ {
return false; return false;
@@ -281,11 +281,11 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
return false; 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) 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 finally
@@ -305,4 +305,4 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
_shaderVisibleHeap.Dispose(); _shaderVisibleHeap.Dispose();
_allocatedDescriptors.Dispose(); _allocatedDescriptors.Dispose();
} }
} }

View File

@@ -5,7 +5,6 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core; using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -22,6 +21,18 @@ public static class D3D12GraphicsEngineFactory
internal class D3D12GraphicsEngine : IGraphicsEngine 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; private readonly GraphicsEngineDesc _desc;
#if ENABLE_DEBUG_LAYER #if ENABLE_DEBUG_LAYER
@@ -34,8 +45,10 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
private readonly D3D12PipelineLibrary _pipelineLibrary; private readonly D3D12PipelineLibrary _pipelineLibrary;
private readonly D3D12ResourceAllocator _resourceAllocator; private readonly D3D12ResourceAllocator _resourceAllocator;
private ImmutableArray<IRenderer> _renderers; private readonly Queue<ICommandBuffer> _commandBufferPool;
private readonly Queue<CommandBufferReturnEntry> _commandBufferReturnQueue;
private ulong _currentFrame;
private bool _disposed; private bool _disposed;
public IRenderDevice Device => _device; public IRenderDevice Device => _device;
@@ -59,9 +72,8 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
_pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase); _pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase);
_resourceAllocator = new D3D12ResourceAllocator(_device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary); _resourceAllocator = new D3D12ResourceAllocator(_device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
_renderers = ImmutableArray<IRenderer>.Empty; _commandBufferPool = new Queue<ICommandBuffer>(4);
_commandBufferReturnQueue = new Queue<CommandBufferReturnEntry>(4);
_pipelineLibrary.InitializeLibrary(null);
} }
~D3D12GraphicsEngine() ~D3D12GraphicsEngine()
@@ -76,27 +88,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
ObjectDisposedException.ThrowIf(_disposed, this); 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) public ICommandAllocator CreateCommandAllocator(CommandBufferType type = CommandBufferType.Graphics)
{ {
return new D3D12CommandAllocator(_device, type); return new D3D12CommandAllocator(_device, type);
@@ -115,25 +106,51 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
type); 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) public ISwapChain CreateSwapChain(SwapChainDesc desc)
{ {
ThrowIfDisposed(); 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(); ThrowIfDisposed();
_resourceDatabase.BeginFrame(cpuFenceValue); _currentFrame = currentFrame;
_resourceDatabase.BeginFrame(currentFrame);
return Result.Success(); return Result.Success();
} }
public Result EndFrame(uint cpuFenceValue, uint gpuFenceValue) public Result EndFrame(ulong completedFrame)
{ {
ThrowIfDisposed(); 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(); return Result.Success();
} }
@@ -144,11 +161,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
return; return;
} }
foreach (var renderer in _renderers)
{
renderer.Dispose();
}
_resourceDatabase.ReleaseAllResourcesImmediately(); _resourceDatabase.ReleaseAllResourcesImmediately();
_resourceAllocator.Dispose(); _resourceAllocator.Dispose();

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

View File

@@ -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 D3D12RenderDevice _device;
private readonly D3D12ResourceDatabase _resourceDatabase; private readonly D3D12ResourceDatabase _resourceDatabase;
private UniquePtr<ID3D12PipelineLibrary1> _library;
private UniquePtr<ID3D12RootSignature> _defaultRootSignature; private UniquePtr<ID3D12RootSignature> _defaultRootSignature;
private readonly Dictionary<Key128<GraphicsPipeline>, D3D12PipelineState> _pipelineCache; private UnsafeHashMap<Key128<GraphicsPipeline>, D3D12PipelineState> _pipelineCache;
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get(); 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) public D3D12PipelineLibrary(D3D12RenderDevice device, D3D12ResourceDatabase resourceDatabase)
:base(CreateLibrary(device, null)) // TODO: we need to path to load the existing library from disk.
{ {
_device = device; _device = device;
_resourceDatabase = resourceDatabase; _resourceDatabase = resourceDatabase;
_pipelineCache = new Dictionary<Key128<GraphicsPipeline>, D3D12PipelineState>(); _pipelineCache = new UnsafeHashMap<Key128<GraphicsPipeline>, D3D12PipelineState>(32, Allocator.Persistent);
CreateDefaultRootSignature().ThrowIfFailed(); 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() private Result CreateDefaultRootSignature()
{ {
_defaultRootSignature = default; _defaultRootSignature = default;
@@ -96,7 +115,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
} }
ID3D12RootSignature* pRootSignature = default; 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)); __uuidof(pRootSignature), (void**)&pRootSignature));
_defaultRootSignature.Attach(pRootSignature); _defaultRootSignature.Attach(pRootSignature);
@@ -104,26 +123,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
return Result.Success(); 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) public void SaveLibraryToDisk(string filePath)
{ {
var dir = Path.GetDirectoryName(filePath); var dir = Path.GetDirectoryName(filePath);
@@ -132,10 +131,10 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
throw new InvalidOperationException($"Directory does not exist: {dir}"); 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. 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); using var fs = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(buffer.AsSpan()); fs.Write(buffer.AsSpan());
@@ -225,6 +224,8 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
return psr.Value; return psr.Value;
} }
AssertNotDisposed();
if (descriptor.RtvFormats.Length > D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT) 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}."); 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."); 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) if (hr == E.E_INVALIDARG)
{ {
// Pipeline not found in the library, create a new one. // Pipeline not found in the library, create a new one.
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState)); ThrowIfFailed(_device.NativeObject.Get()->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
ThrowIfFailed(_library.Get()->StorePipeline(pKeyStr, pPipelineState)); ThrowIfFailed(pNativeObject->StorePipeline(pKeyStr, pPipelineState));
} }
else else
{ {
@@ -325,11 +326,13 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
public bool HasPipeline(Key128<GraphicsPipeline> key) public bool HasPipeline(Key128<GraphicsPipeline> key)
{ {
AssertNotDisposed();
return _pipelineCache.ContainsKey(key); return _pipelineCache.ContainsKey(key);
} }
public Result<SharedPtr<ID3D12PipelineState>, Error> GetGraphicsPSO(Key128<GraphicsPipeline> key) public Result<SharedPtr<ID3D12PipelineState>, Error> GetGraphicsPSO(Key128<GraphicsPipeline> key)
{ {
AssertNotDisposed();
if (_pipelineCache.TryGetValue(key, out var cacheEntry)) if (_pipelineCache.TryGetValue(key, out var cacheEntry))
{ {
return cacheEntry.pso.Share(); return cacheEntry.pso.Share();
@@ -338,16 +341,14 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
return Error.NotFound; return Error.NotFound;
} }
public void Dispose() protected override void Dispose(bool disposing)
{ {
foreach (var kvp in _pipelineCache) foreach (var kvp in _pipelineCache)
{ {
kvp.Value.Dispose(); kvp.Value.Dispose();
} }
_pipelineCache.Clear(); _pipelineCache.Dispose();
_defaultRootSignature.Dispose(); _defaultRootSignature.Dispose();
_library.Dispose();
} }
} }

View File

@@ -9,12 +9,8 @@ using static TerraFX.Aliases.DXGI_Alias;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
/// <summary> internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDevice
/// D3D12 implementation of the render device interface
/// </summary>
internal unsafe class D3D12RenderDevice : IRenderDevice
{ {
private UniquePtr<ID3D12Device14> _device;
private UniquePtr<IDXGIFactory7> _dxgiFactory; private UniquePtr<IDXGIFactory7> _dxgiFactory;
private UniquePtr<IDXGIAdapter1> _adapter; private UniquePtr<IDXGIAdapter1> _adapter;
@@ -22,8 +18,6 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
private readonly D3D12CommandQueue _computeQueue; private readonly D3D12CommandQueue _computeQueue;
private readonly D3D12CommandQueue _copyQueue; private readonly D3D12CommandQueue _copyQueue;
private bool _disposed;
public ICommandQueue GraphicsQueue => _graphicsQueue; public ICommandQueue GraphicsQueue => _graphicsQueue;
public ICommandQueue ComputeQueue => _computeQueue; public ICommandQueue ComputeQueue => _computeQueue;
public ICommandQueue CopyQueue => _copyQueue; public ICommandQueue CopyQueue => _copyQueue;
@@ -34,14 +28,28 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
} }
public SharedPtr<IDXGIFactory7> DXGIFactory => _dxgiFactory.Share(); public SharedPtr<IDXGIFactory7> DXGIFactory => _dxgiFactory.Share();
public SharedPtr<ID3D12Device14> NativeDevice => _device.Share();
public SharedPtr<IDXGIAdapter1> Adapter => _adapter.Share(); public SharedPtr<IDXGIAdapter1> Adapter => _adapter.Share();
public SharedPtr<ID3D12CommandQueue> NativeGraphicsQueue => _graphicsQueue.NativeQueue; public SharedPtr<ID3D12CommandQueue1> NativeGraphicsQueue => _graphicsQueue.NativeObject;
public SharedPtr<ID3D12CommandQueue> NativeComputeQueue => _computeQueue.NativeQueue; public SharedPtr<ID3D12CommandQueue1> NativeComputeQueue => _computeQueue.NativeObject;
public SharedPtr<ID3D12CommandQueue> NativeCopyQueue => _copyQueue.NativeQueue; public SharedPtr<ID3D12CommandQueue1> NativeCopyQueue => _copyQueue.NativeObject;
public D3D12RenderDevice() 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; IDXGIFactory7* pFactory = default;
#if DEBUG #if DEBUG
ThrowIfFailed(CreateDXGIFactory2(TRUE, __uuidof(pFactory), (void**)&pFactory)); ThrowIfFailed(CreateDXGIFactory2(TRUE, __uuidof(pFactory), (void**)&pFactory));
@@ -49,29 +57,13 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
ThrowIfFailed(CreateDXGIFactory2(FALSE, __uuidof(pFactory), (void**)&pFactory)); ThrowIfFailed(CreateDXGIFactory2(FALSE, __uuidof(pFactory), (void**)&pFactory));
#endif #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; ID3D12Device14* pDevice = default;
IDXGIAdapter1* pAdapter = default; IDXGIAdapter1* pAdapter = default;
for (uint adapterIndex = 0; 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++) adapterIndex++)
{ {
DXGI_ADAPTER_DESC1 desc = default; 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) if (D3D12CreateDevice((IUnknown*)pAdapter, D3D_FEATURE_LEVEL_12_0, __uuidof(pDevice), (void**)&pDevice).SUCCEEDED)
{ {
_adapter.Attach(pAdapter); adapter = pAdapter;
break; break;
} }
@@ -96,20 +88,21 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
if (pDevice == null) if (pDevice == null)
{ {
pAdapter->Release(); // Dispose the last adapter we tried. 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"); throw new PlatformNotSupportedException("Cannot create ID3D12Device with feature level 12.0");
} }
_device.Attach(pDevice); return pDevice;
} }
private FeatureSupport GetFeatureSupport() private FeatureSupport GetFeatureSupport()
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ThrowIfDisposed();
var support = FeatureSupport.None; var support = FeatureSupport.None;
D3D12_FEATURE_DATA_D3D12_OPTIONS options = default; 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) if (options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_3)
{ {
@@ -123,7 +116,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
} }
D3D12_FEATURE_DATA_D3D12_OPTIONS5 options5 = default; 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) if (options5.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED)
{ {
@@ -132,7 +125,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
} }
D3D12_FEATURE_DATA_D3D12_OPTIONS6 options6 = default; 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) 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; 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) 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; 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) if (options9.WorkGraphsTier != D3D12_WORK_GRAPHS_TIER.D3D12_WORK_GRAPHS_TIER_NOT_SUPPORTED)
{ {
@@ -166,22 +159,13 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
return support; return support;
} }
public void Dispose() protected override void Dispose(bool disposing)
{ {
if (_disposed)
{
return;
}
_graphicsQueue.Dispose(); _graphicsQueue.Dispose();
_computeQueue.Dispose(); _computeQueue.Dispose();
_copyQueue.Dispose(); _copyQueue.Dispose();
_device.Dispose();
_dxgiFactory.Dispose(); _dxgiFactory.Dispose();
_adapter.Dispose(); _adapter.Dispose();
_disposed = true;
GC.SuppressFinalize(this);
} }
} }

View File

@@ -468,7 +468,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
var desc = new D3D12MA_ALLOCATOR_DESC var desc = new D3D12MA_ALLOCATOR_DESC
{ {
pAdapter = (IDXGIAdapter*)device.Adapter.Get(), 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, 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(); 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 return new ResourceSizeInfo
{ {
Size = info.SizeInBytes, 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 isCubeMap = desc.Dimension == TextureDimension.TextureCube || desc.Dimension == TextureDimension.TextureCubeArray;
var srvDesc = CreateTextureSrvDesc(pResource, resourceDesc.MipLevels, resourceDesc.DepthOrArraySize, isCubeMap); 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); _descriptorAllocator.CopyToShaderVisible(resourceDescriptor.srv);
} }
@@ -656,7 +656,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.rtv); var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.rtv);
var rtvDesc = CreateRtvDesc(pResource); var rtvDesc = CreateRtvDesc(pResource);
_device.NativeDevice.Get()->CreateRenderTargetView(pResource, &rtvDesc, cpuHandle); _device.NativeObject.Get()->CreateRenderTargetView(pResource, &rtvDesc, cpuHandle);
} }
if (desc.Usage.HasFlag(TextureUsage.DepthStencil)) if (desc.Usage.HasFlag(TextureUsage.DepthStencil))
@@ -665,7 +665,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.dsv); var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.dsv);
var dsvDesc = CreateDsvDesc(pResource); var dsvDesc = CreateDsvDesc(pResource);
_device.NativeDevice.Get()->CreateDepthStencilView(pResource, &dsvDesc, cpuHandle); _device.NativeObject.Get()->CreateDepthStencilView(pResource, &dsvDesc, cpuHandle);
} }
if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess)) if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess))
@@ -674,7 +674,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav); var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
var uavDesc = CreateTextureUavDesc(pResource); var uavDesc = CreateTextureUavDesc(pResource);
_device.NativeDevice.Get()->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle); _device.NativeObject.Get()->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.uav); _descriptorAllocator.CopyToShaderVisible(resourceDescriptor.uav);
} }
@@ -764,7 +764,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
SizeInBytes = (uint)resourceDesc.Width, SizeInBytes = (uint)resourceDesc.Width,
}; };
_device.NativeDevice.Get()->CreateConstantBufferView(&cbvDesc, cpuHandle); _device.NativeObject.Get()->CreateConstantBufferView(&cbvDesc, cpuHandle);
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.cbv); _descriptorAllocator.CopyToShaderVisible(resourceDescriptor.cbv);
} }
@@ -774,7 +774,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv); var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
var srvDesc = CreateBufferSrvDesc(pResource, desc.Stride, isRaw); 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); _descriptorAllocator.CopyToShaderVisible(resourceDescriptor.srv);
} }
@@ -784,7 +784,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav); var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
var uavDesc = CreateBufferUavDesc(pResource, desc.Stride, isRaw); 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); _descriptorAllocator.CopyToShaderVisible(resourceDescriptor.uav);
} }
@@ -862,7 +862,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
var samplerDescriptor = _descriptorAllocator.AllocateSampler(); var samplerDescriptor = _descriptorAllocator.AllocateSampler();
var cpuHandle = _descriptorAllocator.GetCpuHandle(samplerDescriptor); var cpuHandle = _descriptorAllocator.GetCpuHandle(samplerDescriptor);
_device.NativeDevice.Get()->CreateSampler(&samplerDesc, cpuHandle); _device.NativeObject.Get()->CreateSampler(&samplerDesc, cpuHandle);
_descriptorAllocator.CopyToShaderVisible(samplerDescriptor); _descriptorAllocator.CopyToShaderVisible(samplerDescriptor);
return _resourceDatabase.AddSampler(in desc, samplerDescriptor.Value); return _resourceDatabase.AddSampler(in desc, samplerDescriptor.Value);

View File

@@ -88,9 +88,9 @@ internal class D3D12ResourceDatabase : IResourceDatabase
private readonly struct ReleaseEntry private readonly struct ReleaseEntry
{ {
public readonly ResourceRecord record; 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.record = record;
this.fenceValue = fenceValue; this.fenceValue = fenceValue;
@@ -108,7 +108,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
private UnsafeQueue<ReleaseEntry> _releaseQueue; private UnsafeQueue<ReleaseEntry> _releaseQueue;
private uint _currentFrameFenceValue; private ulong _currentFrame;
private bool _disposed; private bool _disposed;
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator) public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
@@ -136,7 +136,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
if (pResource == null) if (pResource == null)
{ {
#if DEBUG #if DEBUG
System.Diagnostics.Debugger.Break(); Debugger.Break();
#endif #endif
return Handle<GPUResource>.Invalid; return Handle<GPUResource>.Invalid;
} }
@@ -155,13 +155,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return handle; 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); Debug.Assert(!_disposed);
if (allocation == null) if (allocation == null)
{ {
#if DEBUG #if DEBUG
System.Diagnostics.Debugger.Break(); Debugger.Break();
#endif #endif
return Handle<GPUResource>.Invalid; return Handle<GPUResource>.Invalid;
} }
@@ -288,7 +288,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return; return;
} }
var entry = new ReleaseEntry(record, _currentFrameFenceValue); var entry = new ReleaseEntry(record, _currentFrame);
_releaseQueue.Enqueue(entry); _releaseQueue.Enqueue(entry);
_resources.Remove(handle.ID, handle.Generation); _resources.Remove(handle.ID, handle.Generation);
@@ -359,20 +359,20 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return Error.None; return Error.None;
} }
public void BeginFrame(uint currentFrameFenceValue) public void BeginFrame(ulong currentFrame)
{ {
Debug.Assert(!_disposed); Debug.Assert(!_disposed);
_currentFrameFenceValue = currentFrameFenceValue; _currentFrame = currentFrame;
} }
public void EndFrame(uint completedFenceValue) public void EndFrame(ulong completedFrame)
{ {
Debug.Assert(!_disposed); Debug.Assert(!_disposed);
while (_releaseQueue.Count > 0) while (_releaseQueue.Count > 0)
{ {
var toRelease = _releaseQueue.Peek(); var toRelease = _releaseQueue.Peek();
if (toRelease.fenceValue > completedFenceValue) if (toRelease.fenceValue > completedFrame)
{ {
break; break;
} }

View File

@@ -12,14 +12,11 @@ using static TerraFX.Aliases.DXGI_Alias;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
/// <summary> internal unsafe class DXGISwapChain : ISwapChain
/// D3D12 implementation of swap chain interface
/// </summary>
internal unsafe class D3D12SwapChain : ISwapChain
{ {
private readonly D3D12ResourceDatabase _resourceDatabase; private readonly D3D12ResourceDatabase _resourceDatabase;
private readonly D3D12DescriptorAllocator _descriptorAllocator; private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly D3D12RenderDevice _renderDevice; private readonly D3D12RenderDevice _device;
private UniquePtr<IDXGISwapChain4> _swapChain; private UniquePtr<IDXGISwapChain4> _swapChain;
private UnsafeArray<Handle<Texture>> _backBuffers; private UnsafeArray<Handle<Texture>> _backBuffers;
@@ -48,37 +45,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
get; private set; get; private set;
} }
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc, uint bufferCount) private static IDXGISwapChain4* CreateSwapChain(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)
{ {
var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1 var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1
{ {
@@ -97,8 +64,8 @@ internal unsafe class D3D12SwapChain : ISwapChain
IDXGISwapChain1* pTempSwapChain = default; IDXGISwapChain1* pTempSwapChain = default;
var pFactory = _renderDevice.DXGIFactory.Get(); var pFactory = device.DXGIFactory.Get();
var pCommandQueue = _renderDevice.NativeGraphicsQueue.Get(); var pCommandQueue = device.NativeGraphicsQueue.Get();
switch (desc.Target.Type) switch (desc.Target.Type)
{ {
@@ -139,6 +106,36 @@ internal unsafe class D3D12SwapChain : ISwapChain
return pSwapChain; 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() private void CreateBackBuffers()
{ {
for (uint i = 0; i < _backBuffers.Count; i++) for (uint i = 0; i < _backBuffers.Count; i++)
@@ -149,7 +146,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
var rtv = _descriptorAllocator.AllocateRTV(); var rtv = _descriptorAllocator.AllocateRTV();
var cpuHandle = _descriptorAllocator.GetCpuHandle(rtv); var cpuHandle = _descriptorAllocator.GetCpuHandle(rtv);
_renderDevice.NativeDevice.Get()->CreateRenderTargetView(pBackBuffer, null, cpuHandle); _device.NativeObject.Get()->CreateRenderTargetView(pBackBuffer, null, cpuHandle);
var view = ResourceViewGroup.Invalid with var view = ResourceViewGroup.Invalid with
{ {

View File

@@ -68,7 +68,7 @@ internal static unsafe class D3D12Utility
if (ptr != null) if (ptr != null)
{ {
var refCount = ptr->Release(); var refCount = ptr->Release();
Debug.Assert((refCount != 0)); Debug.Assert(refCount != 0);
} }
} }

View File

@@ -627,7 +627,7 @@ public struct BarrierDesc
} }
} }
public struct ResourceDesc public record struct ResourceDesc
{ {
[StructLayout(LayoutKind.Explicit)] [StructLayout(LayoutKind.Explicit)]
internal struct resource_union internal struct resource_union
@@ -638,7 +638,7 @@ public struct ResourceDesc
public BufferDesc bufferDescription; public BufferDesc bufferDescription;
} }
internal resource_union _desc; private resource_union _desc;
public ResourceType Type public ResourceType Type
{ {
@@ -682,6 +682,31 @@ public struct ResourceDesc
TextureDescription = desc 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}")
};
}
} }
/// <summary> /// <summary>
@@ -831,7 +856,7 @@ public struct RenderTargetDesc
/// <summary> /// <summary>
/// Texture description /// Texture description
/// </summary> /// </summary>
public struct TextureDesc public record struct TextureDesc
{ {
/// <summary> /// <summary>
/// Width of the texture /// Width of the texture
@@ -939,7 +964,7 @@ public record struct SamplerDesc
} }
} }
public struct BufferDesc public record struct BufferDesc
{ {
public ulong Size public ulong Size
{ {

View File

@@ -37,25 +37,6 @@ public interface IGraphicsEngine : IDisposable
get; get;
} }
/// <summary>
/// Creates a new instance of a renderer for drawing graphical content.
/// </summary>
/// <returns>An object that implements the IRenderer interface, which can be used to render graphics.</returns>
IRenderer CreateRenderer();
/// <summary>
/// Removes the specified renderer from the collection of active renderers.
/// </summary>
/// <param name="renderer">The renderer instance to remove.</param>
void RemoveRenderer(IRenderer renderer);
/// <summary>
/// Removes all registered renderers from the collection.
/// </summary>
/// <remarks>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.</remarks>
void ClearRenderers();
/// <summary> /// <summary>
/// Creates a new command allocator for the specified command buffer space. /// Creates a new command allocator for the specified command buffer space.
/// </summary> /// </summary>
@@ -70,6 +51,19 @@ public interface IGraphicsEngine : IDisposable
/// <returns>A new command buffer instance</returns> /// <returns>A new command buffer instance</returns>
ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics); ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics);
/// <summary>
/// Gets a command buffer from the pool for recording rendering commands.
/// </summary>
/// <param name="type">Type of command buffer to get from the pool</param>
/// <returns>A command buffer instance from the pool</returns>
ICommandBuffer GetPooledCommandBuffer(CommandBufferType type = CommandBufferType.Graphics);
/// <summary>
/// Returns a command buffer to the pool after use.
/// </summary>
/// <param name="commandBuffer">The command buffer to return to the pool</param>
void ReturnPooledCommandBuffer(ICommandBuffer commandBuffer);
/// <summary> /// <summary>
/// Creates a swap chain for presentation /// Creates a swap chain for presentation
/// </summary> /// </summary>
@@ -80,16 +74,14 @@ public interface IGraphicsEngine : IDisposable
/// <summary> /// <summary>
/// Begin the current frame. /// Begin the current frame.
/// </summary> /// </summary>
/// <param name="cpuFenceValue">CPU fence value for synchronization</param> /// <param name="currentFrame">CPU fence value for synchronization</param>
/// <param name="gpuFenceValue">GPU fence value for synchronization</param>
/// <returns>Result of the begin frame operation</returns> /// <returns>Result of the begin frame operation</returns>
Result BeginFrame(uint cpuFenceValue, uint gpuFenceValue); Result BeginFrame(ulong currentFrame);
/// <summary> /// <summary>
/// End the current frame. /// End the current frame.
/// </summary> /// </summary>
/// <param name="cpuFenceValue">CPU fence value for synchronization</param> /// <param name="completedFrame">GPU fence value for synchronization</param>
/// <param name="gpuFenceValue">GPU fence value for synchronization</param>
/// <returns>Result of the end frame operation</returns> /// <returns>Result of the end frame operation</returns>
Result EndFrame(uint cpuFenceValue, uint gpuFenceValue); Result EndFrame(ulong completedFrame);
} }

View File

@@ -4,11 +4,6 @@ namespace Ghost.Graphics.RHI;
public interface IPipelineLibrary : IDisposable public interface IPipelineLibrary : IDisposable
{ {
/// <summary>
/// Load pipeline library from disk.
/// </summary>
/// <param name="filePath">File path. If null, load default library.</param>
void InitializeLibrary(string? filePath);
void SaveLibraryToDisk(string filePath); void SaveLibraryToDisk(string filePath);
bool HasPipeline(Key128<GraphicsPipeline> key); bool HasPipeline(Key128<GraphicsPipeline> key);
Result<Key128<GraphicsPipeline>> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled); Result<Key128<GraphicsPipeline>> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);

View File

@@ -0,0 +1,6 @@
namespace Ghost.Graphics.RHI;
public interface IRHIObject: IDisposable
{
string Name { get; set; }
}

View File

@@ -5,6 +5,9 @@ namespace Ghost.Graphics.RHI;
public readonly struct RenderContext public readonly struct RenderContext
{ {
public ICommandBuffer CommandBuffer { get; init; } 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. // TODO: We may don't need this anymore. We Use RenderExtractionSystem to extract render data from entities and pass them to IRenderPipeline to render.

View File

@@ -34,12 +34,12 @@ public struct Frustum
public static void CalculateFrustumPlanes(float4x4 finalMatrix, ref plane_array outPlanes) public static void CalculateFrustumPlanes(float4x4 finalMatrix, ref plane_array outPlanes)
{ {
const int kPlaneFrustumLeft = 0; const int planeFrustumLeft = 0;
const int kPlaneFrustumRight = 1; const int planeFrustumRight = 1;
const int kPlaneFrustumBottom = 2; const int planeFrustumBottom = 2;
const int kPlaneFrustumTop = 3; const int planeFrustumTop = 3;
const int kPlaneFrustumNear = 4; const int planeFrustumNear = 4;
const int kPlaneFrustumFar = 5; const int planeFrustumFar = 5;
float4 tmpVec = default; float4 tmpVec = default;
float4 otherVec = default; float4 otherVec = default;
@@ -66,8 +66,8 @@ public struct Frustum
leftNormalY *= leftInvMagnitude; leftNormalY *= leftInvMagnitude;
leftNormalZ *= leftInvMagnitude; leftNormalZ *= leftInvMagnitude;
leftDistance *= leftInvMagnitude; leftDistance *= leftInvMagnitude;
outPlanes[kPlaneFrustumLeft].xyz = new float3(leftNormalX, leftNormalY, leftNormalZ); outPlanes[planeFrustumLeft].xyz = new float3(leftNormalX, leftNormalY, leftNormalZ);
outPlanes[kPlaneFrustumLeft].w = leftDistance; outPlanes[planeFrustumLeft].w = leftDistance;
var rightNormalX = -otherVec[0] + tmpVec[0]; var rightNormalX = -otherVec[0] + tmpVec[0];
var rightNormalY = -otherVec[1] + tmpVec[1]; var rightNormalY = -otherVec[1] + tmpVec[1];
@@ -80,8 +80,8 @@ public struct Frustum
rightNormalY *= rightInvMagnitude; rightNormalY *= rightInvMagnitude;
rightNormalZ *= rightInvMagnitude; rightNormalZ *= rightInvMagnitude;
rightDistance *= rightInvMagnitude; rightDistance *= rightInvMagnitude;
outPlanes[kPlaneFrustumRight].xyz = new float3(rightNormalX, rightNormalY, rightNormalZ); outPlanes[planeFrustumRight].xyz = new float3(rightNormalX, rightNormalY, rightNormalZ);
outPlanes[kPlaneFrustumRight].w = rightDistance; outPlanes[planeFrustumRight].w = rightDistance;
// bottom & top // bottom & top
otherVec[0] = finalMatrix[0][1]; otherVec[0] = finalMatrix[0][1];
@@ -100,8 +100,8 @@ public struct Frustum
bottomNormalY *= bottomInvMagnitude; bottomNormalY *= bottomInvMagnitude;
bottomNormalZ *= bottomInvMagnitude; bottomNormalZ *= bottomInvMagnitude;
bottomDistance *= bottomInvMagnitude; bottomDistance *= bottomInvMagnitude;
outPlanes[kPlaneFrustumBottom].xyz = new float3(bottomNormalX, bottomNormalY, bottomNormalZ); outPlanes[planeFrustumBottom].xyz = new float3(bottomNormalX, bottomNormalY, bottomNormalZ);
outPlanes[kPlaneFrustumBottom].w = bottomDistance; outPlanes[planeFrustumBottom].w = bottomDistance;
var topNormalX = -otherVec[0] + tmpVec[0]; var topNormalX = -otherVec[0] + tmpVec[0];
var topNormalY = -otherVec[1] + tmpVec[1]; var topNormalY = -otherVec[1] + tmpVec[1];
@@ -114,8 +114,8 @@ public struct Frustum
topNormalY *= topInvMagnitude; topNormalY *= topInvMagnitude;
topNormalZ *= topInvMagnitude; topNormalZ *= topInvMagnitude;
topDistance *= topInvMagnitude; topDistance *= topInvMagnitude;
outPlanes[kPlaneFrustumTop].xyz = new float3(topNormalX, topNormalY, topNormalZ); outPlanes[planeFrustumTop].xyz = new float3(topNormalX, topNormalY, topNormalZ);
outPlanes[kPlaneFrustumTop].w = topDistance; outPlanes[planeFrustumTop].w = topDistance;
// near & far // near & far
otherVec[0] = finalMatrix[0][2]; otherVec[0] = finalMatrix[0][2];
@@ -134,8 +134,8 @@ public struct Frustum
nearNormalY *= nearInvMagnitude; nearNormalY *= nearInvMagnitude;
nearNormalZ *= nearInvMagnitude; nearNormalZ *= nearInvMagnitude;
nearDistance *= nearInvMagnitude; nearDistance *= nearInvMagnitude;
outPlanes[kPlaneFrustumNear].xyz = new float3(nearNormalX, nearNormalY, nearNormalZ); outPlanes[planeFrustumNear].xyz = new float3(nearNormalX, nearNormalY, nearNormalZ);
outPlanes[kPlaneFrustumNear].w = nearDistance; outPlanes[planeFrustumNear].w = nearDistance;
var farNormalX = -otherVec[0] + tmpVec[0]; var farNormalX = -otherVec[0] + tmpVec[0];
var farNormalY = -otherVec[1] + tmpVec[1]; var farNormalY = -otherVec[1] + tmpVec[1];
@@ -148,8 +148,8 @@ public struct Frustum
farNormalY *= farInvMagnitude; farNormalY *= farInvMagnitude;
farNormalZ *= farInvMagnitude; farNormalZ *= farInvMagnitude;
farDistance *= farInvMagnitude; farDistance *= farInvMagnitude;
outPlanes[kPlaneFrustumFar].xyz = new float3(farNormalX, farNormalY, farNormalZ); outPlanes[planeFrustumFar].xyz = new float3(farNormalX, farNormalY, farNormalZ);
outPlanes[kPlaneFrustumFar].w = farDistance; outPlanes[planeFrustumFar].w = farDistance;
} }
} }
@@ -177,7 +177,7 @@ public struct RenderView
public RenderingLayerMask renderingLayerMask; public RenderingLayerMask renderingLayerMask;
} }
public unsafe struct RenderRequest public unsafe struct RenderRequest: IDisposable
{ {
public RenderView view; public RenderView view;
@@ -189,4 +189,11 @@ public unsafe struct RenderRequest
public RenderList shadowCasterRenderList; public RenderList shadowCasterRenderList;
public delegate*<ref readonly RenderContext, ref readonly RenderRequest, void> renderFunc; public delegate*<ref readonly RenderContext, ref readonly RenderRequest, void> renderFunc;
public void Dispose()
{
opaqueRenderList.Dispose();
transparentRenderList.Dispose();
shadowCasterRenderList.Dispose();
}
} }

View File

@@ -47,7 +47,7 @@ public partial class GhostRenderPipeline
private readonly uint _padding1; private readonly uint _padding1;
private readonly uint _padding2; private readonly uint _padding2;
} }
#if flase
private void RenderTest(RenderGraph graph, Identifier<RGTexture> backbuffer) private void RenderTest(RenderGraph graph, Identifier<RGTexture> backbuffer)
{ {
Identifier<RGTexture> renderTarget; Identifier<RGTexture> renderTarget;
@@ -107,4 +107,5 @@ public partial class GhostRenderPipeline
}); });
} }
} }
# endif
} }

View File

@@ -1,7 +1,10 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12; using Ghost.Graphics.D3D12;
using Ghost.Graphics.RenderPipeline; using Ghost.Graphics.RenderPipeline;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
@@ -70,14 +73,16 @@ public class RenderSystem : IDisposable
private readonly FrameResource[] _frameResources; private readonly FrameResource[] _frameResources;
private readonly Thread _renderThread; private readonly Thread _renderThread;
private readonly AutoResetEvent _shutdownEvent; private readonly AutoResetEvent _shutdownEvent;
private UnsafeArray<UnsafeList<RenderRequest>> _renderRequests;
private readonly ConcurrentDictionary<ISwapChain, uint2> _resizeRequest; private readonly ConcurrentDictionary<ISwapChain, uint2> _resizeRequest;
private IRenderPipelineSettings _renderPipelineSettings; private IRenderPipelineSettings _renderPipelineSettings;
private IRenderPipeline _renderPipeline; private IRenderPipeline _renderPipeline;
private uint _frameIndex; private uint _frameIndex;
private uint _cpuFenceValue; private ulong _cpuFenceValue;
private uint _gpuFenceValue; private ulong _gpuFenceValue;
private bool _isRunning; private bool _isRunning;
private bool _disposed; private bool _disposed;
@@ -86,8 +91,8 @@ public class RenderSystem : IDisposable
public ResourceManager ResourceManager => _resourceManager; public ResourceManager ResourceManager => _resourceManager;
public bool IsRunning => _isRunning; public bool IsRunning => _isRunning;
public uint CPUFenceValue => _cpuFenceValue; public ulong CPUFenceValue => _cpuFenceValue;
public uint GPUFenceValue => _gpuFenceValue; public ulong GPUFenceValue => _gpuFenceValue;
public uint FrameIndex => _frameIndex; public uint FrameIndex => _frameIndex;
public uint MaxFrameLatency => _config.FrameBufferCount; public uint MaxFrameLatency => _config.FrameBufferCount;
@@ -160,6 +165,11 @@ public class RenderSystem : IDisposable
_shutdownEvent = new AutoResetEvent(false); _shutdownEvent = new AutoResetEvent(false);
_resizeRequest = new ConcurrentDictionary<ISwapChain, uint2>(); _resizeRequest = new ConcurrentDictionary<ISwapChain, uint2>();
_renderRequests = new UnsafeArray<UnsafeList<RenderRequest>>((int)desc.FrameBufferCount, Allocator.Persistent);
for (var i = 0; i < desc.FrameBufferCount; i++)
{
_renderRequests[i] = new UnsafeList<RenderRequest>(2, Allocator.Persistent);
}
_renderPipelineSettings = new GhostRenderPipelineSettings(); _renderPipelineSettings = new GhostRenderPipelineSettings();
_renderPipeline = _renderPipelineSettings.CreatePipeline(this); _renderPipeline = _renderPipelineSettings.CreatePipeline(this);
@@ -191,7 +201,7 @@ public class RenderSystem : IDisposable
while (_isRunning) while (_isRunning)
{ {
_frameIndex = _gpuFenceValue % _config.FrameBufferCount; _frameIndex = (uint)(_gpuFenceValue % _config.FrameBufferCount);
ref var frameResource = ref _frameResources[_frameIndex]; ref var frameResource = ref _frameResources[_frameIndex];
// Wait for either CPU ready signal or shutdown signal // Wait for either CPU ready signal or shutdown signal
@@ -214,7 +224,6 @@ public class RenderSystem : IDisposable
if (!_resizeRequest.IsEmpty) if (!_resizeRequest.IsEmpty)
{ {
//WaitIdle();
_gpuFenceValue++; _gpuFenceValue++;
var flushFence = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue); var flushFence = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue);
_graphicsEngine.Device.GraphicsQueue.WaitForValue(flushFence); _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 continue; // Skip rendering this frame since we just resized and may have invalid render targets
} }
// Begin rendering for this frame
frameResource.CommandAllocator.Reset(); frameResource.CommandAllocator.Reset();
var r = _graphicsEngine.BeginFrame(_cpuFenceValue, _gpuFenceValue); _resourceManager.BeginFrame(_cpuFenceValue);
var r = _graphicsEngine.BeginFrame(_cpuFenceValue);
if (r.IsFailure) if (r.IsFailure)
{ {
StopRenderLoop(r); 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 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) if (r.IsFailure)
{ {
StopRenderLoop(r); 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++; _gpuFenceValue++;
frameResource.GpuReadyEvent.Set(); frameResource.GpuReadyEvent.Set();
@@ -312,6 +358,14 @@ public class RenderSystem : IDisposable
_resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize); _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) public bool WaitForGPUReady(int timeOut = -1)
{ {
Debug.Assert(!_disposed, "Cannot wait for GPU ready on a disposed RenderSystem."); Debug.Assert(!_disposed, "Cannot wait for GPU ready on a disposed RenderSystem.");
@@ -346,6 +400,16 @@ public class RenderSystem : IDisposable
frameResource.Dispose(); frameResource.Dispose();
} }
foreach (ref var renderRequestList in _renderRequests)
{
foreach (ref var request in renderRequestList)
{
request.Dispose();
}
renderRequestList.Dispose();
}
_graphicsEngine.Dispose(); _graphicsEngine.Dispose();
_shutdownEvent.Dispose(); _shutdownEvent.Dispose();

View File

@@ -4,11 +4,24 @@ using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Diagnostics;
namespace Ghost.Graphics; namespace Ghost.Graphics;
public sealed class ResourceManager : IDisposable public sealed class ResourceManager : IDisposable
{ {
private readonly struct ResourceReturnEntry
{
public readonly Handle<GPUResource> handle;
public readonly ulong returnFrame;
public ResourceReturnEntry(Handle<GPUResource> handle, ulong returnFrame)
{
this.handle = handle;
this.returnFrame = returnFrame;
}
}
private readonly IResourceAllocator _resourceAllocator; private readonly IResourceAllocator _resourceAllocator;
private readonly IResourceDatabase _resourceDatabase; private readonly IResourceDatabase _resourceDatabase;
@@ -18,6 +31,11 @@ public sealed class ResourceManager : IDisposable
private readonly MaterialPaletteStore _materialPalettes; private readonly MaterialPaletteStore _materialPalettes;
private ulong _currentFrame;
private UnsafeHashMap<ResourceDesc, UnsafeQueue<Handle<GPUResource>>> _resourceCache;
private UnsafeQueue<ResourceReturnEntry> _resourceReturnQueue;
private bool _disposed; private bool _disposed;
public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase) public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
@@ -28,7 +46,11 @@ public sealed class ResourceManager : IDisposable
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent); _meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent);
_materials = new UnsafeSlotMap<Material>(64, Allocator.Persistent); _materials = new UnsafeSlotMap<Material>(64, Allocator.Persistent);
_shaders = new UnsafeList<Shader>(16, Allocator.Persistent); _shaders = new UnsafeList<Shader>(16, Allocator.Persistent);
_materialPalettes = new MaterialPaletteStore(); _materialPalettes = new MaterialPaletteStore();
_resourceCache = new UnsafeHashMap<ResourceDesc, UnsafeQueue<Handle<GPUResource>>>(32, Allocator.Persistent);
_resourceReturnQueue = new UnsafeQueue<ResourceReturnEntry>(32, Allocator.Persistent);
} }
~ResourceManager() ~ResourceManager()
@@ -36,6 +58,32 @@ public sealed class ResourceManager : IDisposable
Dispose(); 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<Handle<GPUResource>>(4, Allocator.Persistent);
}
queue.Enqueue(entry.handle);
}
}
/// <summary> /// <summary>
/// Creates a new mesh from the specified vertex and index data. /// Creates a new mesh from the specified vertex and index data.
/// </summary> /// </summary>
@@ -44,7 +92,7 @@ public sealed class ResourceManager : IDisposable
/// <returns>An <see cref="Identifier{Mesh}"/> representing the newly created mesh.</returns> /// <returns>An <see cref="Identifier{Mesh}"/> representing the newly created mesh.</returns>
public unsafe Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices) public unsafe Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
var vertexBufferDesc = new BufferDesc var vertexBufferDesc = new BufferDesc
{ {
@@ -94,7 +142,7 @@ public sealed class ResourceManager : IDisposable
/// <returns>An <see cref="Identifier{Material}"/> representing the newly created material.</returns> /// <returns>An <see cref="Identifier{Material}"/> representing the newly created material.</returns>
public Handle<Material> CreateMaterial(Identifier<Shader> shader) public Handle<Material> CreateMaterial(Identifier<Shader> shader)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
var material = new Material(); var material = new Material();
if (material.SetShader(shader, this, _resourceDatabase, _resourceAllocator) != Error.None) if (material.SetShader(shader, this, _resourceDatabase, _resourceAllocator) != Error.None)
@@ -113,7 +161,7 @@ public sealed class ResourceManager : IDisposable
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param> /// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor) public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
var shader = new Shader(descriptor); var shader = new Shader(descriptor);
@@ -129,7 +177,7 @@ public sealed class ResourceManager : IDisposable
/// <returns>true if a mesh with the specified Handle exists; otherwise, false.</returns> /// <returns>true if a mesh with the specified Handle exists; otherwise, false.</returns>
public bool HasMesh(Handle<Mesh> handle) public bool HasMesh(Handle<Mesh> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
return _meshes.Contains(handle.ID, handle.Generation); return _meshes.Contains(handle.ID, handle.Generation);
} }
@@ -155,7 +203,7 @@ public sealed class ResourceManager : IDisposable
/// <param name="handle">The handle of the mesh to release. Must refer to a mesh that was previously created and not already released.</param> /// <param name="handle">The handle of the mesh to release. Must refer to a mesh that was previously created and not already released.</param>
public void ReleaseMesh(Handle<Mesh> handle) public void ReleaseMesh(Handle<Mesh> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
if (!_meshes.TryGetElementAt(handle.ID, handle.Generation, out var mesh)) if (!_meshes.TryGetElementAt(handle.ID, handle.Generation, out var mesh))
{ {
@@ -173,7 +221,7 @@ public sealed class ResourceManager : IDisposable
/// <returns>true if a material with the specified handle exists; otherwise, false.</returns> /// <returns>true if a material with the specified handle exists; otherwise, false.</returns>
public bool HasMaterial(Handle<Material> handle) public bool HasMaterial(Handle<Material> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
return _materials.Contains(handle.ID, handle.Generation); return _materials.Contains(handle.ID, handle.Generation);
} }
@@ -199,7 +247,7 @@ public sealed class ResourceManager : IDisposable
/// <param name="handle">The handle of the material to release. Must refer to a material that has been previously acquired.</param> /// <param name="handle">The handle of the material to release. Must refer to a material that has been previously acquired.</param>
public void ReleaseMaterial(Handle<Material> handle) public void ReleaseMaterial(Handle<Material> handle)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
var material = _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist); var material = _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist) if (!exist)
@@ -218,7 +266,7 @@ public sealed class ResourceManager : IDisposable
/// <returns>The palette index. Index 0 represents an empty palette.</returns> /// <returns>The palette index. Index 0 represents an empty palette.</returns>
public int GetOrCreateMaterialPalette(ReadOnlySpan<Handle<Material>> materials) public int GetOrCreateMaterialPalette(ReadOnlySpan<Handle<Material>> materials)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
foreach (var material in materials) foreach (var material in materials)
{ {
@@ -237,7 +285,7 @@ public sealed class ResourceManager : IDisposable
/// <param name="paletteID">The palette index to validate.</param> /// <param name="paletteID">The palette index to validate.</param>
public bool HasMaterialPalette(Identifier<MaterialPalette> paletteID) public bool HasMaterialPalette(Identifier<MaterialPalette> paletteID)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
return _materialPalettes.IsValid(paletteID); return _materialPalettes.IsValid(paletteID);
} }
@@ -247,7 +295,7 @@ public sealed class ResourceManager : IDisposable
/// <param name="paletteID">The palette index to query.</param> /// <param name="paletteID">The palette index to query.</param>
public MaterialPalette GetMaterialPaletteInfo(Identifier<MaterialPalette> paletteID) public MaterialPalette GetMaterialPaletteInfo(Identifier<MaterialPalette> paletteID)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
return _materialPalettes.GetInfo(paletteID); return _materialPalettes.GetInfo(paletteID);
} }
@@ -258,7 +306,7 @@ public sealed class ResourceManager : IDisposable
/// <param name="localMaterialIndex">The material slot inside the palette.</param> /// <param name="localMaterialIndex">The material slot inside the palette.</param>
public Handle<Material> GetMaterialPaletteMaterial(Identifier<MaterialPalette> paletteID, int localMaterialIndex) public Handle<Material> GetMaterialPaletteMaterial(Identifier<MaterialPalette> paletteID, int localMaterialIndex)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
return _materialPalettes.GetMaterial(paletteID, localMaterialIndex); return _materialPalettes.GetMaterial(paletteID, localMaterialIndex);
} }
@@ -268,7 +316,7 @@ public sealed class ResourceManager : IDisposable
/// <param name="paletteID">The palette index to release.</param> /// <param name="paletteID">The palette index to release.</param>
public void ReleaseMaterialPalette(Identifier<MaterialPalette> paletteID) public void ReleaseMaterialPalette(Identifier<MaterialPalette> paletteID)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
_materialPalettes.Release(paletteID); _materialPalettes.Release(paletteID);
} }
@@ -279,7 +327,7 @@ public sealed class ResourceManager : IDisposable
/// <returns>true if a shader with the specified identifier exists; otherwise, false.</returns> /// <returns>true if a shader with the specified identifier exists; otherwise, false.</returns>
public bool HasShader(Identifier<Shader> id) public bool HasShader(Identifier<Shader> id)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
return id.Value >= 0 && id.Value < _shaders.Count; return id.Value >= 0 && id.Value < _shaders.Count;
} }
@@ -304,7 +352,7 @@ public sealed class ResourceManager : IDisposable
/// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param> /// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param>
public void ReleaseShader(Identifier<Shader> id) public void ReleaseShader(Identifier<Shader> id)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); Debug.Assert(!_disposed);
if (!HasShader(id)) if (!HasShader(id))
{ {
@@ -315,6 +363,38 @@ public sealed class ResourceManager : IDisposable
shader.ReleaseResource(_resourceDatabase); shader.ReleaseResource(_resourceDatabase);
} }
public Handle<GPUResource> GetPooledResource(in ResourceDesc desc)
{
Debug.Assert(!_disposed);
ref var queue = ref _resourceCache.GetValueRef(desc, out var exist);
if (exist && queue.TryDequeue(out Handle<GPUResource> 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<GPUResource> handle)
{
Debug.Assert(!_disposed);
if (handle.IsInvalid)
{
return;
}
_resourceReturnQueue.Enqueue(new ResourceReturnEntry(handle, _currentFrame));
}
public void Dispose() public void Dispose()
{ {
if (_disposed) if (_disposed)
@@ -342,6 +422,20 @@ public sealed class ResourceManager : IDisposable
_shaders.Dispose(); _shaders.Dispose();
_materialPalettes.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; _disposed = true;
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }