using Ghost.Core; using System.Diagnostics; namespace Ghost.RenderGraph.Concept; [Flags] public enum AccessFlags { None = 0, Read = 1 << 0, Write = 1 << 1, ReadWrite = Read | Write, } public interface IRenderGraphBuilder : IDisposable { /// /// Enables or disables pass culling for the current context. /// /// A value indicating whether pass culling is allowed. void AllowPassCulling(bool value); /// /// Creates a new texture resource based on the specified descriptor. /// /// A structure that defines the properties and configuration of the texture to create. /// An identifier for the newly created texture resource. Identifier CreateTexture(in TextureDescriptor descriptor); /// /// Registers the specified texture for use in the current render graph pass with the given access mode. /// /// The identifier of the texture to be used in the render graph pass. /// The access mode specifying how the texture will be read or written during the pass. /// An identifier for the texture. Identifier UseTexture(Identifier texture, AccessFlags accessMode); } public interface IRasterRenderGraphBuilder : IRenderGraphBuilder { /// /// Binds a texture for random access operations within the current rendering pass. /// /// The identifier of the texture to be used for random access. /// An identifier for the texture. Identifier UseRandomAccessTexture(Identifier texture); /// /// Specifies that the given buffer will be used for random access operations with the specified access mode within the current context. /// /// An identifier for the buffer to be used for random access. Must reference a valid buffer resource. /// An identifier for the buffer. Identifier UseRandomAccessBuffer(Identifier buffer); /// /// Sets the color attachment at the specified index to the given texture. /// /// The identifier of the texture to use as the color attachment. /// The zero-based index of the color attachment to set. void SetColorAttachment(Identifier texture, int index); /// /// Sets the depth attachment for the current render pass using the specified texture. /// /// The identifier of the texture to use as the depth attachment. Cannot be null. void SetDepthAttachment(Identifier texture); /// /// Sets the function used to render a pass with the specified pass data and render context. /// /// The type of data associated with the render pass. /// The delegate that defines the rendering logic for the pass. void SetRenderFunc(Action renderFunc) where TPassData : class, new(); } public interface IComputeRenderGraphBuilder : IRenderGraphBuilder { /// /// Enables or disables asynchronous compute operations. /// /// true to enable asynchronous compute; otherwise, false. void EnableAsyncCompute(bool value); /// /// Sets the render function to be invoked during the compute rendering process. /// /// The type of the data object passed to the render function. /// The delegate that defines the rendering logic to execute. void SetRenderFunc(Action renderFunc) where TPassData : class, new(); } internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGraphBuilder { private RenderGraph _graph = null!; private RenderGraphPassBase _pass = null!; private RenderGraphResourceRegistry _resources = null!; private bool _disposed; internal void Init(RenderGraph graph, RenderGraphPassBase pass, RenderGraphResourceRegistry resources) { _graph = graph; _pass = pass; _resources = resources; _disposed = false; } private void ThrowIfDisposed() { ObjectDisposedException.ThrowIf(_disposed, this); } private Identifier UseResource(Identifier resource, AccessFlags accessFlags) { if (accessFlags.HasFlag(AccessFlags.Read)) { _pass.resourceReads.Add(resource); _resources.AddConsumer(resource, _pass.index); } if (accessFlags.HasFlag(AccessFlags.Write)) { _pass.resourceWrites.Add(resource); _resources.SetProducer(resource, _pass.index); } return resource; } public void AllowPassCulling(bool value) { _pass.allowCulling = value; } public void EnableAsyncCompute(bool value) { _pass.asyncCompute = value; } public Identifier CreateTexture(in TextureDescriptor descriptor) { ThrowIfDisposed(); var handle = _resources.CreateTexture(descriptor); _pass.resourceCreates.Add(handle.AsResource()); _resources.SetProducer(handle.AsResource(), _pass.index); return handle; } public Identifier UseTexture(Identifier texture, AccessFlags flags) { ThrowIfDisposed(); return UseResource(texture.AsResource(), flags).AsTexture(); } public Identifier UseRandomAccessTexture(Identifier texture) { ThrowIfDisposed(); var resource = texture.AsResource(); UseResource(resource, AccessFlags.ReadWrite); _pass.randomAccess.Add(resource); return texture; } public Identifier UseRandomAccessBuffer(Identifier buffer) { ThrowIfDisposed(); var resource = buffer.AsResource(); UseResource(resource, AccessFlags.ReadWrite); _pass.randomAccess.Add(resource); return buffer; } public void SetColorAttachment(Identifier texture, int index) { ThrowIfDisposed(); Debug.Assert(index >= 0 && index < _pass.colorAccess.Length, "Color attachment index out of range."); var id = UseTexture(texture, AccessFlags.Write); if (_pass.colorAccess[index].id == id || _pass.colorAccess[index].id.IsInvalid) { _pass.maxColorIndex = Math.Max(_pass.maxColorIndex, index); _pass.colorAccess[index] = new TextureAccess(id, AccessFlags.Write); } else { throw new InvalidOperationException($"Color attachment at index {index} is already set to a different texture."); } } public void SetDepthAttachment(Identifier texture) { ThrowIfDisposed(); var id = UseTexture(texture, AccessFlags.Write); if (_pass.depthAccess.id == id || _pass.depthAccess.id.IsInvalid) { _pass.depthAccess = new TextureAccess(id, AccessFlags.Write); } else { throw new InvalidOperationException("Depth attachment is already set to a different texture."); } } public void SetRenderFunc(Action renderFunc) where TPassData : class, new() { ((RasterRenderGraphPass)_pass).renderFunc = renderFunc; } public void SetRenderFunc(Action renderFunc) where TPassData : class, new() { ((ComputeRenderGraphPass)_pass).renderFunc = renderFunc; } public void Dispose() { if (_disposed) { return; } if (!_pass.HasRenderFunc()) { throw new InvalidOperationException("RenderGraphBuilder must be disposed after setting up the render function."); } _graph = null!; _pass = null!; _resources = null!; _disposed = true; } }