Refactoring rendering system.

Added new IRenderSystem and IFenceSynchronizer

Changed IRenderer managment from RenderSystem to IGraphicsEngine
This commit is contained in:
2025-11-07 16:46:21 +09:00
parent 15aca9aefb
commit 56f73e774b
8 changed files with 216 additions and 94 deletions

View File

@@ -14,9 +14,6 @@ using Ghost.Graphics.Core;
namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of command buffer interface
/// </summary>
internal unsafe class D3D12CommandBuffer : ICommandBuffer
{
private ComPtr<ID3D12CommandAllocator> _allocator;
@@ -26,14 +23,15 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
private readonly D3D12ResourceDatabase _resourceDatabase;
private readonly D3D12ResourceAllocator _resourceAllocator;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly CommandBufferType _type;
private string _name;
private readonly CommandBufferType _type;
private ushort _commandCount;
private bool _isRecording;
private bool _disposed;
public ID3D12GraphicsCommandList10* NativeCommandList => _commandList.Get();
public CommandBufferType Type => _type;
public bool IsEmpty => _commandCount == 0;
@@ -76,6 +74,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
// Command lists are created in recording state, so close it
_commandList.Get()->Close();
_isRecording = false;
_disposed = false;
}
~D3D12CommandBuffer()
@@ -83,12 +82,32 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
Dispose();
}
private static D3D12_COMMAND_LIST_TYPE ConvertCommandBufferType(CommandBufferType type)
{
return type switch
{
CommandBufferType.Graphics => D3D12_COMMAND_LIST_TYPE_DIRECT,
CommandBufferType.Compute => D3D12_COMMAND_LIST_TYPE_COMPUTE,
CommandBufferType.Copy => D3D12_COMMAND_LIST_TYPE_COPY,
_ => throw new ArgumentException($"Unknown command buffer type: {type}")
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfRecording()
{
if (_isRecording)
{
throw new InvalidOperationException("Command buffer is already recording");
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfNotRecording()
{
@@ -121,11 +140,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
}
ThrowIfDisposed();
if (_isRecording)
{
throw new InvalidOperationException("Command buffer is already recording");
}
ThrowIfRecording();
ResetCommandList();
SetBindlessHeap();
@@ -196,7 +211,9 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false)
{
// TODO: Implement render pass begin
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
var pRtvDescs = stackalloc D3D12_RENDER_PASS_RENDER_TARGET_DESC[rtDescs.Length];
for (var i = 0; i < rtDescs.Length; i++)
@@ -299,6 +316,10 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer)
{
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
var resource = _resourceDatabase.GetResource(buffer.AsResource());
_commandList.Get()->SetGraphicsRootConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, resource->GetGPUVirtualAddress());
}
@@ -339,6 +360,10 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
public void SetPrimitiveTopology(PrimitiveTopology topology)
{
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
var d3d12Topology = topology switch
{
PrimitiveTopology.Point => D3D_PRIMITIVE_TOPOLOGY_POINTLIST,
@@ -480,29 +505,18 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
}
}
private static D3D12_COMMAND_LIST_TYPE ConvertCommandBufferType(CommandBufferType type)
{
return type switch
{
CommandBufferType.Graphics => D3D12_COMMAND_LIST_TYPE_DIRECT,
CommandBufferType.Compute => D3D12_COMMAND_LIST_TYPE_COMPUTE,
CommandBufferType.Copy => D3D12_COMMAND_LIST_TYPE_COPY,
_ => throw new ArgumentException($"Unknown command buffer type: {type}")
};
}
public void Dispose()
{
if (_isRecording)
{
throw new InvalidOperationException("Command buffer is still recording");
}
if (_disposed)
{
return;
}
if (_isRecording)
{
throw new InvalidOperationException("Command buffer is still recording");
}
_commandList.Dispose();
_allocator.Dispose();
_isRecording = false;

View File

@@ -1,3 +1,6 @@
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using Ghost.Graphics.RHI;
namespace Ghost.Graphics.D3D12;
@@ -11,18 +14,20 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
private readonly D3D12RenderDevice _device;
private readonly D3D12PipelineLibrary _pipelineLibrary;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly D3D12ResourceDatabase _resourceDatabase;
private readonly D3D12ResourceAllocator _resourceAllocator;
private readonly D3D12CommandBuffer _copyCommandBuffer;
private ImmutableArray<IRenderer> _renderers;
private bool _disposed;
public IRenderDevice Device => _device;
public IPipelineLibrary PipelineLibrary => _pipelineLibrary;
public IResourceDatabase ResourceDatabase => _resourceDatabase;
public IResourceAllocator ResourceAllocator => _resourceAllocator;
public D3D12GraphicsEngine(RenderSystem renderSystem)
public D3D12GraphicsEngine(IRenderSystem renderSystem)
{
#if DEBUG
_debugLayer = new();
@@ -41,6 +46,8 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
_resourceAllocator,
_descriptorAllocator,
CommandBufferType.Copy);
_renderers = ImmutableArray<IRenderer>.Empty;
}
~D3D12GraphicsEngine()
@@ -48,13 +55,40 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
Dispose();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(D3D12GraphicsEngine));
}
}
public IRenderer CreateRenderer()
{
return new D3D12Renderer(this, _resourceAllocator, _resourceDatabase);
ThrowIfDisposed();
var renderer = new D3D12Renderer(this, _resourceAllocator, _resourceDatabase);
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 ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics)
{
ThrowIfDisposed();
return new D3D12CommandBuffer(
_device,
_pipelineLibrary,
@@ -66,21 +100,43 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
public ISwapChain CreateSwapChain(SwapChainDesc desc)
{
ThrowIfDisposed();
return new D3D12SwapChain(_resourceDatabase, _device.DXGIFactory, ((D3D12CommandQueue)_device.ComputeQueue).NativeQueue, desc);
}
public void BeginFrame()
{
throw new NotImplementedException();
ThrowIfDisposed();
foreach (var renderer in _renderers)
{
renderer.ExecutePendingResize();
}
}
public void RenderFrame()
{
ThrowIfDisposed();
foreach (var renderer in _renderers)
{
renderer.Render();
}
}
public void EndFrame()
{
throw new NotImplementedException();
ThrowIfDisposed();
_resourceAllocator.ReleaseTempResources();
}
public void Dispose()
{
if (_disposed)
{
return;
}
_copyCommandBuffer.Dispose();
_pipelineLibrary.Dispose();
@@ -93,6 +149,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
_debugLayer.Dispose();
#endif
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -3,7 +3,6 @@ using Ghost.Core.Graphics;
using Ghost.Core.Utilities;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI;
using Ghost.Graphics.D3D12.Utilities;
using Misaki.HighPerformance.Mathematics;
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices;
@@ -588,11 +587,13 @@ internal unsafe sealed partial class D3D12ResourceAllocator
}
}
// TODO: Dedicated pool for copy, render graph, and persistent resources
// TODO: Thread safety for resource allocator
// A common solution is to use ticket. Each allocation request create a ticket and put it into a thread-safe queue. A dedicated thread process the queue and fulfill the requests.
internal unsafe sealed partial class D3D12ResourceAllocator : IResourceAllocator, IDisposable
{
private readonly RenderSystem _renderSystem;
private readonly IFenceSynchronizer _fenceSynchronizer;
private readonly D3D12RenderDevice _device;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly D3D12ResourceDatabase _resourceDatabase;
@@ -602,7 +603,7 @@ internal unsafe sealed partial class D3D12ResourceAllocator : IResourceAllocator
private bool _disposed;
public D3D12ResourceAllocator(RenderSystem renderSystem, D3D12RenderDevice device, D3D12DescriptorAllocator descriptorAllocator, D3D12ResourceDatabase resourceDatabase)
public D3D12ResourceAllocator(IFenceSynchronizer fenceSynchronizer, D3D12RenderDevice device, D3D12DescriptorAllocator descriptorAllocator, D3D12ResourceDatabase resourceDatabase)
{
var desc = new D3D12MA_ALLOCATOR_DESC
{
@@ -616,7 +617,7 @@ internal unsafe sealed partial class D3D12ResourceAllocator : IResourceAllocator
_allocator.Attach(pAllocator);
_device = device;
_renderSystem = renderSystem;
_fenceSynchronizer = fenceSynchronizer;
_descriptorAllocator = descriptorAllocator;
_resourceDatabase = resourceDatabase;
@@ -631,7 +632,7 @@ internal unsafe sealed partial class D3D12ResourceAllocator : IResourceAllocator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Handle<GPUResource> TrackResource(ComPtr<D3D12MA_Allocation> allocation, D3D12_RESOURCE_STATES state, ResourceViewGroup resourceDescriptor, ResourceDesc desc, bool isTemp)
{
var handle = _resourceDatabase.AddResource(allocation, _renderSystem.CPUFenceValue, D3D12StatesToRHIState(state), resourceDescriptor, desc);
var handle = _resourceDatabase.AddResource(allocation, _fenceSynchronizer.CPUFenceValue, D3D12StatesToRHIState(state), resourceDescriptor, desc);
if (isTemp)
{
@@ -884,7 +885,7 @@ internal unsafe sealed partial class D3D12ResourceAllocator : IResourceAllocator
continue;
}
if (info.cpuFenceValue > _renderSystem.GPUFenceValue)
if (info.cpuFenceValue > _fenceSynchronizer.GPUFenceValue)
{
// Resource still in use by GPU, stop processing.
// Since resources are enqueued in order, we can break here.
@@ -896,11 +897,6 @@ internal unsafe sealed partial class D3D12ResourceAllocator : IResourceAllocator
}
}
public void ReleaseResource(Handle<GPUResource> handle)
{
_resourceDatabase.ReleaseResource(handle);
}
public void Dispose()
{
if (_disposed)
@@ -917,7 +913,7 @@ internal unsafe sealed partial class D3D12ResourceAllocator : IResourceAllocator
foreach (var handle in _temResources)
{
ReleaseResource(handle);
_resourceDatabase.ReleaseResource(handle);
}
_temResources.Dispose();

View File

@@ -371,7 +371,7 @@ internal static unsafe class D3D12ShaderCompiler
break;
}
// NOTE: Currently we are not support resource bindings yet, everything access through bindless heaps.
// NOTE: Currently we do not support resource bindings yet, everything access through bindless heaps.
default:
{
reflectionData.OtherResources.Add(new ResourceBindingInfo