Refactor render pipeline and resource management APIs
Split IFenceSynchronizer/IRenderSystem interfaces for clarity. Refactor D3D12GraphicsEngine to use IFenceSynchronizer. Update RenderGraph and context to use explicit resource manager/database/allocator references. Add multi-buffering methods to IRGBuilder (stub). Support history access for multi-frame resources. Remove legacy RenderPipelineBase; introduce IRenderPipelineSettings and sealed GhostRenderPipeline. Clean up resource aliasing and pool logic. Improve modularity and future extensibility.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
[*]
|
[*]
|
||||||
max_line_length = 400
|
max_line_length = 200
|
||||||
|
|
||||||
[*.cs]
|
[*.cs]
|
||||||
csharp_new_line_before_open_brace = all
|
csharp_new_line_before_open_brace = all
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using Misaki.HighPerformance.LowLevel.Utilities;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Gdiplus;
|
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
using static TerraFX.Aliases.D3D_Alias;
|
using static TerraFX.Aliases.D3D_Alias;
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ namespace Ghost.Graphics.D3D12;
|
|||||||
|
|
||||||
public static class D3D12GraphicsEngineFactory
|
public static class D3D12GraphicsEngineFactory
|
||||||
{
|
{
|
||||||
public static IGraphicsEngine Create(IRenderSystem renderSystem)
|
public static IGraphicsEngine Create(IFenceSynchronizer fenceSynchronizer)
|
||||||
{
|
{
|
||||||
return new D3D12GraphicsEngine(renderSystem);
|
return new D3D12GraphicsEngine(fenceSynchronizer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
{
|
{
|
||||||
private GCHandle _thisHandle;
|
private GCHandle _thisHandle;
|
||||||
|
|
||||||
private readonly IRenderSystem _renderSystem;
|
private readonly IFenceSynchronizer _fenceSynchronizer;
|
||||||
|
|
||||||
#if ENABLE_DEBUG
|
#if ENABLE_DEBUG
|
||||||
private readonly D3D12DebugLayer _debugLayer;
|
private readonly D3D12DebugLayer _debugLayer;
|
||||||
@@ -45,9 +45,9 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||||
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
||||||
|
|
||||||
public D3D12GraphicsEngine(IRenderSystem renderSystem)
|
public D3D12GraphicsEngine(IFenceSynchronizer fenceSynchronizer)
|
||||||
{
|
{
|
||||||
_renderSystem = renderSystem;
|
_fenceSynchronizer = fenceSynchronizer;
|
||||||
|
|
||||||
#if ENABLE_DEBUG
|
#if ENABLE_DEBUG
|
||||||
_debugLayer = new D3D12DebugLayer();
|
_debugLayer = new D3D12DebugLayer();
|
||||||
@@ -56,7 +56,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
_shaderCompiler = new DxcShaderCompiler();
|
_shaderCompiler = new DxcShaderCompiler();
|
||||||
_descriptorAllocator = new D3D12DescriptorAllocator(_device);
|
_descriptorAllocator = new D3D12DescriptorAllocator(_device);
|
||||||
|
|
||||||
_resourceDatabase = new D3D12ResourceDatabase(renderSystem, _descriptorAllocator);
|
_resourceDatabase = new D3D12ResourceDatabase(_fenceSynchronizer, _descriptorAllocator);
|
||||||
_pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase);
|
_pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase);
|
||||||
_resourceAllocator = new D3D12ResourceAllocator(_device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
|
_resourceAllocator = new D3D12ResourceAllocator(_device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
public ISwapChain CreateSwapChain(SwapChainDesc desc)
|
public ISwapChain CreateSwapChain(SwapChainDesc desc)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _renderSystem.MaxFrameLatency);
|
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _fenceSynchronizer.MaxFrameLatency);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result RenderFrame(ICommandAllocator commandAllocator)
|
public Result RenderFrame(ICommandAllocator commandAllocator)
|
||||||
|
|||||||
@@ -28,20 +28,3 @@ public interface IFenceSynchronizer
|
|||||||
void SignalCPUReady();
|
void SignalCPUReady();
|
||||||
void WaitIdle();
|
void WaitIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IRenderSystem : IFenceSynchronizer, IDisposable
|
|
||||||
{
|
|
||||||
IGraphicsEngine GraphicsEngine
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsRunning
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Start();
|
|
||||||
void Stop();
|
|
||||||
void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize);
|
|
||||||
}
|
|
||||||
@@ -148,6 +148,11 @@ public struct RenderList : IDisposable
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
if (!IsCreated)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < _threadLocalRecords.Length; i++)
|
for (int i = 0; i < _threadLocalRecords.Length; i++)
|
||||||
{
|
{
|
||||||
_threadLocalRecords[i].Dispose();
|
_threadLocalRecords[i].Dispose();
|
||||||
|
|||||||
@@ -46,9 +46,8 @@ public struct Frustum
|
|||||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||||
public struct RenderView
|
public struct RenderView
|
||||||
{
|
{
|
||||||
public float4x4 view;
|
public float4x4 viewMatrix;
|
||||||
public float4x4 projection;
|
public float4x4 projectionMatrix;
|
||||||
public float4x4 viewProjection;
|
|
||||||
public float3 position;
|
public float3 position;
|
||||||
|
|
||||||
public Frustum frustum; // 192 bytes
|
public Frustum frustum; // 192 bytes
|
||||||
@@ -69,8 +68,13 @@ public struct RenderView
|
|||||||
public unsafe struct RenderRequest
|
public unsafe struct RenderRequest
|
||||||
{
|
{
|
||||||
public RenderView view;
|
public RenderView view;
|
||||||
|
|
||||||
public Handle<Texture> colorTarget;
|
public Handle<Texture> colorTarget;
|
||||||
public Handle<Texture> depthTarget;
|
public Handle<Texture> depthTarget;
|
||||||
|
|
||||||
public delegate*<ref readonly RenderingContext, ref readonly RenderRequest, void> renderFunc;
|
public RenderList opaqueRenderList;
|
||||||
|
public RenderList transparentRenderList;
|
||||||
|
public RenderList shadowCasterRenderList;
|
||||||
|
|
||||||
|
public delegate*<ref readonly RenderContext, ref readonly RenderRequest, void> renderFunc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ namespace Ghost.Graphics.RenderGraphModule;
|
|||||||
public sealed class RenderGraph : IDisposable
|
public sealed class RenderGraph : IDisposable
|
||||||
{
|
{
|
||||||
private readonly IResourceManager _resourceManager;
|
private readonly IResourceManager _resourceManager;
|
||||||
|
private readonly IResourceAllocator _resourceAllocator;
|
||||||
|
private readonly IResourceDatabase _resourceDatabase;
|
||||||
|
|
||||||
private readonly RenderGraphObjectPool _objectPool;
|
private readonly RenderGraphObjectPool _objectPool;
|
||||||
private readonly RenderGraphResourceRegistry _resources;
|
private readonly RenderGraphResourceRegistry _resources;
|
||||||
@@ -35,9 +37,11 @@ public sealed class RenderGraph : IDisposable
|
|||||||
|
|
||||||
public RenderGraphBlackboard Blackboard => _blackboard;
|
public RenderGraphBlackboard Blackboard => _blackboard;
|
||||||
|
|
||||||
public RenderGraph(IResourceManager resourceManager, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler)
|
public RenderGraph(IResourceManager resourceManager, IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler)
|
||||||
{
|
{
|
||||||
_resourceManager = resourceManager;
|
_resourceManager = resourceManager;
|
||||||
|
_resourceAllocator = resourceAllocator;
|
||||||
|
_resourceDatabase = resourceDatabase;
|
||||||
|
|
||||||
_objectPool = new RenderGraphObjectPool();
|
_objectPool = new RenderGraphObjectPool();
|
||||||
_resources = new RenderGraphResourceRegistry(_objectPool);
|
_resources = new RenderGraphResourceRegistry(_objectPool);
|
||||||
@@ -47,20 +51,21 @@ public sealed class RenderGraph : IDisposable
|
|||||||
_nativePasses = new List<NativeRenderPass>(32);
|
_nativePasses = new List<NativeRenderPass>(32);
|
||||||
|
|
||||||
_builder = new RenderGraphBuilder();
|
_builder = new RenderGraphBuilder();
|
||||||
_aliasingManager = new ResourceAliasingManager(resourceManager.ResourceAllocator, _objectPool);
|
_aliasingManager = new ResourceAliasingManager(_resourceAllocator, _objectPool);
|
||||||
|
|
||||||
_compilationCache = new RenderGraphCompilationCache();
|
_compilationCache = new RenderGraphCompilationCache();
|
||||||
|
|
||||||
_context = new RenderGraphContext(
|
_context = new RenderGraphContext(
|
||||||
resourceManager,
|
_resourceManager,
|
||||||
|
_resourceDatabase,
|
||||||
pipelineLibrary,
|
pipelineLibrary,
|
||||||
shaderCompiler,
|
shaderCompiler,
|
||||||
_resources
|
_resources
|
||||||
);
|
);
|
||||||
|
|
||||||
_nativePassBuilder = new RenderGraphNativePassBuilder(_objectPool, _resources);
|
_nativePassBuilder = new RenderGraphNativePassBuilder(_objectPool, _resources);
|
||||||
_compiler = new RenderGraphCompiler(resourceManager, _resources, _aliasingManager, _nativePassBuilder, _compilationCache);
|
_compiler = new RenderGraphCompiler(_resourceManager, _resources, _aliasingManager, _nativePassBuilder, _compilationCache);
|
||||||
_executor = new RenderGraphExecutor(resourceManager, _resources, _context);
|
_executor = new RenderGraphExecutor(_resourceManager, _resourceDatabase, _resources, _context);
|
||||||
|
|
||||||
_blackboard = new RenderGraphBlackboard();
|
_blackboard = new RenderGraphBlackboard();
|
||||||
}
|
}
|
||||||
@@ -103,7 +108,7 @@ public sealed class RenderGraph : IDisposable
|
|||||||
Color128 clearColor = default, float clearDepth = 1.0f, byte clearStencil = 0,
|
Color128 clearColor = default, float clearDepth = 1.0f, byte clearStencil = 0,
|
||||||
bool clearAtFirstUse = true, bool discardAtLastUse = true)
|
bool clearAtFirstUse = true, bool discardAtLastUse = true)
|
||||||
{
|
{
|
||||||
var r = _resourceManager.ResourceDatabase.GetResourceDescription(texture.AsResource());
|
var r = _resourceDatabase.GetResourceDescription(texture.AsResource());
|
||||||
if (r.IsFailure)
|
if (r.IsFailure)
|
||||||
{
|
{
|
||||||
return Identifier<RGTexture>.Invalid;
|
return Identifier<RGTexture>.Invalid;
|
||||||
@@ -120,7 +125,7 @@ public sealed class RenderGraph : IDisposable
|
|||||||
/// <returns>The identifier of the imported render graph buffer. Invalid if import fails.</returns>
|
/// <returns>The identifier of the imported render graph buffer. Invalid if import fails.</returns>
|
||||||
public Identifier<RGBuffer> ImportBuffer(Handle<GraphicsBuffer> buffer, string name)
|
public Identifier<RGBuffer> ImportBuffer(Handle<GraphicsBuffer> buffer, string name)
|
||||||
{
|
{
|
||||||
var r = _resourceManager.ResourceDatabase.GetResourceDescription(buffer.AsResource());
|
var r = _resourceDatabase.GetResourceDescription(buffer.AsResource());
|
||||||
if (r.IsFailure)
|
if (r.IsFailure)
|
||||||
{
|
{
|
||||||
return Identifier<RGBuffer>.Invalid;
|
return Identifier<RGBuffer>.Invalid;
|
||||||
@@ -195,14 +200,14 @@ public sealed class RenderGraph : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Executes all compiled passes using native render passes where possible.
|
/// Executes all compiled passes using native render passes where possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Error Execute(ICommandBuffer cmd)
|
public Error Execute(ICommandBuffer commandBuffer)
|
||||||
{
|
{
|
||||||
if (!_compiled)
|
if (!_compiled)
|
||||||
{
|
{
|
||||||
return Error.InvalidState;
|
return Error.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _executor.Execute(cmd, _compiledPasses, _nativePasses, _compiledBarriers);
|
return _executor.Execute(commandBuffer, _compiledPasses, _nativePasses, _compiledBarriers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
using Ghost.Core.Utilities;
|
using Ghost.Core.Utilities;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ghost.Graphics.RenderGraphModule;
|
namespace Ghost.Graphics.RenderGraphModule;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a memory block within a heap.
|
|
||||||
/// </summary>
|
|
||||||
internal struct MemoryBlock
|
internal struct MemoryBlock
|
||||||
{
|
{
|
||||||
public ulong offset;
|
public ulong offset;
|
||||||
@@ -35,17 +33,12 @@ internal struct MemoryBlock
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a GPU memory heap for placed resources.
|
|
||||||
/// Supports D3D12-style heap tier 2 (buffers and textures can alias).
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class ResourceHeap
|
internal sealed class ResourceHeap
|
||||||
{
|
{
|
||||||
public int index;
|
public int index;
|
||||||
public ulong size;
|
public ulong size;
|
||||||
private readonly List<MemoryBlock> _blocks = new(32);
|
private readonly List<MemoryBlock> _blocks = new(32);
|
||||||
|
|
||||||
// D3D12 heap alignment requirement (64KB for MSAA textures, 4KB for others)
|
|
||||||
public const ulong DEFAULT_ALIGNMENT = 65536; // 64KB
|
public const ulong DEFAULT_ALIGNMENT = 65536; // 64KB
|
||||||
|
|
||||||
public ResourceHeap(int index, ulong initialSize = 16 * 1024 * 1024) // 16MB default
|
public ResourceHeap(int index, ulong initialSize = 16 * 1024 * 1024) // 16MB default
|
||||||
@@ -81,6 +74,7 @@ internal sealed class ResourceHeap
|
|||||||
var smallestWaste = ulong.MaxValue;
|
var smallestWaste = ulong.MaxValue;
|
||||||
|
|
||||||
// Find the best fit block that doesn't overlap with lifetime
|
// Find the best fit block that doesn't overlap with lifetime
|
||||||
|
// TODO: Is first-fit better? Since we already sort by size beforehand.
|
||||||
var blockSpan = CollectionsMarshal.AsSpan(_blocks);
|
var blockSpan = CollectionsMarshal.AsSpan(_blocks);
|
||||||
for (var i = 0; i < blockSpan.Length; i++)
|
for (var i = 0; i < blockSpan.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -166,10 +160,6 @@ internal sealed class ResourceHeap
|
|||||||
return (true, bestFitOffset, bestFit);
|
return (true, bestFitOffset, bestFit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if a resource can be placed at the given offset without lifetime conflicts.
|
|
||||||
/// Must check ALL blocks that overlap with this offset range.
|
|
||||||
/// </summary>
|
|
||||||
private bool CanPlaceAtOffset(ulong offset, ulong size, int firstUsePass, int lastUsePass)
|
private bool CanPlaceAtOffset(ulong offset, ulong size, int firstUsePass, int lastUsePass)
|
||||||
{
|
{
|
||||||
var endOffset = offset + size;
|
var endOffset = offset + size;
|
||||||
@@ -202,12 +192,9 @@ internal sealed class ResourceHeap
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the total memory that would be used if no aliasing occurred.
|
|
||||||
/// </summary>
|
|
||||||
public ulong GetTotalAllocatedWithoutAliasing()
|
public ulong GetTotalAllocatedWithoutAliasing()
|
||||||
{
|
{
|
||||||
ulong total = 0;
|
var total = 0ul;
|
||||||
foreach (var block in _blocks)
|
foreach (var block in _blocks)
|
||||||
{
|
{
|
||||||
if (!block.isFree)
|
if (!block.isFree)
|
||||||
@@ -219,12 +206,9 @@ internal sealed class ResourceHeap
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the peak memory usage considering aliasing (max offset + size).
|
|
||||||
/// </summary>
|
|
||||||
public ulong GetPeakUsage()
|
public ulong GetPeakUsage()
|
||||||
{
|
{
|
||||||
ulong peak = 0;
|
var peak = 0ul;
|
||||||
foreach (var block in _blocks)
|
foreach (var block in _blocks)
|
||||||
{
|
{
|
||||||
if (!block.isFree)
|
if (!block.isFree)
|
||||||
@@ -242,9 +226,6 @@ internal sealed class ResourceHeap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a placed resource within a heap.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class PlacedResource
|
internal sealed class PlacedResource
|
||||||
{
|
{
|
||||||
public int index;
|
public int index;
|
||||||
@@ -279,10 +260,6 @@ internal sealed class PlacedResource
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Manages physical resource allocation and aliasing using heap-based allocation.
|
|
||||||
/// Supports D3D12 heap tier 2: buffers and textures can alias as long as lifetimes don't overlap.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class ResourceAliasingManager
|
internal sealed class ResourceAliasingManager
|
||||||
{
|
{
|
||||||
private readonly IResourceAllocator _allocator;
|
private readonly IResourceAllocator _allocator;
|
||||||
@@ -293,15 +270,11 @@ internal sealed class ResourceAliasingManager
|
|||||||
// Mapping from logical resource index to placed resource index
|
// Mapping from logical resource index to placed resource index
|
||||||
private readonly Dictionary<int, int> _logicalToPlaced;
|
private readonly Dictionary<int, int> _logicalToPlaced;
|
||||||
|
|
||||||
// D3D12 alignment constants
|
|
||||||
private const ulong _DEFAULT_TEXTURE_ALIGNMENT = 65536; // 64KB
|
private const ulong _DEFAULT_TEXTURE_ALIGNMENT = 65536; // 64KB
|
||||||
private const ulong _DEFAULT_BUFFER_ALIGNMENT = 65536; // 64KB for D3D12
|
private const ulong _DEFAULT_BUFFER_ALIGNMENT = 65536; // 64KB
|
||||||
|
|
||||||
public ResourceHeap Heap => _heap;
|
public ResourceHeap Heap => _heap;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper method to get the size of a resource
|
|
||||||
/// </summary>
|
|
||||||
private ulong GetResourceSize(RenderGraphResource resource)
|
private ulong GetResourceSize(RenderGraphResource resource)
|
||||||
{
|
{
|
||||||
if (resource.type == RenderGraphResourceType.Texture)
|
if (resource.type == RenderGraphResourceType.Texture)
|
||||||
@@ -311,7 +284,6 @@ internal sealed class ResourceAliasingManager
|
|||||||
}
|
}
|
||||||
else // Buffer
|
else // Buffer
|
||||||
{
|
{
|
||||||
//return resource.bufferDesc.Size;
|
|
||||||
return _allocator.GetSizeInfo(ResourceDesc.Buffer(resource.bufferDesc)).Size;
|
return _allocator.GetSizeInfo(ResourceDesc.Buffer(resource.bufferDesc)).Size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -339,13 +311,6 @@ internal sealed class ResourceAliasingManager
|
|||||||
_heap.Reset();
|
_heap.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Assigns physical resources (placed resources) to logical resources using heap-based allocation.
|
|
||||||
/// This is the modern D3D12 approach: check if resource fits in a hole, not if it matches size/format.
|
|
||||||
/// Uses a two-pass algorithm:
|
|
||||||
/// 1. First pass: Simulate allocation to determine peak memory usage
|
|
||||||
/// 2. Second pass: Create a single heap of the peak size and do the real allocation
|
|
||||||
/// </summary>
|
|
||||||
public void AssignPhysicalResources(RenderGraphResourceRegistry registry, int passCount)
|
public void AssignPhysicalResources(RenderGraphResourceRegistry registry, int passCount)
|
||||||
{
|
{
|
||||||
// Build list of all logical resources (both textures and buffers) with their lifetimes
|
// Build list of all logical resources (both textures and buffers) with their lifetimes
|
||||||
@@ -369,7 +334,16 @@ internal sealed class ResourceAliasingManager
|
|||||||
return sizeB.CompareTo(sizeA); // Descending
|
return sizeB.CompareTo(sizeA); // Descending
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// NOTE: We assume we are at least D3D12 heap tier 2 (the engine won't even start if the hardware does not supports)
|
||||||
|
// so buffers and textures can alias as long as lifetimes don't overlap.
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Handle non-aliased resources like history buffers that need to persist across frames.
|
||||||
|
// They will be placed in the same heap but we can mark them as non-aliased and skip them when looking for aliasing candidates.
|
||||||
|
// We do not place them in a separate heap because our buffers are cached and reused across frames as long as the graph topology doesn't change.
|
||||||
|
|
||||||
// ===== PASS 1: Simulate allocation to determine peak memory usage =====
|
// ===== PASS 1: Simulate allocation to determine peak memory usage =====
|
||||||
|
|
||||||
var simulationHeap = new ResourceHeap(0, ulong.MaxValue); // Unlimited size for simulation
|
var simulationHeap = new ResourceHeap(0, ulong.MaxValue); // Unlimited size for simulation
|
||||||
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
||||||
{
|
{
|
||||||
@@ -385,10 +359,7 @@ internal sealed class ResourceAliasingManager
|
|||||||
logicalIndex,
|
logicalIndex,
|
||||||
alignment);
|
alignment);
|
||||||
|
|
||||||
if (!success)
|
Debug.Assert(success, "Simulation allocation failed - heap should be unlimited in size");
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Simulation allocation failed - this should never happen with unlimited heap");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get peak usage from simulation
|
// Get peak usage from simulation
|
||||||
@@ -398,12 +369,15 @@ internal sealed class ResourceAliasingManager
|
|||||||
peakMemoryUsage = AlignUp(peakMemoryUsage, _DEFAULT_TEXTURE_ALIGNMENT);
|
peakMemoryUsage = AlignUp(peakMemoryUsage, _DEFAULT_TEXTURE_ALIGNMENT);
|
||||||
|
|
||||||
// ===== PASS 2: Create a single heap of the peak size and do the real allocation =====
|
// ===== PASS 2: Create a single heap of the peak size and do the real allocation =====
|
||||||
_heap.size = peakMemoryUsage;
|
|
||||||
_heap.Reset();
|
_heap.Reset();
|
||||||
|
_heap.size = peakMemoryUsage;
|
||||||
|
|
||||||
// Allocate each logical resource in the heap
|
// Allocate each logical resource in the heap
|
||||||
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
||||||
{
|
{
|
||||||
|
// TODO: Currently we are recalculating the aliasing candidates in the real allocation pass.
|
||||||
|
// We can optimize this by caching the candidates from the simulation pass since the heap layout should be the same.
|
||||||
var size = GetResourceSize(logicalResource);
|
var size = GetResourceSize(logicalResource);
|
||||||
var alignment = logicalResource.type == RenderGraphResourceType.Texture
|
var alignment = logicalResource.type == RenderGraphResourceType.Texture
|
||||||
? _DEFAULT_TEXTURE_ALIGNMENT
|
? _DEFAULT_TEXTURE_ALIGNMENT
|
||||||
@@ -488,9 +462,6 @@ internal sealed class ResourceAliasingManager
|
|||||||
return (value + alignment - 1) & ~(alignment - 1);
|
return (value + alignment - 1) & ~(alignment - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Restores aliasing state from cache.
|
|
||||||
/// </summary>
|
|
||||||
public void RestoreFromCache(Dictionary<int, int> logicalToPlaced, List<PlacedResourceData> placedData)
|
public void RestoreFromCache(Dictionary<int, int> logicalToPlaced, List<PlacedResourceData> placedData)
|
||||||
{
|
{
|
||||||
_logicalToPlaced.Clear();
|
_logicalToPlaced.Clear();
|
||||||
@@ -518,9 +489,6 @@ internal sealed class ResourceAliasingManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stores current aliasing state to cache.
|
|
||||||
/// </summary>
|
|
||||||
public void StoreToCache(Dictionary<int, int> outLogicalToPlaced, List<PlacedResourceData> outPlacedData)
|
public void StoreToCache(Dictionary<int, int> outLogicalToPlaced, List<PlacedResourceData> outPlacedData)
|
||||||
{
|
{
|
||||||
outLogicalToPlaced.Clear();
|
outLogicalToPlaced.Clear();
|
||||||
|
|||||||
@@ -32,6 +32,17 @@ public interface IRenderGraphBuilder : IDisposable
|
|||||||
/// <returns>An identifier for the newly created texture resource.</returns>
|
/// <returns>An identifier for the newly created texture resource.</returns>
|
||||||
Identifier<RGTexture> CreateTexture(in RGTextureDesc desc, string name);
|
Identifier<RGTexture> CreateTexture(in RGTextureDesc desc, string name);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates multiple texture resources based on the specified desc.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Those textures will be used as multi buffering in the render graph automaticlly and not aliasable with other resources.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="desc">A structure that defines the properties and configuration of the texture to create.</param>
|
||||||
|
/// <param name="name">The base name of the texture resources. The actual name for each texture will be generated by appending an index to this base name.</param>
|
||||||
|
/// <param name="textureIDs">A span to receive the identifiers for the newly created texture resources. The length of the span determines how many textures will be created.</param>
|
||||||
|
void CreateTextures(in RGTextureDesc desc, string name, Span<Identifier<RGTexture>> textureIDs);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new buffer resource based on the specified desc.
|
/// Creates a new buffer resource based on the specified desc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -40,6 +51,17 @@ public interface IRenderGraphBuilder : IDisposable
|
|||||||
/// <returns>An identifier for the newly created buffer resource.</returns>
|
/// <returns>An identifier for the newly created buffer resource.</returns>
|
||||||
Identifier<RGBuffer> CreateBuffer(in BufferDesc desc, string name);
|
Identifier<RGBuffer> CreateBuffer(in BufferDesc desc, string name);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates multiple buffer resources based on the specified desc.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Those buffers will be used as multi buffering in the render graph automaticlly and not aliasable with other resources.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="desc">A structure that defines the properties and configuration of the buffer to create.</param>
|
||||||
|
/// <param name="name">The base name of the buffer resources. The actual name for each buffer will be generated by appending an index to this base name.</param>
|
||||||
|
/// <param name="bufferIDs">A span to receive the identifiers for the newly created buffer resources. The length of the span determines how many buffers will be created.</param>
|
||||||
|
void CreateBuffers(in BufferDesc desc, string name, Span<Identifier<RGBuffer>> bufferIDs);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers the specified texture for use in the current render graph pass with the given access mode.
|
/// Registers the specified texture for use in the current render graph pass with the given access mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -140,7 +162,6 @@ public interface IUnsafeRenderGraphBuilder : IRenderGraphBuilder
|
|||||||
|
|
||||||
internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGraphBuilder, IUnsafeRenderGraphBuilder
|
internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGraphBuilder, IUnsafeRenderGraphBuilder
|
||||||
{
|
{
|
||||||
|
|
||||||
private RenderGraph _graph = null!;
|
private RenderGraph _graph = null!;
|
||||||
private RenderGraphPassBase _pass = null!;
|
private RenderGraphPassBase _pass = null!;
|
||||||
private RenderGraphResourceRegistry _resources = null!;
|
private RenderGraphResourceRegistry _resources = null!;
|
||||||
@@ -206,6 +227,18 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
|
|||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CreateTextures(in RGTextureDesc desc, string name, Span<Identifier<RGTexture>> textureIDs)
|
||||||
|
{
|
||||||
|
// TODO: Create multiple textures, mark them as no aliasable, and add them to the resource registry and current pass.
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateBuffers(in BufferDesc desc, string name, Span<Identifier<RGBuffer>> bufferIDs)
|
||||||
|
{
|
||||||
|
// TODO: Create multiple buffers, mark them as no aliasable, and add them to the resource registry and current pass.
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public Identifier<RGTexture> UseTexture(Identifier<RGTexture> texture, AccessFlags flags)
|
public Identifier<RGTexture> UseTexture(Identifier<RGTexture> texture, AccessFlags flags)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ public interface IRenderGraphContext
|
|||||||
Handle<GPUResource> GetActualResource(Identifier<RGResource> resource);
|
Handle<GPUResource> GetActualResource(Identifier<RGResource> resource);
|
||||||
Handle<Texture> GetActualTexture(Identifier<RGTexture> texture);
|
Handle<Texture> GetActualTexture(Identifier<RGTexture> texture);
|
||||||
Handle<GraphicsBuffer> GetActualBuffer(Identifier<RGBuffer> buffer);
|
Handle<GraphicsBuffer> GetActualBuffer(Identifier<RGBuffer> buffer);
|
||||||
|
|
||||||
|
Handle<Texture> GetHistoryTexture(ReadOnlySpan<Identifier<RGTexture>> texture, int historyOffset);
|
||||||
|
Handle<GraphicsBuffer> GetHistoryBuffer(ReadOnlySpan<Identifier<RGBuffer>> buffer, int historyOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IRasterRenderContext : IRenderGraphContext
|
public interface IRasterRenderContext : IRenderGraphContext
|
||||||
@@ -38,11 +41,13 @@ public interface IUnsafeRenderContext : IRasterRenderContext, IRenderGraphContex
|
|||||||
internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderContext, IUnsafeRenderContext
|
internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderContext, IUnsafeRenderContext
|
||||||
{
|
{
|
||||||
private readonly IResourceManager _resourceManager;
|
private readonly IResourceManager _resourceManager;
|
||||||
|
private readonly IResourceDatabase _resourceDatabase;
|
||||||
private readonly IPipelineLibrary _pipelineLibrary;
|
private readonly IPipelineLibrary _pipelineLibrary;
|
||||||
private readonly IShaderCompiler _shaderCompiler;
|
private readonly IShaderCompiler _shaderCompiler;
|
||||||
private readonly RenderGraphResourceRegistry _resources;
|
private readonly RenderGraphResourceRegistry _resources;
|
||||||
|
|
||||||
private ICommandBuffer _commandBuffer = null!;
|
private uint _frameIndex;
|
||||||
|
private ICommandBuffer _commandBuffer;
|
||||||
|
|
||||||
private readonly TextureFormat[] _rtvFormats;
|
private readonly TextureFormat[] _rtvFormats;
|
||||||
private TextureFormat _dsvFormat;
|
private TextureFormat _dsvFormat;
|
||||||
@@ -58,19 +63,23 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
|||||||
|
|
||||||
public ICommandBuffer CommandBuffer => _commandBuffer;
|
public ICommandBuffer CommandBuffer => _commandBuffer;
|
||||||
|
|
||||||
internal RenderGraphContext(IResourceManager resourceManager, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler, RenderGraphResourceRegistry resources)
|
internal RenderGraphContext(IResourceManager resourceManager, IResourceDatabase resourceDatabase, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler, RenderGraphResourceRegistry resources)
|
||||||
{
|
{
|
||||||
_resourceManager = resourceManager;
|
_resourceManager = resourceManager;
|
||||||
|
_resourceDatabase = resourceDatabase;
|
||||||
_pipelineLibrary = pipelineLibrary;
|
_pipelineLibrary = pipelineLibrary;
|
||||||
_shaderCompiler = shaderCompiler;
|
_shaderCompiler = shaderCompiler;
|
||||||
_resources = resources;
|
_resources = resources;
|
||||||
|
|
||||||
|
_commandBuffer = null!;
|
||||||
|
|
||||||
_rtvFormats = new TextureFormat[RHIUtility.MAX_RENDER_TARGETS];
|
_rtvFormats = new TextureFormat[RHIUtility.MAX_RENDER_TARGETS];
|
||||||
_dsvFormat = TextureFormat.Unknown;
|
_dsvFormat = TextureFormat.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetCommandBuffer(ICommandBuffer commandBuffer)
|
internal void BeginNewFrame(uint frameIndex, ICommandBuffer commandBuffer)
|
||||||
{
|
{
|
||||||
|
_frameIndex = frameIndex;
|
||||||
_commandBuffer = commandBuffer;
|
_commandBuffer = commandBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +109,38 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
|||||||
return _resources.GetResource(buffer.AsResource()).backingResource.AsGraphicsBuffer();
|
return _resources.GetResource(buffer.AsResource()).backingResource.AsGraphicsBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Handle<Texture> GetHistoryTexture(ReadOnlySpan<Identifier<RGTexture>> textures, int historyOffset)
|
||||||
|
{
|
||||||
|
if (historyOffset < 0 || historyOffset >= textures.Length)
|
||||||
|
{
|
||||||
|
return Handle<Texture>.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = (int)(_frameIndex % textures.Length) - historyOffset;
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
index += textures.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetActualTexture(textures[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Handle<GraphicsBuffer> GetHistoryBuffer(ReadOnlySpan<Identifier<RGBuffer>> buffers, int historyOffset)
|
||||||
|
{
|
||||||
|
if (historyOffset < 0 || historyOffset >= buffers.Length)
|
||||||
|
{
|
||||||
|
return Handle<GraphicsBuffer>.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = (int)(_frameIndex % buffers.Length) - historyOffset;
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
index += buffers.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetActualBuffer(buffers[index]);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetActiveMaterial(Handle<Material> material)
|
public void SetActiveMaterial(Handle<Material> material)
|
||||||
{
|
{
|
||||||
var r = _resourceManager.GetMaterialReference(material);
|
var r = _resourceManager.GetMaterialReference(material);
|
||||||
@@ -183,8 +224,8 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
|||||||
// TODO: Global and view constants
|
// TODO: Global and view constants
|
||||||
var data = new PushConstantsData
|
var data = new PushConstantsData
|
||||||
{
|
{
|
||||||
objectIndex = _resourceManager.ResourceDatabase.GetBindlessIndex(_activePerMeshData.AsResource()),
|
objectIndex = _resourceDatabase.GetBindlessIndex(_activePerMeshData.AsResource()),
|
||||||
materialIndex = _resourceManager.ResourceDatabase.GetBindlessIndex(_activePerMaterialData.AsResource()),
|
materialIndex = _resourceDatabase.GetBindlessIndex(_activePerMaterialData.AsResource()),
|
||||||
};
|
};
|
||||||
|
|
||||||
var pushConstantSpan = new ReadOnlySpan<uint>(&data, sizeof(PushConstantsData) / sizeof(uint));
|
var pushConstantSpan = new ReadOnlySpan<uint>(&data, sizeof(PushConstantsData) / sizeof(uint));
|
||||||
@@ -196,4 +237,4 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
|||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,30 +3,29 @@ using Ghost.Graphics.RHI;
|
|||||||
|
|
||||||
namespace Ghost.Graphics.RenderGraphModule;
|
namespace Ghost.Graphics.RenderGraphModule;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles execution of compiled render graphs, including barrier execution and native render passes.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class RenderGraphExecutor
|
internal sealed class RenderGraphExecutor
|
||||||
{
|
{
|
||||||
private readonly IResourceManager _resourceManager;
|
private readonly IResourceManager _resourceManager;
|
||||||
|
private readonly IResourceDatabase _resourceDatabase;
|
||||||
private readonly RenderGraphResourceRegistry _resources;
|
private readonly RenderGraphResourceRegistry _resources;
|
||||||
private readonly RenderGraphContext _context;
|
private readonly RenderGraphContext _context;
|
||||||
|
|
||||||
|
private uint _frameIndex;
|
||||||
|
|
||||||
public RenderGraphExecutor(
|
public RenderGraphExecutor(
|
||||||
IResourceManager resourceManager,
|
IResourceManager resourceManager,
|
||||||
|
IResourceDatabase resourceDatabase,
|
||||||
RenderGraphResourceRegistry resources,
|
RenderGraphResourceRegistry resources,
|
||||||
RenderGraphContext context)
|
RenderGraphContext context)
|
||||||
{
|
{
|
||||||
_resourceManager = resourceManager;
|
_resourceManager = resourceManager;
|
||||||
|
_resourceDatabase = resourceDatabase;
|
||||||
_resources = resources;
|
_resources = resources;
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Executes all compiled passes using native render passes where possible.
|
|
||||||
/// </summary>
|
|
||||||
public unsafe Error Execute(
|
public unsafe Error Execute(
|
||||||
ICommandBuffer cmd,
|
ICommandBuffer commandBuffer,
|
||||||
List<RenderGraphPassBase> compiledPasses,
|
List<RenderGraphPassBase> compiledPasses,
|
||||||
List<NativeRenderPass> nativePasses,
|
List<NativeRenderPass> nativePasses,
|
||||||
List<CompiledBarrier> compiledBarriers)
|
List<CompiledBarrier> compiledBarriers)
|
||||||
@@ -35,7 +34,7 @@ internal sealed class RenderGraphExecutor
|
|||||||
var nativePassIndex = 0;
|
var nativePassIndex = 0;
|
||||||
var logicalPassIndex = 0;
|
var logicalPassIndex = 0;
|
||||||
|
|
||||||
_context.SetCommandBuffer(cmd);
|
_context.BeginNewFrame(_frameIndex++, commandBuffer);
|
||||||
|
|
||||||
var pPassRTDescs = stackalloc PassRenderTargetDesc[8];
|
var pPassRTDescs = stackalloc PassRenderTargetDesc[8];
|
||||||
var pRtFormats = stackalloc TextureFormat[8];
|
var pRtFormats = stackalloc TextureFormat[8];
|
||||||
@@ -53,7 +52,7 @@ internal sealed class RenderGraphExecutor
|
|||||||
for (var i = 0; i < nativePass.mergedPassIndices.Count; i++)
|
for (var i = 0; i < nativePass.mergedPassIndices.Count; i++)
|
||||||
{
|
{
|
||||||
var mergedPassIdx = nativePass.mergedPassIndices[i];
|
var mergedPassIdx = nativePass.mergedPassIndices[i];
|
||||||
var e = ExecuteBarriersForPass(cmd, mergedPassIdx, ref barrierIndex, compiledBarriers);
|
var e = ExecuteBarriersForPass(commandBuffer, mergedPassIdx, ref barrierIndex, compiledBarriers);
|
||||||
if (e != Error.None)
|
if (e != Error.None)
|
||||||
{
|
{
|
||||||
return e;
|
return e;
|
||||||
@@ -94,7 +93,7 @@ internal sealed class RenderGraphExecutor
|
|||||||
: AttachmentStoreOp.DontCare
|
: AttachmentStoreOp.DontCare
|
||||||
};
|
};
|
||||||
|
|
||||||
cmd.BeginRenderPass(new Span<PassRenderTargetDesc>(pPassRTDescs, nativePass.colorAttachmentCount), depthDesc);
|
commandBuffer.BeginRenderPass(new Span<PassRenderTargetDesc>(pPassRTDescs, nativePass.colorAttachmentCount), depthDesc);
|
||||||
|
|
||||||
for (var i = 0; i < nativePass.colorAttachmentCount; i++)
|
for (var i = 0; i < nativePass.colorAttachmentCount; i++)
|
||||||
{
|
{
|
||||||
@@ -117,13 +116,13 @@ internal sealed class RenderGraphExecutor
|
|||||||
logicalPassIndex++;
|
logicalPassIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.EndRenderPass();
|
commandBuffer.EndRenderPass();
|
||||||
nativePassIndex++;
|
nativePassIndex++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Compute pass or standalone raster pass (not merged) or Unsafe pass
|
// Compute pass or standalone raster pass (not merged) or Unsafe pass
|
||||||
var e = ExecuteBarriersForPass(cmd, logicalPassIndex, ref barrierIndex, compiledBarriers);
|
var e = ExecuteBarriersForPass(commandBuffer, logicalPassIndex, ref barrierIndex, compiledBarriers);
|
||||||
if (e != Error.None)
|
if (e != Error.None)
|
||||||
{
|
{
|
||||||
return e;
|
return e;
|
||||||
@@ -137,10 +136,6 @@ internal sealed class RenderGraphExecutor
|
|||||||
return Error.None;
|
return Error.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Executes all barriers for a specific pass.
|
|
||||||
/// Uses pre-compiled barriers and queries before state from ResourceManager.
|
|
||||||
/// </summary>
|
|
||||||
private unsafe Error ExecuteBarriersForPass(
|
private unsafe Error ExecuteBarriersForPass(
|
||||||
ICommandBuffer cmd,
|
ICommandBuffer cmd,
|
||||||
int passIndex,
|
int passIndex,
|
||||||
@@ -168,7 +163,7 @@ internal sealed class RenderGraphExecutor
|
|||||||
var resourceHandle = resource.backingResource;
|
var resourceHandle = resource.backingResource;
|
||||||
|
|
||||||
// Always query the before state from ResourceManager (single source of truth)
|
// Always query the before state from ResourceManager (single source of truth)
|
||||||
var currentStateResult = _resourceManager.ResourceDatabase.GetResourceBarrierData(resourceHandle);
|
var currentStateResult = _resourceDatabase.GetResourceBarrierData(resourceHandle);
|
||||||
if (currentStateResult.IsFailure)
|
if (currentStateResult.IsFailure)
|
||||||
{
|
{
|
||||||
return currentStateResult.Error;
|
return currentStateResult.Error;
|
||||||
@@ -184,7 +179,7 @@ internal sealed class RenderGraphExecutor
|
|||||||
if (compiledBarrier.AliasingPredecessor.IsValid)
|
if (compiledBarrier.AliasingPredecessor.IsValid)
|
||||||
{
|
{
|
||||||
var predHandle = _resources.GetResource(compiledBarrier.AliasingPredecessor).backingResource;
|
var predHandle = _resources.GetResource(compiledBarrier.AliasingPredecessor).backingResource;
|
||||||
var predStateResult = _resourceManager.ResourceDatabase.GetResourceBarrierData(predHandle);
|
var predStateResult = _resourceDatabase.GetResourceBarrierData(predHandle);
|
||||||
if (predStateResult.IsFailure)
|
if (predStateResult.IsFailure)
|
||||||
{
|
{
|
||||||
return predStateResult.Error;
|
return predStateResult.Error;
|
||||||
|
|||||||
@@ -4,10 +4,6 @@ using Misaki.HighPerformance.Buffer;
|
|||||||
|
|
||||||
namespace Ghost.Graphics.RenderGraphModule;
|
namespace Ghost.Graphics.RenderGraphModule;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Object pool for reusing allocated objects across frames.
|
|
||||||
/// This is key to minimizing GC allocations after the first frame.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class RenderGraphObjectPool
|
internal sealed class RenderGraphObjectPool
|
||||||
{
|
{
|
||||||
private static readonly List<SharedObjectPoolBase> s_allocatedPools = new();
|
private static readonly List<SharedObjectPoolBase> s_allocatedPools = new();
|
||||||
@@ -18,7 +14,8 @@ internal sealed class RenderGraphObjectPool
|
|||||||
public virtual void Clear() { }
|
public virtual void Clear() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SharedObjectPool<T> : SharedObjectPoolBase where T : class, new()
|
private class SharedObjectPool<T> : SharedObjectPoolBase
|
||||||
|
where T : class, new()
|
||||||
{
|
{
|
||||||
private static readonly ObjectPool<T> s_pool = AllocatePool();
|
private static readonly ObjectPool<T> s_pool = AllocatePool();
|
||||||
|
|
||||||
@@ -30,28 +27,20 @@ internal sealed class RenderGraphObjectPool
|
|||||||
return newPool;
|
return newPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clear the pool using SharedObjectPool instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override void Clear()
|
public override void Clear()
|
||||||
{
|
{
|
||||||
s_pool.Reset();
|
s_pool.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static T Rent()
|
||||||
/// Rent a new instance from the pool.
|
{
|
||||||
/// </summary>
|
return s_pool.Rent();
|
||||||
/// <returns></returns>
|
}
|
||||||
// FIX: ObjectPool<T>.Rent() has a critical bug that it will put the newly created object into the pool directly and give out the same instance again.
|
|
||||||
// This will cause multiple renters to get the same instance.
|
|
||||||
public static T Rent() => s_pool.Rent();
|
|
||||||
|
|
||||||
/// <summary>
|
public static void Return(T toRelease)
|
||||||
/// Return an object to the pool.
|
{
|
||||||
/// </summary>
|
s_pool.Return(toRelease);
|
||||||
/// <param name="toRelease">instance to release.</param>
|
}
|
||||||
public static void Return(T toRelease) => s_pool.Return(toRelease);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Rent<T>()
|
public T Rent<T>()
|
||||||
@@ -75,9 +64,6 @@ internal sealed class RenderGraphObjectPool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a resource in the render graph (texture or buffer).
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class RenderGraphResource
|
internal sealed class RenderGraphResource
|
||||||
{
|
{
|
||||||
public string name = string.Empty;
|
public string name = string.Empty;
|
||||||
@@ -121,11 +107,6 @@ internal sealed class RenderGraphResource
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registry for managing all resources in the render graph.
|
|
||||||
/// Uses pooling to minimize allocations after the first frame.
|
|
||||||
/// Uses a single unified list for both textures and buffers with global indexing.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class RenderGraphResourceRegistry
|
internal sealed class RenderGraphResourceRegistry
|
||||||
{
|
{
|
||||||
private readonly RenderGraphObjectPool _pool;
|
private readonly RenderGraphObjectPool _pool;
|
||||||
|
|||||||
@@ -1,11 +1,65 @@
|
|||||||
using Ghost.Graphics.Core;
|
using Ghost.Graphics.Core;
|
||||||
|
using Ghost.Graphics.RenderGraphModule;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ghost.Graphics.RenderPipeline;
|
namespace Ghost.Graphics.RenderPipeline;
|
||||||
|
|
||||||
public partial class GhostRenderPipeline : RenderPipelineBase
|
public sealed class GhostRenderPipelineSettings : IRenderPipelineSettings
|
||||||
{
|
{
|
||||||
public override void Render(RenderContext ctx, ReadOnlySpan<RenderRequest> requests)
|
public static IRenderPipeline CreatePipeline(IRenderSystem renderSystem)
|
||||||
{
|
{
|
||||||
|
return new GhostRenderPipeline(renderSystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe partial class GhostRenderPipeline : IRenderPipeline
|
||||||
|
{
|
||||||
|
private readonly RenderGraph _renderGraph;
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
~GhostRenderPipeline()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected void ThrowIfDisposed()
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal GhostRenderPipeline(IRenderSystem renderSystem)
|
||||||
|
{
|
||||||
|
_renderGraph = new RenderGraph(renderSystem.ResourceManager,
|
||||||
|
renderSystem.GraphicsEngine.ResourceAllocator,
|
||||||
|
renderSystem.GraphicsEngine.ResourceDatabase,
|
||||||
|
renderSystem.GraphicsEngine.PipelineLibrary,
|
||||||
|
renderSystem.GraphicsEngine.ShaderCompiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render(RenderContext ctx, ReadOnlySpan<RenderRequest> requests)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < requests.Length; i++)
|
||||||
|
{
|
||||||
|
ref readonly var request = ref requests[i];
|
||||||
|
|
||||||
|
if (request.renderFunc != null)
|
||||||
|
{
|
||||||
|
request.renderFunc(in ctx, in request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/Runtime/Ghost.Graphics/RenderPipeline/IRenderPipeline.cs
Normal file
14
src/Runtime/Ghost.Graphics/RenderPipeline/IRenderPipeline.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Ghost.Graphics.Core;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.RenderPipeline;
|
||||||
|
|
||||||
|
public interface IRenderPipelineSettings
|
||||||
|
{
|
||||||
|
static abstract IRenderPipeline CreatePipeline(IRenderSystem renderSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IRenderPipeline : IDisposable
|
||||||
|
{
|
||||||
|
void Render(RenderContext ctx, ReadOnlySpan<RenderRequest> requests);
|
||||||
|
}
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using Ghost.Graphics.Core;
|
|
||||||
using Ghost.Graphics.RHI;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.RenderPipeline;
|
|
||||||
|
|
||||||
public interface IRenderPipeline : IDisposable
|
|
||||||
{
|
|
||||||
void AddRenderList(RenderList renderList, string key);
|
|
||||||
|
|
||||||
void Render(RenderContext ctx, ReadOnlySpan<RenderRequest> requests);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class RenderPipelineBase : IRenderPipeline
|
|
||||||
{
|
|
||||||
protected readonly Dictionary<string, RenderList> _renderLists = new();
|
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
~RenderPipelineBase()
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
protected void ThrowIfDisposed()
|
|
||||||
{
|
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddRenderList(RenderList renderList, string key)
|
|
||||||
{
|
|
||||||
ThrowIfDisposed();
|
|
||||||
|
|
||||||
ref var existingList = ref CollectionsMarshal.GetValueRefOrAddDefault(_renderLists, key, out var exists);
|
|
||||||
if (!exists)
|
|
||||||
{
|
|
||||||
existingList = renderList;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
existingList.Append(renderList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void Render(RenderContext ctx, ReadOnlySpan<RenderRequest> requests);
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispose(true);
|
|
||||||
|
|
||||||
foreach (var list in _renderLists.Values)
|
|
||||||
{
|
|
||||||
list.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,28 @@ using System.Collections.Concurrent;
|
|||||||
|
|
||||||
namespace Ghost.Graphics;
|
namespace Ghost.Graphics;
|
||||||
|
|
||||||
|
public interface IRenderSystem : IFenceSynchronizer, IDisposable
|
||||||
|
{
|
||||||
|
IGraphicsEngine GraphicsEngine
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
IResourceManager ResourceManager
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRunning
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start();
|
||||||
|
void Stop();
|
||||||
|
void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize);
|
||||||
|
}
|
||||||
|
|
||||||
public enum GraphicsAPI
|
public enum GraphicsAPI
|
||||||
{
|
{
|
||||||
Direct3D12
|
Direct3D12
|
||||||
@@ -63,6 +85,7 @@ internal class RenderSystem : IRenderSystem
|
|||||||
|
|
||||||
private readonly RenderingConfig _config;
|
private readonly RenderingConfig _config;
|
||||||
private readonly IGraphicsEngine _graphicsEngine;
|
private readonly IGraphicsEngine _graphicsEngine;
|
||||||
|
private readonly IResourceManager _resourceManager;
|
||||||
|
|
||||||
private readonly FrameResource[] _frameResources;
|
private readonly FrameResource[] _frameResources;
|
||||||
private readonly Thread _renderThread;
|
private readonly Thread _renderThread;
|
||||||
@@ -77,6 +100,7 @@ internal class RenderSystem : IRenderSystem
|
|||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public IGraphicsEngine GraphicsEngine => _graphicsEngine;
|
public IGraphicsEngine GraphicsEngine => _graphicsEngine;
|
||||||
|
public IResourceManager ResourceManager => _resourceManager;
|
||||||
public bool IsRunning => _isRunning;
|
public bool IsRunning => _isRunning;
|
||||||
|
|
||||||
public uint CPUFenceValue => _cpuFenceValue;
|
public uint CPUFenceValue => _cpuFenceValue;
|
||||||
@@ -107,6 +131,8 @@ internal class RenderSystem : IRenderSystem
|
|||||||
throw new NotSupportedException($"The specified graphics API '{config.GraphicsAPI}' is not supported.");
|
throw new NotSupportedException($"The specified graphics API '{config.GraphicsAPI}' is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_resourceManager = new ResourceManager(_graphicsEngine.ResourceAllocator, _graphicsEngine.ResourceDatabase);
|
||||||
|
|
||||||
// Create frame resources for synchronization
|
// Create frame resources for synchronization
|
||||||
_frameResources = new FrameResource[config.FrameBufferCount];
|
_frameResources = new FrameResource[config.FrameBufferCount];
|
||||||
for (var i = 0; i < config.FrameBufferCount; i++)
|
for (var i = 0; i < config.FrameBufferCount; i++)
|
||||||
|
|||||||
@@ -9,16 +9,6 @@ namespace Ghost.Graphics;
|
|||||||
|
|
||||||
public interface IResourceManager
|
public interface IResourceManager
|
||||||
{
|
{
|
||||||
IResourceAllocator ResourceAllocator
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
IResourceDatabase ResourceDatabase
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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>
|
||||||
@@ -113,16 +103,13 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
|||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
|
||||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
|
||||||
|
|
||||||
public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
|
public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
|
||||||
{
|
{
|
||||||
_resourceAllocator = resourceAllocator;
|
_resourceAllocator = resourceAllocator;
|
||||||
_resourceDatabase = resourceDatabase;
|
_resourceDatabase = resourceDatabase;
|
||||||
|
|
||||||
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent);
|
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent);
|
||||||
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent);
|
_materials = new UnsafeSlotMap<Material>(64, Allocator.Persistent);
|
||||||
_shaders = new UnsafeList<Shader>(16, Allocator.Persistent);
|
_shaders = new UnsafeList<Shader>(16, Allocator.Persistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user