using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI;
namespace Ghost.Graphics.RenderGraphModule;
///
/// Main render graph class that manages resource allocation and pass execution.
/// Delegates complex operations to specialized components for better organization.
///
public sealed class RenderGraph : IDisposable
{
private readonly IGraphicsEngine _graphicsEngine;
private readonly RenderGraphObjectPool _objectPool;
private readonly RenderGraphResourceRegistry _resources;
private readonly List _passes;
private readonly List _compiledPasses;
private readonly List _nativePasses;
private readonly RenderGraphBuilder _builder;
private readonly ResourceAliasingManager _aliasingManager;
private readonly List _compiledBarriers = new(128);
private readonly RenderGraphCompilationCache _compilationCache = new();
private readonly RenderGraphContext _context;
private readonly RenderGraphCompiler _compiler;
private readonly RenderGraphExecutor _executor;
private readonly RenderGraphNativePassBuilder _nativePassBuilder;
private bool _compiled;
public RenderGraphBlackboard Blackboard
{
get;
}
public RenderGraph(IGraphicsEngine graphicsEngine)
{
_graphicsEngine = graphicsEngine;
_objectPool = new RenderGraphObjectPool();
_resources = new RenderGraphResourceRegistry(_objectPool);
_passes = new List(32);
_compiledPasses = new List(32);
_nativePasses = new List(32);
_builder = new RenderGraphBuilder();
_aliasingManager = new ResourceAliasingManager(graphicsEngine.ResourceAllocator, _objectPool);
_compilationCache = new RenderGraphCompilationCache();
_context = new RenderGraphContext(
_graphicsEngine.ResourceDatabase,
_graphicsEngine.PipelineLibrary,
_graphicsEngine.ShaderCompiler,
_resources
);
_nativePassBuilder = new RenderGraphNativePassBuilder(_objectPool, _resources);
_compiler = new RenderGraphCompiler(_graphicsEngine, _resources, _aliasingManager, _nativePassBuilder, _compilationCache);
_executor = new RenderGraphExecutor(_graphicsEngine, _resources, _context);
Blackboard = new RenderGraphBlackboard();
}
///
/// Resets the render graph for a new frame.
/// Reuses existing allocations to minimize GC.
///
public void Reset()
{
// Clear blackboard data
Blackboard.Clear();
// Reset resources but keep allocations
_resources.Reset();
// Reset aliasing manager
_aliasingManager.Reset();
// Clear compiled barriers
_compiledBarriers.Clear();
// Return passes to the pool and reset count
for (var i = 0; i < _passes.Count; i++)
{
var pass = _passes[i];
pass.Reset(_objectPool);
}
_passes.Clear();
// Clear compiled passes list
_compiledPasses.Clear();
// Return native passes to pool
for (var i = 0; i < _nativePasses.Count; i++)
{
_objectPool.Return(_nativePasses[i]);
}
_nativePasses.Clear();
_compiled = false;
}
///
/// Imports an external texture into the render graph.
///
/// The external texture handle.
/// The identifier of the imported render graph texture. Invalid if import fails.
public Identifier ImportTexture(Handle texture, string name,
Color128 clearColor = default, float clearDepth = 1.0f, byte clearStencil = 0,
bool clearAtFirstUse = true, bool discardAtLastUse = true)
{
var r = _graphicsEngine.ResourceDatabase.GetResourceDescription(texture.AsResource());
if (r.IsFailure)
{
return Identifier.Invalid;
}
var desc = r.Value;
return _resources.ImportTexture(in desc._desc.textureDescription, texture, name, clearColor, clearDepth, clearStencil, clearAtFirstUse, discardAtLastUse);
}
///
/// Imports an external buffer into the render graph.
///
/// The external buffer handle.
/// The identifier of the imported render graph buffer. Invalid if import fails.
public Identifier ImportBuffer(Handle buffer, string name)
{
var r = _graphicsEngine.ResourceDatabase.GetResourceDescription(buffer.AsResource());
if (r.IsFailure)
{
return Identifier.Invalid;
}
var desc = r.Value;
return _resources.ImportBuffer(in desc._desc.bufferDescription, buffer, name);
}
public IRasterRenderGraphBuilder AddRasterRenderPass(string name, out TPassData passData)
where TPassData : class, new()
{
var renderPass = _objectPool.Rent>();
renderPass.Init(_passes.Count, _objectPool.Rent(), name, RenderPassType.Raster);
passData = renderPass.passData;
_passes.Add(renderPass);
_builder.Init(this, renderPass, _resources);
return _builder;
}
public IComputeRenderGraphBuilder AddComputeRenderPass(string name, out TPassData passData)
where TPassData : class, new()
{
var renderPass = _objectPool.Rent>();
renderPass.Init(_passes.Count, _objectPool.Rent(), name, RenderPassType.Compute);
passData = renderPass.passData;
_passes.Add(renderPass);
_builder.Init(this, renderPass, _resources);
return _builder;
}
public IUnsafeRenderGraphBuilder AddUnsafeRenderPass(string name, out TPassData passData)
where TPassData : class, new()
{
var renderPass = _objectPool.Rent>();
renderPass.Init(_passes.Count, _objectPool.Rent(), name, RenderPassType.Unsafe);
passData = renderPass.passData;
_passes.Add(renderPass);
_builder.Init(this, renderPass, _resources);
return _builder;
}
///
/// Compiles the render graph by culling unused passes and determining resource lifetimes.
/// Delegates to RenderGraphCompiler for the actual compilation work.
///
public void Compile(in ViewState viewState)
{
if (_compiled)
{
return;
}
// Resolve texture sizes before computing hash
_resources.ResolveTextureSizes(in viewState);
// Compute structural hash for caching
var graphHash = RenderGraphHasher.ComputeGraphHash(_passes, _resources);
// Delegate to compiler
_compiler.Compile(in viewState, graphHash, _passes, _compiledPasses, _nativePasses, _compiledBarriers);
_compiled = true;
}
///
/// Executes all compiled passes using native render passes where possible.
/// Delegates to RenderGraphExecutor for the actual execution work.
///
public void Execute(ICommandBuffer cmd)
{
if (!_compiled)
{
throw new InvalidOperationException("Render graph must be compiled before execution. Call Compile(viewState) first.");
}
_executor.Execute(cmd, _compiledPasses, _nativePasses, _compiledBarriers);
}
public void Dispose()
{
_compiler.Dispose();
// HACK: Ideally, we should have a Dispose method. But for now, we just reset to release resources.
Reset();
}
}