using System; using System.Collections.Generic; using UnityEngine.Rendering; namespace UnityEngine.Experimental.Rendering.RenderGraphModule { /// /// Use this struct to set up a new Render Pass. /// public struct RenderGraphBuilder : IDisposable { RenderGraphPass m_RenderPass; RenderGraphResourceRegistry m_Resources; RenderGraph m_RenderGraph; bool m_Disposed; #region Public Interface /// /// Specify that the pass will use a Texture resource as a color render target. /// This has the same effect as WriteTexture and also automatically sets the Texture to use as a render target. /// /// The Texture resource to use as a color render target. /// Index for multiple render target usage. /// An updated resource handle to the input resource. public TextureHandle UseColorBuffer(in TextureHandle input, int index) { CheckResource(input.handle, true); m_Resources.IncrementWriteCount(input.handle); m_RenderPass.SetColorBuffer(input, index); return input; } /// /// Specify that the pass will use a Texture resource as a depth buffer. /// /// The Texture resource to use as a depth buffer during the pass. /// Specify the access level for the depth buffer. This allows you to say whether you will read from or write to the depth buffer, or do both. /// An updated resource handle to the input resource. public TextureHandle UseDepthBuffer(in TextureHandle input, DepthAccess flags) { CheckResource(input.handle, true); if ((flags & DepthAccess.Write) != 0) m_Resources.IncrementWriteCount(input.handle); if ((flags & DepthAccess.Read) != 0) { if (!m_Resources.IsRenderGraphResourceImported(input.handle) && m_Resources.TextureNeedsFallback(input)) WriteTexture(input); } m_RenderPass.SetDepthBuffer(input, flags); return input; } /// /// Specify a Texture resource to read from during the pass. /// /// The Texture resource to read from during the pass. /// An updated resource handle to the input resource. public TextureHandle ReadTexture(in TextureHandle input) { CheckResource(input.handle); if (!m_Resources.IsRenderGraphResourceImported(input.handle) && m_Resources.TextureNeedsFallback(input)) { // If texture is read from but never written to, return a fallback black texture to have valid reads // Return one from the preallocated default textures if possible var desc = m_Resources.GetTextureResourceDesc(input.handle); if (!desc.bindTextureMS) { if (desc.dimension == TextureXR.dimension) return m_RenderGraph.defaultResources.blackTextureXR; else if (desc.dimension == TextureDimension.Tex3D) return m_RenderGraph.defaultResources.blackTexture3DXR; else return m_RenderGraph.defaultResources.blackTexture; } // If not, force a write to the texture so that it gets allocated, and ensure it gets initialized with a clear color if (!desc.clearBuffer) m_Resources.ForceTextureClear(input.handle, Color.black); WriteTexture(input); } m_RenderPass.AddResourceRead(input.handle); return input; } /// /// Specify a Texture resource to write to during the pass. /// /// The Texture resource to write to during the pass. /// An updated resource handle to the input resource. public TextureHandle WriteTexture(in TextureHandle input) { CheckResource(input.handle); m_Resources.IncrementWriteCount(input.handle); m_RenderPass.AddResourceWrite(input.handle); return input; } /// /// Specify a Texture resource to read and write to during the pass. /// /// The Texture resource to read and write to during the pass. /// An updated resource handle to the input resource. public TextureHandle ReadWriteTexture(in TextureHandle input) { CheckResource(input.handle); m_Resources.IncrementWriteCount(input.handle); m_RenderPass.AddResourceWrite(input.handle); m_RenderPass.AddResourceRead(input.handle); return input; } /// /// Create a new Render Graph Texture resource. /// This texture will only be available for the current pass and will be assumed to be both written and read so users don't need to add explicit read/write declarations. /// /// Texture descriptor. /// A new transient TextureHandle. public TextureHandle CreateTransientTexture(in TextureDesc desc) { var result = m_Resources.CreateTexture(desc, m_RenderPass.index); m_RenderPass.AddTransientResource(result.handle); return result; } /// /// Create a new Render Graph Texture resource using the descriptor from another texture. /// This texture will only be available for the current pass and will be assumed to be both written and read so users don't need to add explicit read/write declarations. /// /// Texture from which the descriptor should be used. /// A new transient TextureHandle. public TextureHandle CreateTransientTexture(in TextureHandle texture) { var desc = m_Resources.GetTextureResourceDesc(texture.handle); var result = m_Resources.CreateTexture(desc, m_RenderPass.index); m_RenderPass.AddTransientResource(result.handle); return result; } /// /// Specify a Renderer List resource to use during the pass. /// /// The Renderer List resource to use during the pass. /// An updated resource handle to the input resource. public RendererListHandle UseRendererList(in RendererListHandle input) { m_RenderPass.UseRendererList(input); return input; } /// /// Specify a Compute Buffer resource to read from during the pass. /// /// The Compute Buffer resource to read from during the pass. /// An updated resource handle to the input resource. public ComputeBufferHandle ReadComputeBuffer(in ComputeBufferHandle input) { CheckResource(input.handle); m_RenderPass.AddResourceRead(input.handle); return input; } /// /// Specify a Compute Buffer resource to write to during the pass. /// /// The Compute Buffer resource to write to during the pass. /// An updated resource handle to the input resource. public ComputeBufferHandle WriteComputeBuffer(in ComputeBufferHandle input) { CheckResource(input.handle); m_RenderPass.AddResourceWrite(input.handle); m_Resources.IncrementWriteCount(input.handle); return input; } /// /// Create a new Render Graph Compute Buffer resource. /// This Compute Buffer will only be available for the current pass and will be assumed to be both written and read so users don't need to add explicit read/write declarations. /// /// Compute Buffer descriptor. /// A new transient ComputeBufferHandle. public ComputeBufferHandle CreateTransientComputeBuffer(in ComputeBufferDesc desc) { var result = m_Resources.CreateComputeBuffer(desc, m_RenderPass.index); m_RenderPass.AddTransientResource(result.handle); return result; } /// /// Create a new Render Graph Compute Buffer resource using the descriptor from another Compute Buffer. /// This Compute Buffer will only be available for the current pass and will be assumed to be both written and read so users don't need to add explicit read/write declarations. /// /// Compute Buffer from which the descriptor should be used. /// A new transient ComputeBufferHandle. public ComputeBufferHandle CreateTransientComputeBuffer(in ComputeBufferHandle computebuffer) { var desc = m_Resources.GetComputeBufferResourceDesc(computebuffer.handle); var result = m_Resources.CreateComputeBuffer(desc, m_RenderPass.index); m_RenderPass.AddTransientResource(result.handle); return result; } /// /// Specify the render function to use for this pass. /// A call to this is mandatory for the pass to be valid. /// /// The Type of the class that provides data to the Render Pass. /// Render function for the pass. public void SetRenderFunc(RenderFunc renderFunc) where PassData : class, new() { ((RenderGraphPass)m_RenderPass).renderFunc = renderFunc; } /// /// Enable asynchronous compute for this pass. /// /// Set to true to enable asynchronous compute. public void EnableAsyncCompute(bool value) { m_RenderPass.EnableAsyncCompute(value); } /// /// Allow or not pass culling /// By default all passes can be culled out if the render graph detects it's not actually used. /// In some cases, a pass may not write or read any texture but rather do something with side effects (like setting a global texture parameter for example). /// This function can be used to tell the system that it should not cull this pass. /// /// True to allow pass culling. public void AllowPassCulling(bool value) { m_RenderPass.AllowPassCulling(value); } /// /// Dispose the RenderGraphBuilder instance. /// public void Dispose() { Dispose(true); } /// /// Allow or not pass culling based on renderer list results /// By default all passes can be culled out if the render graph detects they are using a renderer list that is empty (does not draw any geometry) /// In some cases, a pass may not write or read any texture but rather do something with side effects (like setting a global texture parameter for example). /// This function can be used to tell the system that it should not cull this pass. /// /// True to allow pass culling. public void AllowRendererListCulling(bool value) { m_RenderPass.AllowRendererListCulling(value); } /// /// Used to indicate that a pass depends on an external renderer list (that is not directly used in this pass). /// /// The renderer list handle this pass depends on. /// public RendererListHandle DependsOn(in RendererListHandle input) { m_RenderPass.UseRendererList(input); return input; } #endregion #region Internal Interface internal RenderGraphBuilder(RenderGraphPass renderPass, RenderGraphResourceRegistry resources, RenderGraph renderGraph) { m_RenderPass = renderPass; m_Resources = resources; m_RenderGraph = renderGraph; m_Disposed = false; } void Dispose(bool disposing) { if (m_Disposed) return; m_RenderGraph.OnPassAdded(m_RenderPass); m_Disposed = true; } void CheckResource(in ResourceHandle res, bool dontCheckTransientReadWrite = false) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (res.IsValid()) { int transientIndex = m_Resources.GetRenderGraphResourceTransientIndex(res); // We have dontCheckTransientReadWrite here because users may want to use UseColorBuffer/UseDepthBuffer API to benefit from render target auto binding. In this case we don't want to raise the error. if (transientIndex == m_RenderPass.index && !dontCheckTransientReadWrite) { Debug.LogError($"Trying to read or write a transient resource at pass {m_RenderPass.name}.Transient resource are always assumed to be both read and written."); } if (transientIndex != -1 && transientIndex != m_RenderPass.index) { throw new ArgumentException($"Trying to use a transient texture (pass index {transientIndex}) in a different pass (pass index {m_RenderPass.index})."); } } else { throw new ArgumentException($"Trying to use an invalid resource (pass {m_RenderPass.name})."); } #endif } internal void GenerateDebugData(bool value) { m_RenderPass.GenerateDebugData(value); } #endregion } }