Render graph integration and resource management refactor

Introduces a full-featured render graph system with pass culling, resource aliasing, and automatic barrier generation. Refactors resource and barrier APIs, improves error handling, and unifies result types. Renderer and render passes now use the new graph-based workflow. Updates shader includes, adds a blit shader, and improves HLSL parsing. Removes dynamic descriptor heaps in favor of persistent ones. Project file now includes the render graph module. Lays the foundation for advanced rendering features and improved memory efficiency.
This commit is contained in:
2026-01-21 18:32:03 +09:00
parent 1c155f962c
commit 92b966fe0d
62 changed files with 4843 additions and 621 deletions

View File

@@ -0,0 +1,172 @@
using Ghost.Core;
using System.Runtime.CompilerServices;
namespace Ghost.Graphics.RenderGraphModule;
/// <summary>
/// Represents different types of render passes.
/// </summary>
public enum RenderPassType : byte
{
Raster,
Compute,
Unsafe
}
/// <summary>
/// Base class for render passes.
/// Uses pooling to avoid allocations after the first frame.
/// </summary>
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[8];
public int maxColorIndex = -1;
public List<Identifier<RGResource>> randomAccess = new(8);
// Resource dependencies
public readonly List<Identifier<RGResource>>[] resourceReads = new List<Identifier<RGResource>>[(int)RenderGraphResourceType.Count];
public readonly List<Identifier<RGResource>>[] resourceWrites = new List<Identifier<RGResource>>[(int)RenderGraphResourceType.Count];
public readonly List<Identifier<RGResource>>[] resourceCreates = new List<Identifier<RGResource>>[(int)RenderGraphResourceType.Count];
// Buffer usage hints (maps buffer resource ID to hint)
public readonly Dictionary<int, BufferHint> bufferHints = new(8);
// Execution state
public bool culled;
public bool hasSideEffects;
public RenderGraphPassBase()
{
for (int i = 0; i < (int)RenderGraphResourceType.Count; i++)
{
resourceReads[i] = new List<Identifier<RGResource>>(8);
resourceWrites[i] = new List<Identifier<RGResource>>(4);
resourceCreates[i] = new List<Identifier<RGResource>>(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();
}
bufferHints.Clear();
culled = false;
hasSideEffects = false;
}
}
internal abstract class RenderGraphPass<TPassData, TRenderContext> : RenderGraphPassBase
where TPassData : class, new()
{
public TPassData passData = null!;
public Action<TPassData, TRenderContext>? 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<TPassData> : RenderGraphPass<TPassData, IRasterRenderContext>
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<TPassData> : RenderGraphPass<TPassData, IComputeRenderContext>
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<TPassData> : RenderGraphPass<TPassData, IUnsafeRenderContext>
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);
}
}