feat(engine): refactor resource mgmt & render pipeline
Refactors engine infrastructure for improved resource/service management and render pipeline extensibility. Replaces World’s resource API with a service-based API. Splits IGraphicsEngine’s RenderFrame into BeginFrame/EndFrame. Adds support for pluggable render pipelines in RenderSystem. Replaces disposed checks with Debug.Assert in performance paths. Updates RenderExtractionSystem and render loop for new APIs. Improves diagnostics and code clarity. BREAKING CHANGE: Resource API replaced with service API; render pipeline and frame lifecycle interfaces changed.
This commit is contained in:
@@ -79,6 +79,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
Dispose();
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using System.Diagnostics;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
@@ -65,7 +66,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public void Submit(ICommandBuffer commandBuffer)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (commandBuffer.IsEmpty)
|
||||
{
|
||||
@@ -86,7 +87,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
Span<int> executableIndices = stackalloc int[commandBuffers.Length];
|
||||
executableIndices.Fill(-1);
|
||||
@@ -129,7 +130,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public ulong Signal(ulong value)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
_fenceValue = value;
|
||||
ThrowIfFailed(_commandQueue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue));
|
||||
@@ -138,7 +139,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public void WaitForValue(ulong value)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (_fence.Get()->GetCompletedValue() < value)
|
||||
{
|
||||
@@ -152,13 +153,13 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
|
||||
public ulong GetCompletedValue()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return _fence.Get()->GetCompletedValue();
|
||||
}
|
||||
|
||||
public void WaitIdle()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
|
||||
WaitForValue(fenceValue);
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
#if DEBUG
|
||||
#define ENABLE_DEBUG
|
||||
#define ENABLE_DEBUG_LAYER
|
||||
#endif
|
||||
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
public static class D3D12GraphicsEngineFactory
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IGraphicsEngine Create(GraphicsEngineDesc desc)
|
||||
{
|
||||
return new D3D12GraphicsEngine(desc);
|
||||
@@ -22,7 +24,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
{
|
||||
private readonly GraphicsEngineDesc _desc;
|
||||
|
||||
#if ENABLE_DEBUG
|
||||
#if ENABLE_DEBUG_LAYER
|
||||
private readonly D3D12DebugLayer _debugLayer;
|
||||
#endif
|
||||
private readonly D3D12RenderDevice _device;
|
||||
@@ -46,7 +48,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
{
|
||||
_desc = desc;
|
||||
|
||||
#if ENABLE_DEBUG
|
||||
#if ENABLE_DEBUG_LAYER
|
||||
_debugLayer = new D3D12DebugLayer();
|
||||
#endif
|
||||
_device = new D3D12RenderDevice();
|
||||
@@ -67,6 +69,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
Dispose();
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
@@ -118,28 +121,20 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _desc.FrameBufferCount);
|
||||
}
|
||||
|
||||
public Result RenderFrame(ICommandAllocator commandAllocator, uint cpuFenceValue, uint gpuFenceValue)
|
||||
public Result BeginFrame(uint cpuFenceValue, uint gpuFenceValue)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var r = Result.Success();
|
||||
|
||||
_resourceDatabase.BeginFrame(cpuFenceValue);
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
// TODO: We should not handle renderers in graphics engine since the purpose of graphics engine is to provide low-level graphics resource management and command buffer creation.
|
||||
// We need to migrate this to IRenderPipeline instead when it's ready.
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
r = renderer.Render(commandAllocator);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
public Result EndFrame(uint cpuFenceValue, uint gpuFenceValue)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
_resourceDatabase.EndFrame(gpuFenceValue);
|
||||
|
||||
return r;
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -163,7 +158,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
_descriptorAllocator.Dispose();
|
||||
_shaderCompiler.Dispose();
|
||||
_device.Dispose();
|
||||
#if ENABLE_DEBUG
|
||||
#if ENABLE_DEBUG_LAYER
|
||||
_debugLayer.Dispose();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using Ghost.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
@@ -601,7 +602,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, string name, CreationOptions options = default)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
CheckTexture2DSize(desc.Width, desc.Height);
|
||||
|
||||
@@ -699,7 +700,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, string name, CreationOptions options = default)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var textureDesc = desc.ToTextureDescription();
|
||||
return CreateTexture(in textureDesc, name, options);
|
||||
@@ -707,7 +708,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
public Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, string name, CreationOptions options = default)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
CheckBufferSize(desc.Size);
|
||||
|
||||
var resourceDesc = desc.ToD3D12ResourceDesc();
|
||||
@@ -839,7 +840,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
public Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (_resourceDatabase.TryGetSampler(in desc, out var id))
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
@@ -130,7 +131,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
internal unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceBarrierData initialBarrierData, ResourceViewGroup viewGroup, string? name = null)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (pResource == null)
|
||||
{
|
||||
@@ -156,7 +157,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
public unsafe Handle<GPUResource> AddAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData initialBarrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
if (allocation == null)
|
||||
{
|
||||
#if DEBUG
|
||||
@@ -186,13 +187,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
public bool HasResource(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return _resources.Contains(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public RefResult<ResourceRecord, Error> GetResourceRecord(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
ref var info = ref _resources.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
@@ -267,7 +268,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
public string? GetResourceName(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (_resourceName.TryGetValue(handle, out var name))
|
||||
@@ -280,7 +281,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
public void ReleaseResource(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (_resources.TryGetElementAt(handle.ID, handle.Generation, out var record))
|
||||
{
|
||||
@@ -299,7 +300,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
public void ReleaseResourceImmediately(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
ref var info = ref _resources.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist || !info.Allocated)
|
||||
@@ -313,7 +314,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
public Identifier<Sampler> AddSampler(ref readonly SamplerDesc desc, int id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (_samplers.ContainsKey(desc))
|
||||
{
|
||||
@@ -328,13 +329,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
public bool TryGetSampler(ref readonly SamplerDesc desc, out Identifier<Sampler> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return _samplers.TryGetValue(desc, out id);
|
||||
}
|
||||
|
||||
public void ReleaseSampler(Identifier<Sampler> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
// NOTE: We almost never release samplers individually, because they are cheap and can be reused.
|
||||
// Ideally we would release all samplers at once when disposing the ResourceDatabase.
|
||||
@@ -360,13 +361,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
public void BeginFrame(uint currentFrameFenceValue)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
_currentFrameFenceValue = currentFrameFenceValue;
|
||||
}
|
||||
|
||||
public void EndFrame(uint completedFenceValue)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
while (_releaseQueue.Count > 0)
|
||||
{
|
||||
@@ -384,7 +385,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
|
||||
internal void ReleaseAllResourcesImmediately()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
foreach (ref var record in _resources)
|
||||
{
|
||||
|
||||
@@ -68,7 +68,9 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
SetScale(desc.ScaleX, desc.ScaleY);
|
||||
|
||||
if (desc.Target.Type == SwapChainTargetType.Composition)
|
||||
{
|
||||
_compositionSurface = desc.Target.CompositionSurface;
|
||||
}
|
||||
}
|
||||
|
||||
~D3D12SwapChain()
|
||||
@@ -169,20 +171,20 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Handle<Texture> GetCurrentBackBuffer()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<Handle<Texture>> GetBackBuffers()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
return _backBuffers.AsSpan();
|
||||
}
|
||||
|
||||
public void Present(bool vsync = true)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var presentFlags = 0u;
|
||||
var syncInterval = vsync ? 1u : 0u;
|
||||
@@ -192,7 +194,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
|
||||
public void Resize(uint width, uint height)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (Width == width && Height == height)
|
||||
{
|
||||
@@ -215,6 +217,8 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
||||
|
||||
public void SetScale(float scaleX, float scaleY)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var inverseScaleX = 1.0f / scaleX;
|
||||
var inverseScaleY = 1.0f / scaleY;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user