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