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