using Ghost.Core; using Ghost.Graphics.RHI; using System.Runtime.CompilerServices; namespace Ghost.Graphics.RenderGraphModule; /// /// Represents different types of render passes. /// public enum RenderPassType : byte { Raster, Compute, Unsafe } /// /// Base class for render passes. /// Uses pooling to avoid allocations after the first frame. /// internal abstract class RenderGraphPassBase { public string name = string.Empty; public int index; public RenderPassType type; public bool allowCulling = true; public bool asyncCompute; public TextureAccess depthAccess; public TextureAccess[] colorAccess = new TextureAccess[RHIUtility.MAX_RENDER_TARGETS]; public int maxColorIndex = -1; public List> randomAccess = new(8); // Resource dependencies public readonly List>[] resourceReads = new List>[(int)RenderGraphResourceType.Count]; public readonly List>[] resourceWrites = new List>[(int)RenderGraphResourceType.Count]; public readonly List>[] resourceCreates = new List>[(int)RenderGraphResourceType.Count]; // Execution state public bool culled; public bool hasSideEffects; public RenderGraphPassBase() { for (int i = 0; i < (int)RenderGraphResourceType.Count; i++) { resourceReads[i] = new List>(8); resourceWrites[i] = new List>(4); resourceCreates[i] = new List>(4); } } public abstract void Execute(RenderGraphContext context); public abstract bool HasRenderFunc(); public abstract int GetRenderFuncHashCode(); public virtual void Reset(RenderGraphObjectPool pool) { name = string.Empty; index = -1; type = RenderPassType.Raster; allowCulling = true; asyncCompute = false; depthAccess = default; colorAccess.AsSpan().Clear(); maxColorIndex = -1; randomAccess.Clear(); for (var i = 0; i < (int)RenderGraphResourceType.Count; i++) { resourceReads[i].Clear(); resourceWrites[i].Clear(); resourceCreates[i].Clear(); } culled = false; hasSideEffects = false; } } internal abstract class RenderGraphPass : RenderGraphPassBase where TPassData : class, new() { public TPassData passData = null!; public Action? renderFunc; public void Init(int index, TPassData passData, string name, RenderPassType type) { this.index = index; this.passData = passData; this.name = name; this.type = type; } public sealed override bool HasRenderFunc() { return renderFunc != null; } public override int GetRenderFuncHashCode() { if (renderFunc == null) { return 0; } var methodHashCode = RuntimeHelpers.GetHashCode(renderFunc.Method); return renderFunc.Target == null ? methodHashCode : methodHashCode ^ RuntimeHelpers.GetHashCode(renderFunc.Target); // static deleget does not have target } public override void Reset(RenderGraphObjectPool pool) { base.Reset(pool); pool.Return(passData); passData = null!; renderFunc = null; } } internal sealed class RasterRenderGraphPass : RenderGraphPass where TPassData : class, new() { public override void Execute(RenderGraphContext context) { renderFunc!(passData, context); } public override void Reset(RenderGraphObjectPool pool) { base.Reset(pool); pool.Return(this); } } internal sealed class ComputeRenderGraphPass : RenderGraphPass where TPassData : class, new() { public override void Execute(RenderGraphContext context) { renderFunc!(passData, context); } public override void Reset(RenderGraphObjectPool pool) { base.Reset(pool); pool.Return(this); } } internal sealed class UnsafeRenderGraphPass : RenderGraphPass where TPassData : class, new() { public override void Execute(RenderGraphContext context) { renderFunc!(passData, context); } public override void Reset(RenderGraphObjectPool pool) { base.Reset(pool); pool.Return(this); } }