diff --git a/com.unity.render-pipelines.core/Runtime/Debugging/ProfilingScope.cs b/com.unity.render-pipelines.core/Runtime/Debugging/ProfilingScope.cs index e0e17821416..1a7176e509e 100644 --- a/com.unity.render-pipelines.core/Runtime/Debugging/ProfilingScope.cs +++ b/com.unity.render-pipelines.core/Runtime/Debugging/ProfilingScope.cs @@ -98,6 +98,42 @@ public ProfilingSampler(string name) #endif } + /// + /// Begin the profiling block. + /// + /// Command buffer used by the profiling block. + public void Begin(CommandBuffer cmd) + { + if (cmd != null) +#if UNITY_USE_RECORDER + if (sampler != null) + cmd.BeginSample(sampler); + else + cmd.BeginSample(name); +#else + cmd.BeginSample(name); +#endif + inlineSampler?.Begin(); + } + + /// + /// End the profiling block. + /// + /// Command buffer used by the profiling block. + public void End(CommandBuffer cmd) + { + if (cmd != null) +#if UNITY_USE_RECORDER + if (sampler != null) + cmd.EndSample(sampler); + else + cmd.EndSample(name); +#else + m_Cmd.EndSample(name); +#endif + inlineSampler?.End(); + } + internal bool IsValid() { return (sampler != null && inlineSampler != null); } internal CustomSampler sampler { get; private set; } @@ -187,11 +223,9 @@ public bool enableRecording /// public struct ProfilingScope : IDisposable { - string m_Name; - CommandBuffer m_Cmd; - bool m_Disposed; - CustomSampler m_Sampler; - CustomSampler m_InlineSampler; + CommandBuffer m_Cmd; + bool m_Disposed; + ProfilingSampler m_Sampler; /// /// Profiling Scope constructor @@ -202,29 +236,8 @@ public ProfilingScope(CommandBuffer cmd, ProfilingSampler sampler) { m_Cmd = cmd; m_Disposed = false; - if (sampler != null) - { - m_Name = sampler.name; // Don't use CustomSampler.name because it causes garbage - m_Sampler = sampler.sampler; - m_InlineSampler = sampler.inlineSampler; - } - else - { - m_Name = "NullProfilingSampler"; // Don't use CustomSampler.name because it causes garbage - m_Sampler = null; - m_InlineSampler = null; - } - - if (cmd != null) -#if UNITY_USE_RECORDER - if (m_Sampler != null) - cmd.BeginSample(m_Sampler); - else - cmd.BeginSample(m_Name); -#else - cmd.BeginSample(m_Name); -#endif - m_InlineSampler?.Begin(); + m_Sampler = sampler; + m_Sampler?.Begin(m_Cmd); } /// @@ -246,16 +259,7 @@ void Dispose(bool disposing) // this but will generate garbage on every frame (and this struct is used quite a lot). if (disposing) { - if (m_Cmd != null) -#if UNITY_USE_RECORDER - if (m_Sampler != null) - m_Cmd.EndSample(m_Sampler); - else - m_Cmd.EndSample(m_Name); -#else - m_Cmd.EndSample(m_Name); -#endif - m_InlineSampler?.End(); + m_Sampler?.End(m_Cmd); } m_Disposed = true; diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs index 700251d73a0..8cd7f991fbc 100644 --- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs @@ -1,8 +1,6 @@ using System; -using System.Diagnostics; using System.Collections.Generic; using UnityEngine.Rendering; -using UnityEngine.Profiling; namespace UnityEngine.Experimental.Rendering.RenderGraphModule { @@ -95,110 +93,81 @@ public class RenderGraph ///Maximum number of MRTs supported by Render Graph. public static readonly int kMaxMRTCount = 8; - [DebuggerDisplay("RenderPass ({name})")] - internal abstract class RenderPass - { - internal RenderFunc GetExecuteDelegate() - where PassData : class, new() => ((RenderPass)this).renderFunc; - - internal abstract void Execute(RenderGraphContext renderGraphContext); - internal abstract void Release(RenderGraphContext renderGraphContext); - internal abstract bool HasRenderFunc(); - - internal string name; - internal int index; - internal ProfilingSampler customSampler; - internal List textureReadList = new List(); - internal List textureWriteList = new List(); - internal List bufferReadList = new List(); - internal List bufferWriteList = new List(); - internal List usedRendererListList = new List(); - internal bool enableAsyncCompute; - internal TextureHandle depthBuffer { get { return m_DepthBuffer; } } - internal TextureHandle[] colorBuffers { get { return m_ColorBuffers; } } - internal int colorBufferMaxIndex { get { return m_MaxColorBufferIndex; } } - - protected TextureHandle[] m_ColorBuffers = new TextureHandle[kMaxMRTCount]; - protected TextureHandle m_DepthBuffer; - protected int m_MaxColorBufferIndex = -1; - - internal void Clear() - { - name = ""; - index = -1; - customSampler = null; - textureReadList.Clear(); - textureWriteList.Clear(); - bufferReadList.Clear(); - bufferWriteList.Clear(); - usedRendererListList.Clear(); - enableAsyncCompute = false; - - // Invalidate everything - m_MaxColorBufferIndex = -1; - m_DepthBuffer = new TextureHandle(); - for (int i = 0; i < RenderGraph.kMaxMRTCount; ++i) - { - m_ColorBuffers[i] = new TextureHandle(); - } - } - - internal void SetColorBuffer(TextureHandle resource, int index) - { - Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0); - m_MaxColorBufferIndex = Math.Max(m_MaxColorBufferIndex, index); - m_ColorBuffers[index] = resource; - textureWriteList.Add(resource); - } + internal struct CompiledResourceInfo + { + public List producers; + public bool resourceCreated; + public int lastReadPassIndex; + public int firstWritePassIndex; + public int refCount; - internal void SetDepthBuffer(TextureHandle resource, DepthAccess flags) + public void Reset() { - m_DepthBuffer = resource; - if ((flags | DepthAccess.Read) != 0) - textureReadList.Add(resource); - if ((flags | DepthAccess.Write) != 0) - textureWriteList.Add(resource); - + if (producers == null) + producers = new List(); + + producers.Clear(); + resourceCreated = false; + lastReadPassIndex = -1; + firstWritePassIndex = int.MaxValue; + refCount = 0; } } - internal sealed class RenderPass : RenderPass - where PassData : class, new() + internal struct CompiledPassInfo { - internal PassData data; - internal RenderFunc renderFunc; - - internal override void Execute(RenderGraphContext renderGraphContext) + public RenderGraphPass pass; + public List textureCreateList; + public List textureReleaseList; + public int refCount; + public bool pruned; + public bool hasSideEffect; + public int syncToPassIndex; // Index of the pass that needs to be waited for. + public int syncFromPassIndex; // Smaller pass index that waits for this pass. + public bool needGraphicsFence; + public GraphicsFence fence; + + public bool enableAsyncCompute { get { return pass.enableAsyncCompute; } } + + public void Reset(RenderGraphPass pass) { - GetExecuteDelegate()(data, renderGraphContext); - } + this.pass = pass; - internal override void Release(RenderGraphContext renderGraphContext) - { - Clear(); - renderGraphContext.renderGraphPool.Release(data); - data = null; - renderFunc = null; - renderGraphContext.renderGraphPool.Release(this); - } + if (textureCreateList == null) + { + textureCreateList = new List(); + textureReleaseList = new List(); + } - internal override bool HasRenderFunc() - { - return renderFunc != null; + textureCreateList.Clear(); + textureReleaseList.Clear(); + refCount = 0; + pruned = false; + hasSideEffect = false; + syncToPassIndex = -1; + syncFromPassIndex = -1; + needGraphicsFence = false; } } - RenderGraphResourceRegistry m_Resources; - RenderGraphObjectPool m_RenderGraphPool = new RenderGraphObjectPool(); - List m_RenderPasses = new List(); - List m_RendererLists = new List(); - RenderGraphDebugParams m_DebugParameters = new RenderGraphDebugParams(); - RenderGraphLogger m_Logger = new RenderGraphLogger(); - RenderGraphDefaultResources m_DefaultResources = new RenderGraphDefaultResources(); + RenderGraphResourceRegistry m_Resources; + RenderGraphObjectPool m_RenderGraphPool = new RenderGraphObjectPool(); + List m_RenderPasses = new List(); + List m_RendererLists = new List(); + RenderGraphDebugParams m_DebugParameters = new RenderGraphDebugParams(); + RenderGraphLogger m_Logger = new RenderGraphLogger(); + RenderGraphDefaultResources m_DefaultResources = new RenderGraphDefaultResources(); + Dictionary m_DefaultProfilingSamplers = new Dictionary(); + + // Compiled Render Graph info. + DynamicArray m_CompiledTextureInfos = new DynamicArray(); + DynamicArray m_CompiledBufferInfos = new DynamicArray(); + DynamicArray m_CompiledPassInfos = new DynamicArray(); + Stack m_PruningStack = new Stack(); #region Public Interface - // TODO: Currently only needed by SSAO to sample correctly depth texture mips. Need to figure out a way to hide this behind a proper formalization. + // TODO RENDERGRAPH: Currently only needed by SSAO to sample correctly depth texture mips. Need to figure out a way to hide this behind a proper formalization. /// /// Gets the RTHandleProperties structure associated with the Render Graph's RTHandle System. /// @@ -347,12 +316,8 @@ public ComputeBufferHandle ImportComputeBuffer(ComputeBuffer computeBuffer) /// A new instance of a RenderGraphBuilder used to setup the new Render Pass. public RenderGraphBuilder AddRenderPass(string passName, out PassData passData, ProfilingSampler sampler = null) where PassData : class, new() { - var renderPass = m_RenderGraphPool.Get>(); - renderPass.Clear(); - renderPass.index = m_RenderPasses.Count; - renderPass.data = m_RenderGraphPool.Get(); - renderPass.name = passName; - renderPass.customSampler = sampler; + var renderPass = m_RenderGraphPool.Get>(); + renderPass.Initialize(m_RenderPasses.Count, m_RenderGraphPool.Get(), passName, sampler != null ? sampler : GetDefaultProfilingSampler(passName)); passData = renderPass.data; @@ -369,88 +334,363 @@ public ComputeBufferHandle ImportComputeBuffer(ComputeBuffer computeBuffer) /// Render Graph execution parameters. public void Execute(ScriptableRenderContext renderContext, CommandBuffer cmd, in RenderGraphExecuteParams parameters) { - m_Logger.Initialize(); + try + { + m_Logger.Initialize(); - // Update RTHandleSystem with size for this rendering pass. - m_Resources.SetRTHandleReferenceSize(parameters.renderingWidth, parameters.renderingHeight, parameters.msaaSamples); + // Update RTHandleSystem with size for this rendering pass. + m_Resources.SetRTHandleReferenceSize(parameters.renderingWidth, parameters.renderingHeight, parameters.msaaSamples); - LogFrameInformation(parameters.renderingWidth, parameters.renderingHeight); + LogFrameInformation(parameters.renderingWidth, parameters.renderingHeight); - // First pass, traversal and pruning - for (int passIndex = 0; passIndex < m_RenderPasses.Count; ++passIndex) + CompileRenderGraph(); + ExecuteRenderGraph(renderContext, cmd); + } + catch (Exception e) { - var pass = m_RenderPasses[passIndex]; + Debug.LogError("Render Graph Execution error"); + Debug.LogException(e); + } + finally + { + ClearCompiledGraph(); - // TODO: Pruning + if (m_DebugParameters.logFrameInformation || m_DebugParameters.logResources) + Debug.Log(m_Logger.GetLog()); - // Gather all renderer lists - m_RendererLists.AddRange(pass.usedRendererListList); + m_DebugParameters.logFrameInformation = false; + m_DebugParameters.logResources = false; } + } + #endregion - // Creates all renderer lists - m_Resources.CreateRendererLists(m_RendererLists); - LogRendererListsCreation(); + #region Internal Interface + private RenderGraph() + { - // Second pass, execution - RenderGraphContext rgContext = new RenderGraphContext(); - rgContext.cmd = cmd; - rgContext.renderContext = renderContext; - rgContext.renderGraphPool = m_RenderGraphPool; - rgContext.resources = m_Resources; - rgContext.defaultResources = m_DefaultResources; + } - try + void ClearCompiledGraph() + { + ClearRenderPasses(); + m_Resources.Clear(); + m_DefaultResources.Clear(); + m_RendererLists.Clear(); + m_CompiledBufferInfos.Clear(); + m_CompiledTextureInfos.Clear(); + m_CompiledPassInfos.Clear(); + } + + void InitializeCompilationData() + { + m_CompiledBufferInfos.Resize(m_Resources.GetComputeBufferResourceCount()); + for (int i = 0; i < m_CompiledBufferInfos.size; ++i) + m_CompiledBufferInfos[i].Reset(); + m_CompiledTextureInfos.Resize(m_Resources.GetTextureResourceCount()); + for (int i = 0; i < m_CompiledTextureInfos.size; ++i) + m_CompiledTextureInfos[i].Reset(); + m_CompiledPassInfos.Resize(m_RenderPasses.Count); + for (int i = 0; i < m_CompiledPassInfos.size; ++i) + m_CompiledPassInfos[i].Reset(m_RenderPasses[i]); + } + + void CountReferences() + { + for (int passIndex = 0; passIndex < m_CompiledPassInfos.size; ++passIndex) { - for (int passIndex = 0; passIndex < m_RenderPasses.Count; ++passIndex) + ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[passIndex]; + + var textureRead = passInfo.pass.textureReadList; + foreach (TextureHandle texture in textureRead) + { + ref CompiledResourceInfo info = ref m_CompiledTextureInfos[texture]; + info.refCount++; + } + + var textureWrite = passInfo.pass.textureWriteList; + foreach (TextureHandle texture in textureWrite) + { + ref CompiledResourceInfo info = ref m_CompiledTextureInfos[texture]; + info.producers.Add(passIndex); + passInfo.refCount++; + + // Writing to an imported texture is considered as a side effect because we don't know what users will do with it outside of render graph. + if (m_Resources.IsTextureImported(texture)) + passInfo.hasSideEffect = true; + } + + // Can't share the code with a generic func as TextureHandle and ComputeBufferHandle are both struct and can't inherit from a common struct with a shared API (thanks C#) + var bufferRead = passInfo.pass.bufferReadList; + foreach (ComputeBufferHandle buffer in bufferRead) + { + ref CompiledResourceInfo info = ref m_CompiledBufferInfos[buffer]; + info.refCount++; + } + + var bufferWrite = passInfo.pass.bufferWriteList; + foreach (ComputeBufferHandle buffer in bufferWrite) + { + ref CompiledResourceInfo info = ref m_CompiledBufferInfos[buffer]; + passInfo.refCount++; + } + } + } + + void PruneUnusedPasses(DynamicArray resourceUsageList) + { + // First gather resources that are never read. + m_PruningStack.Clear(); + for (int i = 0; i < resourceUsageList.size; ++i) + { + if (resourceUsageList[i].refCount == 0) { - var pass = m_RenderPasses[passIndex]; + m_PruningStack.Push(i); + } + } - if (!pass.HasRenderFunc()) + while (m_PruningStack.Count != 0) + { + var unusedResource = resourceUsageList[m_PruningStack.Pop()]; + foreach (var producerIndex in unusedResource.producers) + { + ref var producerInfo = ref m_CompiledPassInfos[producerIndex]; + producerInfo.refCount--; + if (producerInfo.refCount == 0 && !producerInfo.hasSideEffect) { - throw new InvalidOperationException(string.Format("RenderPass {0} was not provided with an execute function.", pass.name)); + // Producer is not necessary anymore as it produces zero resources + // Prune it and decrement refCount of all the textures it reads. + producerInfo.pruned = true; + foreach (var textureIndex in producerInfo.pass.textureReadList) + { + ref CompiledResourceInfo resourceInfo = ref resourceUsageList[textureIndex]; + resourceInfo.refCount--; + // If a texture is not used anymore, add it to the stack to be processed in subsequent iteration. + if (resourceInfo.refCount == 0) + m_PruningStack.Push(textureIndex); + } } + } + } + } + + void PruneUnusedPasses() + { + PruneUnusedPasses(m_CompiledTextureInfos); + PruneUnusedPasses(m_CompiledBufferInfos); + LogPrunedPasses(); + } + + int GetLatestProducerIndex(int passIndex, in CompiledResourceInfo info) + { + // We want to know the highest pass index below the current pass that writes to the resource. + int result = -1; + foreach (var producer in info.producers) + { + // producers are by construction in increasing order. + if (producer < passIndex) + result = producer; + else + return result; + } + + return result; + } + + + void UpdatePassSynchronization(ref CompiledPassInfo currentPassInfo, ref CompiledPassInfo producerPassInfo, int currentPassIndex, int lastProducer, ref int intLastSyncIndex) + { + // Current pass needs to wait for pass index lastProducer + currentPassInfo.syncToPassIndex = lastProducer; + // Update latest pass waiting for the other pipe. + intLastSyncIndex = lastProducer; + + // Producer will need a graphics fence that this pass will wait on. + producerPassInfo.needGraphicsFence = true; + // We update the producer pass with the index of the smallest pass waiting for it. + // This will be used to "lock" resource from being reused until the pipe has been synchronized. + if (producerPassInfo.syncFromPassIndex == -1) + producerPassInfo.syncFromPassIndex = currentPassIndex; + } - using (new ProfilingScope(cmd, pass.customSampler)) + void UpdateResourceSynchronization(ref int lastGraphicsPipeSync, ref int lastComputePipeSync, int currentPassIndex, in CompiledResourceInfo resource) + { + int lastProducer = GetLatestProducerIndex(currentPassIndex, resource); + if (lastProducer != -1) + { + ref CompiledPassInfo currentPassInfo = ref m_CompiledPassInfos[currentPassIndex]; + + //If the passes are on different pipes, we need synchronization. + if (m_CompiledPassInfos[lastProducer].enableAsyncCompute != currentPassInfo.enableAsyncCompute) + { + // Pass is on compute pipe, need sync with graphics pipe. + if (currentPassInfo.enableAsyncCompute) { - LogRenderPassBegin(pass); - using (new RenderGraphLogIndent(m_Logger)) + if (lastProducer > lastGraphicsPipeSync) { - PreRenderPassExecute(passIndex, pass, rgContext); - pass.Execute(rgContext); - PostRenderPassExecute(passIndex, pass, rgContext); + UpdatePassSynchronization(ref currentPassInfo, ref m_CompiledPassInfos[lastProducer], currentPassIndex, lastProducer, ref lastGraphicsPipeSync); + } + } + else + { + if (lastProducer > lastComputePipeSync) + { + UpdatePassSynchronization(ref currentPassInfo, ref m_CompiledPassInfos[lastProducer], currentPassIndex, lastProducer, ref lastComputePipeSync); } } } } - catch(Exception e) + } + + void UpdateResourceAllocationAndSynchronization() + { + int lastGraphicsPipeSync = -1; + int lastComputePipeSync = -1; + + // First go through all passes. + // - Update the last pass read index for each resource. + // - Add texture to creation list for passes that first write to a texture. + // - Update synchronization points for all resources between compute and graphics pipes. + for (int passIndex = 0; passIndex < m_CompiledPassInfos.size; ++passIndex) { - Debug.LogError("Render Graph Execution error"); - Debug.LogException(e); + ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[passIndex]; + + if (passInfo.pruned) + continue; + + foreach (TextureHandle texture in passInfo.pass.textureReadList) + { + ref CompiledResourceInfo resourceInfo = ref m_CompiledTextureInfos[texture]; + resourceInfo.lastReadPassIndex = Math.Max(resourceInfo.lastReadPassIndex, passIndex); + UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, resourceInfo); + } + + foreach (TextureHandle texture in passInfo.pass.textureWriteList) + { + ref CompiledResourceInfo resourceInfo = ref m_CompiledTextureInfos[texture]; + if (resourceInfo.firstWritePassIndex == int.MaxValue) + { + resourceInfo.firstWritePassIndex = Math.Min(resourceInfo.firstWritePassIndex, passIndex); + passInfo.textureCreateList.Add(texture); + } + passInfo.refCount++; + UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, resourceInfo); + } + + foreach (ComputeBufferHandle texture in passInfo.pass.bufferReadList) + { + UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, m_CompiledBufferInfos[texture]); + } + foreach (ComputeBufferHandle texture in passInfo.pass.bufferWriteList) + { + UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, m_CompiledBufferInfos[texture]); + } + + // Add transient resources that are only used during this pass. + foreach (TextureHandle texture in passInfo.pass.transientTextureList) + { + passInfo.textureCreateList.Add(texture); + + ref CompiledResourceInfo info = ref m_CompiledTextureInfos[texture]; + info.lastReadPassIndex = passIndex; + } + + // Gather all renderer lists + m_RendererLists.AddRange(passInfo.pass.usedRendererListList); } - finally + + // Now push textures to the release list of the pass that reads it last. + for (int i = 0; i < m_CompiledTextureInfos.size; ++i) { - ClearRenderPasses(); - m_Resources.Clear(); - m_DefaultResources.Clear(); - m_RendererLists.Clear(); + CompiledResourceInfo textureInfo = m_CompiledTextureInfos[i]; + if (textureInfo.lastReadPassIndex != -1) + { + // In case of async passes, we need to extend lifetime of resource to the first pass on the graphics pipeline that wait for async passes to be over. + // Otherwise, if we freed the resource right away during an async pass, another non async pass could reuse the resource even though the async pipe is not done. + if (m_CompiledPassInfos[textureInfo.lastReadPassIndex].enableAsyncCompute) + { + int currentPassIndex = textureInfo.lastReadPassIndex; + int firstWaitingPassIndex = m_CompiledPassInfos[currentPassIndex].syncFromPassIndex; + // Find the first async pass that is synchronized by the graphics pipeline (ie: passInfo.syncFromPassIndex != -1) + while (firstWaitingPassIndex == -1 && currentPassIndex < m_CompiledPassInfos.size) + { + currentPassIndex++; + if(m_CompiledPassInfos[currentPassIndex].enableAsyncCompute) + firstWaitingPassIndex = m_CompiledPassInfos[currentPassIndex].syncFromPassIndex; + } - if (m_DebugParameters.logFrameInformation || m_DebugParameters.logResources) - Debug.Log(m_Logger.GetLog()); + // Finally add the release command to the pass before the first pass that waits for the compute pipe. + ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[Math.Max(0, firstWaitingPassIndex - 1)]; + passInfo.textureReleaseList.Add(new TextureHandle(i)); - m_DebugParameters.logFrameInformation = false; - m_DebugParameters.logResources = false; + // Fail safe in case render graph is badly formed. + if (currentPassIndex == m_CompiledPassInfos.size) + { + RenderGraphPass invalidPass = m_RenderPasses[textureInfo.lastReadPassIndex]; + throw new InvalidOperationException($"Asynchronous pass {invalidPass.name} was never synchronized on the graphics pipeline."); + } + } + else + { + ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[textureInfo.lastReadPassIndex]; + passInfo.textureReleaseList.Add(new TextureHandle(i)); + } + } } + + // Creates all renderer lists + m_Resources.CreateRendererLists(m_RendererLists); } - #endregion - #region Internal Interface - private RenderGraph() + // Traverse the render graph: + // - Determines when resources are created/released + // - Determines async compute pass synchronization + // - Prune unused render passes. + void CompileRenderGraph() { + InitializeCompilationData(); + CountReferences(); + PruneUnusedPasses(); + UpdateResourceAllocationAndSynchronization(); + LogRendererListsCreation(); + } + + // Execute the compiled render graph + void ExecuteRenderGraph(ScriptableRenderContext renderContext, CommandBuffer cmd) + { + RenderGraphContext rgContext = new RenderGraphContext(); + rgContext.cmd = cmd; + rgContext.renderContext = renderContext; + rgContext.renderGraphPool = m_RenderGraphPool; + rgContext.resources = m_Resources; + rgContext.defaultResources = m_DefaultResources; + for (int passIndex = 0; passIndex < m_CompiledPassInfos.size; ++passIndex) + { + ref var passInfo = ref m_CompiledPassInfos[passIndex]; + if (passInfo.pruned) + continue; + + if (!passInfo.pass.HasRenderFunc()) + { + throw new InvalidOperationException(string.Format("RenderPass {0} was not provided with an execute function.", passInfo.pass.name)); + } + + using (new ProfilingScope(rgContext.cmd, passInfo.pass.customSampler)) + { + LogRenderPassBegin(passInfo); + using (new RenderGraphLogIndent(m_Logger)) + { + PreRenderPassExecute(passInfo, ref rgContext); + passInfo.pass.Execute(rgContext); + PostRenderPassExecute(cmd, ref passInfo, ref rgContext); + } + } + } } - void PreRenderPassSetRenderTargets(in RenderPass pass, RenderGraphContext rgContext) + void PreRenderPassSetRenderTargets(in CompiledPassInfo passInfo, RenderGraphContext rgContext) { + var pass = passInfo.pass; if (pass.depthBuffer.IsValid() || pass.colorBufferMaxIndex != -1) { var mrtArray = rgContext.renderGraphPool.GetTempArray(pass.colorBufferMaxIndex + 1); @@ -492,21 +732,61 @@ void PreRenderPassSetRenderTargets(in RenderPass pass, RenderGraphContext rgCont } } - void PreRenderPassExecute(int passIndex, in RenderPass pass, RenderGraphContext rgContext) + void PreRenderPassExecute(in CompiledPassInfo passInfo, ref RenderGraphContext rgContext) { - // TODO merge clear and setup here if possible - m_Resources.CreateAndClearTexturesForPass(rgContext, pass.index, pass.textureWriteList); - PreRenderPassSetRenderTargets(pass, rgContext); + // TODO RENDERGRAPH merge clear and setup here if possible + RenderGraphPass pass = passInfo.pass; + + // TODO RENDERGRAPH remove this when we do away with auto global texture setup + // (can't put it in the profiling scope otherwise it might be executed on compute queue which is not possible for global sets) m_Resources.PreRenderPassSetGlobalTextures(rgContext, pass.textureReadList); + + foreach (var texture in passInfo.textureCreateList) + m_Resources.CreateAndClearTexture(rgContext, texture); + + PreRenderPassSetRenderTargets(passInfo, rgContext); + + // Flush first the current command buffer on the render context. + rgContext.renderContext.ExecuteCommandBuffer(rgContext.cmd); + rgContext.cmd.Clear(); + + if (pass.enableAsyncCompute) + { + CommandBuffer asyncCmd = CommandBufferPool.Get(pass.name); + asyncCmd.SetExecutionFlags(CommandBufferExecutionFlags.AsyncCompute); + rgContext.cmd = asyncCmd; + } + + // Synchronize with graphics or compute pipe if needed. + if (passInfo.syncToPassIndex != -1) + { + rgContext.cmd.WaitOnAsyncGraphicsFence(m_CompiledPassInfos[passInfo.syncToPassIndex].fence); + } } - void PostRenderPassExecute(int passIndex, in RenderPass pass, RenderGraphContext rgContext) + void PostRenderPassExecute(CommandBuffer mainCmd, ref CompiledPassInfo passInfo, ref RenderGraphContext rgContext) { + RenderGraphPass pass = passInfo.pass; + + if (passInfo.needGraphicsFence) + passInfo.fence = rgContext.cmd.CreateAsyncGraphicsFence(); + + if (pass.enableAsyncCompute) + { + // The command buffer has been filled. We can kick the async task. + rgContext.renderContext.ExecuteCommandBufferAsync(rgContext.cmd, ComputeQueueType.Background); + CommandBufferPool.Release(rgContext.cmd); + rgContext.cmd = mainCmd; // Restore the main command buffer. + } + if (m_DebugParameters.unbindGlobalTextures) m_Resources.PostRenderPassUnbindGlobalTextures(rgContext, pass.textureReadList); m_RenderGraphPool.ReleaseAllTempAlloc(); - m_Resources.ReleaseTexturesForPass(rgContext, pass.index, pass.textureReadList, pass.textureWriteList); + + foreach (var texture in passInfo.textureReleaseList) + m_Resources.ReleaseTexture(rgContext, texture); + pass.Release(rgContext); } @@ -520,7 +800,7 @@ void LogFrameInformation(int renderingWidth, int renderingHeight) if (m_DebugParameters.logFrameInformation) { m_Logger.LogLine("==== Staring frame at resolution ({0}x{1}) ====", renderingWidth, renderingHeight); - m_Logger.LogLine("Number of passes declared: {0}", m_RenderPasses.Count); + m_Logger.LogLine("Number of passes declared: {0}\n", m_RenderPasses.Count); } } @@ -528,18 +808,57 @@ void LogRendererListsCreation() { if (m_DebugParameters.logFrameInformation) { - m_Logger.LogLine("Number of renderer lists created: {0}", m_RendererLists.Count); + m_Logger.LogLine("Number of renderer lists created: {0}\n", m_RendererLists.Count); + } + } + + void LogRenderPassBegin(in CompiledPassInfo passInfo) + { + if (m_DebugParameters.logFrameInformation) + { + RenderGraphPass pass = passInfo.pass; + + m_Logger.LogLine("[{0}][{1}] \"{2}\"", pass.index, pass.enableAsyncCompute ? "Compute" : "Graphics", pass.name); + using (new RenderGraphLogIndent(m_Logger)) + { + if (passInfo.syncToPassIndex != -1) + m_Logger.LogLine("Synchronize with [{0}]", passInfo.syncToPassIndex); + } } } - void LogRenderPassBegin(in RenderPass pass) + void LogPrunedPasses() { if (m_DebugParameters.logFrameInformation) { - m_Logger.LogLine("Executing pass \"{0}\" (index: {1})", pass.name, pass.index); + m_Logger.LogLine("Pass pruning report:"); + using (new RenderGraphLogIndent(m_Logger)) + { + for (int i = 0; i < m_CompiledPassInfos.size; ++i) + { + if (m_CompiledPassInfos[i].pruned) + { + var pass = m_RenderPasses[i]; + m_Logger.LogLine("[{0}] {1}", pass.index, pass.name); + } + } + m_Logger.LogLine("\n"); + } } } + ProfilingSampler GetDefaultProfilingSampler(string name) + { + int hash = name.GetHashCode(); + if (!m_DefaultProfilingSamplers.TryGetValue(hash, out var sampler)) + { + sampler = new ProfilingSampler(name); + m_DefaultProfilingSamplers.Add(hash, sampler); + } + + return sampler; + } + #endregion } } diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs index b100ba50abc..404565f4297 100644 --- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs @@ -8,7 +8,7 @@ namespace UnityEngine.Experimental.Rendering.RenderGraphModule /// public struct RenderGraphBuilder : IDisposable { - RenderGraph.RenderPass m_RenderPass; + RenderGraphPass m_RenderPass; RenderGraphResourceRegistry m_Resources; bool m_Disposed; @@ -22,8 +22,9 @@ public struct RenderGraphBuilder : IDisposable /// An updated resource handle to the input resource. public TextureHandle UseColorBuffer(TextureHandle input, int index) { + CheckTransientTexture(input); + m_RenderPass.SetColorBuffer(input, index); - m_Resources.UpdateTextureFirstWrite(input, m_RenderPass.index); return input; } @@ -35,11 +36,9 @@ public TextureHandle UseColorBuffer(TextureHandle input, int index) /// An updated resource handle to the input resource. public TextureHandle UseDepthBuffer(TextureHandle input, DepthAccess flags) { + CheckTransientTexture(input); + m_RenderPass.SetDepthBuffer(input, flags); - if ((flags | DepthAccess.Read) != 0) - m_Resources.UpdateTextureLastRead(input, m_RenderPass.index); - if ((flags | DepthAccess.Write) != 0) - m_Resources.UpdateTextureFirstWrite(input, m_RenderPass.index); return input; } @@ -50,8 +49,9 @@ public TextureHandle UseDepthBuffer(TextureHandle input, DepthAccess flags) /// An updated resource handle to the input resource. public TextureHandle ReadTexture(TextureHandle input) { - m_RenderPass.textureReadList.Add(input); - m_Resources.UpdateTextureLastRead(input, m_RenderPass.index); + CheckTransientTexture(input); + + m_RenderPass.AddTextureRead(input); return input; } @@ -62,12 +62,39 @@ public TextureHandle ReadTexture(TextureHandle input) /// An updated resource handle to the input resource. public TextureHandle WriteTexture(TextureHandle input) { + CheckTransientTexture(input); + // TODO: Manage resource "version" for debugging purpose - m_RenderPass.textureWriteList.Add(input); - m_Resources.UpdateTextureFirstWrite(input, m_RenderPass.index); + m_RenderPass.AddTextureWrite(input); 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, 0, m_RenderPass.index); + m_RenderPass.AddTransientTexture(result); + return result; + } + + /// + /// Create a new Render Graph Texture resource using the descriptor from another texture. + /// + /// Texture from which the descriptor should be used. + /// A new transient TextureHandle. + public TextureHandle CreateTransientTexture(TextureHandle texture) + { + var desc = m_Resources.GetTextureResourceDesc(texture); + var result = m_Resources.CreateTexture(desc, 0, m_RenderPass.index); + m_RenderPass.AddTransientTexture(result); + return result; + } + /// /// Specify a Renderer List resource to use during the pass. /// @@ -75,7 +102,7 @@ public TextureHandle WriteTexture(TextureHandle input) /// An updated resource handle to the input resource. public RendererListHandle UseRendererList(RendererListHandle input) { - m_RenderPass.usedRendererListList.Add(input); + m_RenderPass.UseRendererList(input); return input; } @@ -86,7 +113,7 @@ public RendererListHandle UseRendererList(RendererListHandle input) /// An updated resource handle to the input resource. public ComputeBufferHandle ReadComputeBuffer(ComputeBufferHandle input) { - m_RenderPass.bufferReadList.Add(input); + m_RenderPass.AddBufferRead(input); return input; } @@ -97,7 +124,7 @@ public ComputeBufferHandle ReadComputeBuffer(ComputeBufferHandle input) /// An updated resource handle to the input resource. public ComputeBufferHandle WriteComputeBuffer(ComputeBufferHandle input) { - m_RenderPass.bufferWriteList.Add(input); + m_RenderPass.AddBufferWrite(input); return input; } @@ -109,7 +136,7 @@ public ComputeBufferHandle WriteComputeBuffer(ComputeBufferHandle input) /// Render function for the pass. public void SetRenderFunc(RenderFunc renderFunc) where PassData : class, new() { - ((RenderGraph.RenderPass)m_RenderPass).renderFunc = renderFunc; + ((RenderGraphPass)m_RenderPass).renderFunc = renderFunc; } /// @@ -118,7 +145,7 @@ public ComputeBufferHandle WriteComputeBuffer(ComputeBufferHandle input) /// Set to true to enable asynchronous compute. public void EnableAsyncCompute(bool value) { - m_RenderPass.enableAsyncCompute = value; + m_RenderPass.EnableAsyncCompute(value); } /// @@ -131,7 +158,7 @@ public void Dispose() #endregion #region Internal Interface - internal RenderGraphBuilder(RenderGraph.RenderPass renderPass, RenderGraphResourceRegistry resources) + internal RenderGraphBuilder(RenderGraphPass renderPass, RenderGraphResourceRegistry resources) { m_RenderPass = renderPass; m_Resources = resources; @@ -145,6 +172,14 @@ void Dispose(bool disposing) m_Disposed = true; } + + void CheckTransientTexture(TextureHandle input) + { + if (input.transientPassIndex != -1 && input.transientPassIndex != m_RenderPass.index) + { + throw new ArgumentException($"Trying to use a transient texture (pass index {input.transientPassIndex}) in a different pass (pass index {m_RenderPass.index}."); + } + } #endregion } } diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphLogger.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphLogger.cs index b41e1fcfe29..df946c91743 100644 --- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphLogger.cs +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphLogger.cs @@ -25,10 +25,12 @@ public void Dispose() void Dispose(bool disposing) { + Debug.Assert(m_Logger != null, "RenderGraphLogIndent: logger parameter should not be null."); + if (m_Disposed) return; - if (disposing) + if (disposing && m_Logger != null) { m_Logger.DecrementIndentation(m_Indentation); } diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs new file mode 100644 index 00000000000..7a3c5b5ca67 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs @@ -0,0 +1,155 @@ +using System; +using System.Diagnostics; +using System.Collections.Generic; +using UnityEngine.Rendering; + +namespace UnityEngine.Experimental.Rendering.RenderGraphModule +{ + [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")] + abstract class RenderGraphPass + { + public RenderFunc GetExecuteDelegate() + where PassData : class, new() => ((RenderGraphPass)this).renderFunc; + + public abstract void Execute(RenderGraphContext renderGraphContext); + public abstract void Release(RenderGraphContext renderGraphContext); + public abstract bool HasRenderFunc(); + + public string name { get; protected set; } + public int index { get; protected set; } + public ProfilingSampler customSampler { get; protected set; } + public bool enableAsyncCompute { get; protected set; } + + public TextureHandle depthBuffer { get; protected set; } + public TextureHandle[] colorBuffers { get; protected set; } = new TextureHandle[RenderGraph.kMaxMRTCount]; + public int colorBufferMaxIndex { get; protected set; } = -1; + public int refCount { get; protected set; } + + List m_TextureReadList = new List(); + List m_TextureWriteList = new List(); + List m_TransientTextureList = new List(); + List m_BufferReadList = new List(); + List m_BufferWriteList = new List(); + List m_UsedRendererListList = new List(); + + public IReadOnlyCollection textureReadList { get { return m_TextureReadList; } } + public IReadOnlyCollection textureWriteList { get { return m_TextureWriteList; } } + public IReadOnlyCollection transientTextureList { get { return m_TransientTextureList; } } + public IReadOnlyCollection bufferReadList { get { return m_BufferReadList; } } + public IReadOnlyCollection bufferWriteList { get { return m_BufferWriteList; } } + public IReadOnlyCollection usedRendererListList { get { return m_UsedRendererListList; } } + + public void Clear() + { + name = ""; + index = -1; + customSampler = null; + m_TextureReadList.Clear(); + m_TextureWriteList.Clear(); + m_BufferReadList.Clear(); + m_BufferWriteList.Clear(); + m_TransientTextureList.Clear(); + m_UsedRendererListList.Clear(); + enableAsyncCompute = false; + refCount = 0; + + // Invalidate everything + colorBufferMaxIndex = -1; + depthBuffer = new TextureHandle(); + for (int i = 0; i < RenderGraph.kMaxMRTCount; ++i) + { + colorBuffers[i] = new TextureHandle(); + } + } + + public void AddTextureWrite(TextureHandle texture) + { + m_TextureWriteList.Add(texture); + refCount++; + } + + public void AddTextureRead(TextureHandle texture) + { + m_TextureReadList.Add(texture); + } + + public void AddBufferWrite(ComputeBufferHandle buffer) + { + m_BufferWriteList.Add(buffer); + refCount++; + } + + public void AddTransientTexture(TextureHandle texture) + { + m_TransientTextureList.Add(texture); + } + + public void AddBufferRead(ComputeBufferHandle buffer) + { + m_BufferReadList.Add(buffer); + } + + public void UseRendererList(RendererListHandle rendererList) + { + m_UsedRendererListList.Add(rendererList); + } + + public void EnableAsyncCompute(bool value) + { + enableAsyncCompute = value; + } + + public void SetColorBuffer(TextureHandle resource, int index) + { + Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0); + colorBufferMaxIndex = Math.Max(colorBufferMaxIndex, index); + colorBuffers[index] = resource; + AddTextureWrite(resource); + } + + public void SetDepthBuffer(TextureHandle resource, DepthAccess flags) + { + depthBuffer = resource; + if ((flags | DepthAccess.Read) != 0) + AddTextureRead(resource); + if ((flags | DepthAccess.Write) != 0) + AddTextureWrite(resource); + } + } + + [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")] + internal sealed class RenderGraphPass : RenderGraphPass + where PassData : class, new() + { + internal PassData data; + internal RenderFunc renderFunc; + + public override void Execute(RenderGraphContext renderGraphContext) + { + GetExecuteDelegate()(data, renderGraphContext); + } + + public void Initialize(int passIndex, PassData passData, string passName, ProfilingSampler sampler) + { + Clear(); + index = passIndex; + data = passData; + name = passName; + customSampler = sampler; + } + + public override void Release(RenderGraphContext renderGraphContext) + { + Clear(); + renderGraphContext.renderGraphPool.Release(data); + data = null; + renderFunc = null; + renderGraphContext.renderGraphPool.Release(this); + } + + public override bool HasRenderFunc() + { + return renderFunc != null; + } + } +} diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs.meta b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs.meta new file mode 100644 index 00000000000..9f00928f05e --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 377a2b96156b1344eaf58df67e35de17 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs index 3b8a66efc34..c186e92c47f 100644 --- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs @@ -18,7 +18,8 @@ public struct TextureHandle { bool m_IsValid; internal int handle { get; private set; } - internal TextureHandle(int handle) { this.handle = handle; m_IsValid = true; } + internal int transientPassIndex { get; private set; } + internal TextureHandle(int handle, int transientPassIndex = -1) { this.handle = handle; m_IsValid = true; this.transientPassIndex = transientPassIndex; } /// /// Conversion to int. /// @@ -30,6 +31,7 @@ public struct TextureHandle /// /// True if the handle is valid. public bool IsValid() => m_IsValid; + } /// @@ -292,8 +294,6 @@ internal struct TextureResource public bool imported; public RTHandle rt; public int cachedHash; - public int firstWritePassIndex; - public int lastReadPassIndex; public int shaderProperty; public bool wasReleased; @@ -321,8 +321,6 @@ void Reset() imported = false; rt = null; cachedHash = -1; - firstWritePassIndex = int.MaxValue; - lastReadPassIndex = -1; wasReleased = false; } } @@ -441,6 +439,11 @@ internal TextureHandle ImportTexture(RTHandle rt, int shaderProperty = 0) return new TextureHandle(newHandle); } + internal bool IsTextureImported(TextureHandle handle) + { + return handle.IsValid() ? GetTextureResource(handle).imported : false; + } + internal TextureHandle ImportBackbuffer(RenderTargetIdentifier rt) { if (m_CurrentBackbuffer != null) @@ -452,36 +455,17 @@ internal TextureHandle ImportBackbuffer(RenderTargetIdentifier rt) return new TextureHandle(newHandle); } - internal TextureHandle CreateTexture(in TextureDesc desc, int shaderProperty = 0) + internal TextureHandle CreateTexture(in TextureDesc desc, int shaderProperty = 0, int transientPassIndex = -1) { ValidateTextureDesc(desc); int newHandle = m_TextureResources.Add(new TextureResource(desc, shaderProperty)); - return new TextureHandle(newHandle); - } - - internal void UpdateTextureFirstWrite(TextureHandle tex, int passIndex) - { - ref var res = ref GetTextureResource(tex); - res.firstWritePassIndex = Math.Min(passIndex, res.firstWritePassIndex); - - //// We increment lastRead index here so that a resource used only for a single pass can be released at the end of said pass. - //// This will also keep the resource alive as long as it is written to. - //// Typical example is a depth buffer that may never be explicitly read from but is necessary all along - /// - // PROBLEM: Increasing last read on write operation will keep the target alive even if it's not used at all so it's not good. - // If we don't do it though, it means that client code cannot write "by default" into a target as it will try to write to an already released target. - // Example: - // DepthPrepass: Writes to Depth and Normal buffers (pass will create normal buffer) - // ObjectMotion: Writes to MotionVectors and Normal => Exception because NormalBuffer is already released as it not used. - // => Solution includes : Shader Combination (without MRT for example) / Dummy Targets - //res.lastReadPassIndex = Math.Max(passIndex, res.lastReadPassIndex); + return new TextureHandle(newHandle, transientPassIndex); } - internal void UpdateTextureLastRead(TextureHandle tex, int passIndex) + internal int GetTextureResourceCount() { - ref var res = ref GetTextureResource(tex); - res.lastReadPassIndex = Math.Max(passIndex, res.lastReadPassIndex); + return m_TextureResources.size; } ref TextureResource GetTextureResource(TextureHandle res) @@ -508,29 +492,36 @@ internal ComputeBufferHandle ImportComputeBuffer(ComputeBuffer computeBuffer) return new ComputeBufferHandle(newHandle); } - internal void CreateAndClearTexturesForPass(RenderGraphContext rgContext, int passIndex, List textures) + internal int GetComputeBufferResourceCount() + { + return m_ComputeBufferResources.size; + } + + internal ref ComputeBufferResource GetComputeBufferResource(ComputeBufferHandle res) { - foreach (var rgResource in textures) + return ref m_ComputeBufferResources[res]; + } + + internal void CreateAndClearTexture(RenderGraphContext rgContext, TextureHandle texture) + { + ref var resource = ref GetTextureResource(texture); + if (!resource.imported) { - ref var resource = ref GetTextureResource(rgResource); - if (!resource.imported && resource.firstWritePassIndex == passIndex) - { - CreateTextureForPass(ref resource); + CreateTextureForPass(ref resource); - if (resource.desc.clearBuffer || m_RenderGraphDebug.clearRenderTargetsAtCreation) + if (resource.desc.clearBuffer || m_RenderGraphDebug.clearRenderTargetsAtCreation) + { + bool debugClear = m_RenderGraphDebug.clearRenderTargetsAtCreation && !resource.desc.clearBuffer; + var name = debugClear ? "RenderGraph: Clear Buffer (Debug)" : "RenderGraph: Clear Buffer"; + using (new ProfilingScope(rgContext.cmd, ProfilingSampler.Get(RenderGraphProfileId.RenderGraphClear))) { - bool debugClear = m_RenderGraphDebug.clearRenderTargetsAtCreation && !resource.desc.clearBuffer; - var name = debugClear ? "RenderGraph: Clear Buffer (Debug)" : "RenderGraph: Clear Buffer"; - using (new ProfilingScope(rgContext.cmd, ProfilingSampler.Get(RenderGraphProfileId.RenderGraphClear))) - { - var clearFlag = resource.desc.depthBufferBits != DepthBits.None ? ClearFlag.Depth : ClearFlag.Color; - var clearColor = debugClear ? Color.magenta : resource.desc.clearColor; - CoreUtils.SetRenderTarget(rgContext.cmd, resource.rt, clearFlag, clearColor); - } + var clearFlag = resource.desc.depthBufferBits != DepthBits.None ? ClearFlag.Depth : ClearFlag.Color; + var clearColor = debugClear ? Color.magenta : resource.desc.clearColor; + CoreUtils.SetRenderTarget(rgContext.cmd, resource.rt, clearFlag, clearColor); } - - LogTextureCreation(resource.rt, resource.desc.clearBuffer || m_RenderGraphDebug.clearRenderTargetsAtCreation); } + + LogTextureCreation(resource.rt, resource.desc.clearBuffer || m_RenderGraphDebug.clearRenderTargetsAtCreation); } } @@ -540,7 +531,7 @@ void CreateTextureForPass(ref TextureResource resource) int hashCode = desc.GetHashCode(); if(resource.rt != null) - throw new InvalidOperationException(string.Format("Trying to create an already created texture ({0}). Texture was probably declared for writing more than once.", resource.desc.name)); + throw new InvalidOperationException(string.Format("Trying to create an already created texture ({0}). Texture was probably declared for writing more than once in the same pass.", resource.desc.name)); resource.rt = null; if (!TryGetRenderTarget(hashCode, out resource.rt)) @@ -578,7 +569,7 @@ void CreateTextureForPass(ref TextureResource resource) resource.cachedHash = hashCode; } - void SetGlobalTextures(RenderGraphContext rgContext, List textures, bool bindDummyTexture) + void SetGlobalTextures(RenderGraphContext rgContext, IReadOnlyCollection textures, bool bindDummyTexture) { foreach (var resource in textures) { @@ -595,62 +586,35 @@ void SetGlobalTextures(RenderGraphContext rgContext, List texture } - internal void PreRenderPassSetGlobalTextures(RenderGraphContext rgContext, List textures) + internal void PreRenderPassSetGlobalTextures(RenderGraphContext rgContext, IReadOnlyCollection textures) { SetGlobalTextures(rgContext, textures, false); } - internal void PostRenderPassUnbindGlobalTextures(RenderGraphContext rgContext, List textures) + internal void PostRenderPassUnbindGlobalTextures(RenderGraphContext rgContext, IReadOnlyCollection textures) { SetGlobalTextures(rgContext, textures, true); } - internal void ReleaseTexturesForPass(RenderGraphContext rgContext, int passIndex, List readTextures, List writtenTextures) + internal void ReleaseTexture(RenderGraphContext rgContext, TextureHandle resource) { - foreach (var resource in readTextures) + ref var resourceDesc = ref GetTextureResource(resource); + if (!resourceDesc.imported) { - ref var resourceDesc = ref GetTextureResource(resource); - if (!resourceDesc.imported && resourceDesc.lastReadPassIndex == passIndex) + if (m_RenderGraphDebug.clearRenderTargetsAtRelease) { - if (m_RenderGraphDebug.clearRenderTargetsAtRelease) + using (new ProfilingScope(rgContext.cmd, ProfilingSampler.Get(RenderGraphProfileId.RenderGraphClearDebug))) { - using (new ProfilingScope(rgContext.cmd, ProfilingSampler.Get(RenderGraphProfileId.RenderGraphClearDebug))) - { - var clearFlag = resourceDesc.desc.depthBufferBits != DepthBits.None ? ClearFlag.Depth : ClearFlag.Color; - CoreUtils.SetRenderTarget(rgContext.cmd, GetTexture(resource), clearFlag, Color.magenta); - } + var clearFlag = resourceDesc.desc.depthBufferBits != DepthBits.None ? ClearFlag.Depth : ClearFlag.Color; + CoreUtils.SetRenderTarget(rgContext.cmd, GetTexture(resource), clearFlag, Color.magenta); } - - ReleaseTextureForPass(resource); - } - } - - // If a resource was created for only a single pass, we don't want users to have to declare explicitly the read operation. - // So to do that, we also update lastReadIndex on resource writes. - // This means that we need to check written resources for destruction too - foreach (var resource in writtenTextures) - { - ref var resourceDesc = ref GetTextureResource(resource); - // <= because a texture that is only declared as written in a single pass (and read implicitly in the same pass) will have the default lastReadPassIndex at -1 - if (!resourceDesc.imported && resourceDesc.lastReadPassIndex <= passIndex) - { - ReleaseTextureForPass(resource); } - } - } - - void ReleaseTextureForPass(TextureHandle res) - { - ref var resource = ref m_TextureResources[res]; - // This can happen because we release texture in two passes (see ReleaseTexturesForPass) and texture can be present in both passes - if (resource.rt != null) - { - LogTextureRelease(resource.rt); - ReleaseTextureResource(resource.cachedHash, resource.rt); - resource.cachedHash = -1; - resource.rt = null; - resource.wasReleased = true; + LogTextureRelease(resourceDesc.rt); + ReleaseTextureResource(resourceDesc.cachedHash, resourceDesc.rt); + resourceDesc.cachedHash = -1; + resourceDesc.rt = null; + resourceDesc.wasReleased = true; } } @@ -761,12 +725,16 @@ internal void Clear() #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_AllocatedTextures.Count != 0) { - Debug.LogWarning("RenderGraph: Not all textures were released."); + string logMessage = "RenderGraph: Not all textures were released."; + List<(int, RTHandle)> tempList = new List<(int, RTHandle)>(m_AllocatedTextures); foreach (var value in tempList) { + logMessage = $"{logMessage}\n\t{value.Item2.name}"; ReleaseTextureResource(value.Item1, value.Item2); } + + Debug.LogWarning(logMessage); } #endif } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.RenderGraph.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.RenderGraph.cs index 926a23f2f84..49bd699a153 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.RenderGraph.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.RenderGraph.cs @@ -30,7 +30,7 @@ public TextureHandle Render(RenderGraph renderGraph, HDCamera hdCamera, TextureH historyRT.referenceSize.y * historyRT.scaleFactor.y); var rtScaleForHistory = hdCamera.historyRTHandleProperties.rtHandleScale; - var aoParameters = PrepareRenderAOParameters(hdCamera, renderGraph.rtHandleProperties, historySize * rtScaleForHistory, frameCount); + var aoParameters = PrepareRenderAOParameters(hdCamera, historySize * rtScaleForHistory, frameCount); var packedData = RenderAO(renderGraph, aoParameters, depthPyramid); result = DenoiseAO(renderGraph, aoParameters, motionVectors, packedData, currentHistory, outputHistory); @@ -102,8 +102,8 @@ TextureHandle DenoiseAO( RenderGraph renderGraph, passData.parameters = parameters; passData.packedData = builder.ReadTexture(aoPackedData); passData.motionVectors = builder.ReadTexture(motionVectors); - passData.packedDataBlurred = builder.WriteTexture(renderGraph.CreateTexture( - new TextureDesc(Vector2.one * scaleFactor, true, true) { colorFormat = GraphicsFormat.R32_UInt, enableRandomWrite = true, name = "AO Packed blurred data" } )); + passData.packedDataBlurred = builder.CreateTransientTexture( + new TextureDesc(Vector2.one * scaleFactor, true, true) { colorFormat = GraphicsFormat.R32_UInt, enableRandomWrite = true, name = "AO Packed blurred data" } ); passData.currentHistory = builder.ReadTexture(currentHistory); // can also be written on first frame, but since it's an imported resource, it doesn't matter in term of lifetime. passData.outputHistory = builder.WriteTexture(outputHistory); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.cs index 25273975777..74170876cb9 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.cs @@ -300,7 +300,7 @@ struct RenderAOParameters public ShaderVariablesAmbientOcclusion cb; } - RenderAOParameters PrepareRenderAOParameters(HDCamera camera, RTHandleProperties rtHandleProperties, Vector2 historySize, int frameCount) + RenderAOParameters PrepareRenderAOParameters(HDCamera camera, Vector2 historySize, int frameCount) { var parameters = new RenderAOParameters(); @@ -567,7 +567,7 @@ internal void Dispatch(CommandBuffer cmd, HDCamera camera, int frameCount) currentHistory.referenceSize.y * currentHistory.scaleFactor.y); var rtScaleForHistory = camera.historyRTHandleProperties.rtHandleScale; - var aoParameters = PrepareRenderAOParameters(camera, RTHandles.rtHandleProperties, historySize * rtScaleForHistory, frameCount); + var aoParameters = PrepareRenderAOParameters(camera, historySize * rtScaleForHistory, frameCount); using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.HorizonSSAO))) { RenderAO(aoParameters, m_PackedDataTex, m_Resources, cmd); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricLighting.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricLighting.cs index eaafd5edd74..7c3280d4d4f 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricLighting.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricLighting.cs @@ -467,7 +467,7 @@ internal void DestroyVolumetricLightingBuffers() CoreUtils.SafeRelease(m_VisibleVolumeDataBuffer); CoreUtils.SafeRelease(m_VisibleVolumeBoundsBuffer); - m_VisibleVolumeData = null; // free() + m_VisibleVolumeData = null; // free() m_VisibleVolumeBounds = null; // free() } @@ -493,7 +493,7 @@ internal void ResizeVolumetricLightingBuffers(HDCamera hdCamera, int frameIndex) var currentParams = hdCamera.vBufferParams[currIdx]; - ResizeVolumetricBuffer(ref m_DensityBuffer, "VBufferDensity", currentParams.viewportSize.x, + ResizeVolumetricBuffer(ref m_DensityBuffer, "VBufferDensity", currentParams.viewportSize.x, currentParams.viewportSize.y, currentParams.viewportSize.z); ResizeVolumetricBuffer(ref m_LightingBuffer, "VBufferLighting", currentParams.viewportSize.x, diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.RenderGraph.cs b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.RenderGraph.cs index 5b55edea50a..692f3918445 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.RenderGraph.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.RenderGraph.cs @@ -5,9 +5,9 @@ namespace UnityEngine.Rendering.HighDefinition { partial class PostProcessSystem { - public void Render( RenderGraph renderGraph, - HDCamera hdCamera, - BlueNoise blueNoise, + public void Render( RenderGraph renderGraph, + HDCamera hdCamera, + BlueNoise blueNoise, TextureHandle colorBuffer, TextureHandle afterPostProcessTexture, TextureHandle depthBuffer, diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs index af115aa692b..24985b52709 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs @@ -188,6 +188,15 @@ public PostProcessSystem(HDRenderPipelineAsset hdAsset, RenderPipelineResources m_KeepAlpha = hdAsset.currentPlatformRenderPipelineSettings.supportsAlpha; } + // Setup a default exposure textures and clear it to neutral values so that the exposure + // multiplier is 1 and thus has no effect + // Beware that 0 in EV100 maps to a multiplier of 0.833 so the EV100 value in this + // neutral exposure texture isn't 0 + m_EmptyExposureTexture = RTHandles.Alloc(1, 1, colorFormat: k_ExposureFormat, + enableRandomWrite: true, name: "Empty EV100 Exposure"); + + FillEmptyExposureTexture(); + // Call after initializing m_LutSize and m_KeepAlpha as it's needed for render target allocation. InitializeNonRenderGraphResources(hdAsset); } @@ -196,6 +205,9 @@ public void Cleanup() { CleanupNonRenderGraphResources(); + RTHandles.Release(m_EmptyExposureTexture); + m_EmptyExposureTexture = null; + CoreUtils.Destroy(m_ExposureCurveTexture); CoreUtils.Destroy(m_InternalSpectralLut); CoreUtils.Destroy(m_FinalPassMaterial); @@ -247,16 +259,6 @@ public void InitializeNonRenderGraphResources(HDRenderPipelineAsset hdAsset) enableRandomWrite: true ); - // Setup a default exposure textures and clear it to neutral values so that the exposure - // multiplier is 1 and thus has no effect - // Beware that 0 in EV100 maps to a multiplier of 0.833 so the EV100 value in this - // neutral exposure texture isn't 0 - m_EmptyExposureTexture = RTHandles.Alloc(1, 1, colorFormat: k_ExposureFormat, - enableRandomWrite: true, name: "Empty EV100 Exposure" - ); - - FillEmptyExposureTexture(); - // Misc targets m_TempTexture1024 = RTHandles.Alloc( 1024, 1024, colorFormat: GraphicsFormat.R16G16_SFloat, @@ -283,30 +285,29 @@ public void CleanupNonRenderGraphResources() m_Pool.Cleanup(); - RTHandles.Release(m_EmptyExposureTexture); RTHandles.Release(m_TempTexture1024); RTHandles.Release(m_TempTexture32); RTHandles.Release(m_AlphaTexture); RTHandles.Release(m_InternalLogLut); - m_EmptyExposureTexture = null; m_TempTexture1024 = null; m_TempTexture32 = null; m_AlphaTexture = null; m_InternalLogLut = null; } + // In some cases, the internal buffer of render textures might be invalid. // Usually when using these textures with API such as SetRenderTarget, they are recreated internally. // This is not the case when these textures are used exclusively with Compute Shaders. So to make sure they work in this case, we recreate them here. void CheckRenderTexturesValidity() { - if (!m_NonRenderGraphResourcesAvailable) - return; - if (!m_EmptyExposureTexture.rt.IsCreated()) FillEmptyExposureTexture(); + if (!m_NonRenderGraphResourcesAvailable) + return; + HDUtils.CheckRTCreated(m_InternalLogLut.rt); HDUtils.CheckRTCreated(m_TempTexture1024.rt); HDUtils.CheckRTCreated(m_TempTexture32.rt); diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs index 81881ecbf12..e77c0825e69 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs @@ -746,8 +746,8 @@ internal void ExecuteCaptureActions(RenderGraph renderGraph, TextureHandle input passData.input = builder.ReadTexture(input); // We need to blit to an intermediate texture because input resolution can be bigger than the camera resolution // Since recorder does not know about this, we need to send a texture of the right size. - passData.tempTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(actualWidth, actualHeight) - { colorFormat = inputDesc.colorFormat, name = "TempCaptureActions" })); + passData.tempTexture = builder.CreateTransientTexture(new TextureDesc(actualWidth, actualHeight) + { colorFormat = inputDesc.colorFormat, name = "TempCaptureActions" }); builder.SetRenderFunc( (ExecuteCaptureActionsPassData data, RenderGraphContext ctx) => diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs index 8eda7f371ca..fe353879da5 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs @@ -137,12 +137,12 @@ static void RenderLightVolumes(RenderGraph renderGraph, in DebugParameters debug using (var builder = renderGraph.AddRenderPass("LightVolumes", out var passData)) { passData.parameters = s_lightVolumes.PrepareLightVolumeParameters(debugParameters.hdCamera, debugParameters.debugDisplaySettings.data.lightingDebugSettings, cullResults); - passData.lightCountBuffer = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) - { colorFormat= GraphicsFormat.R32_SFloat, clearBuffer = true, clearColor = Color.black, name = "LightVolumeCount" })); - passData.colorAccumulationBuffer = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) - { colorFormat = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.black, name = "LightVolumeColorAccumulation" })); - passData.debugLightVolumesTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) - { colorFormat = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.black, enableRandomWrite = true, name = "LightVolumeDebugLightVolumesTexture" })); + passData.lightCountBuffer = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true) + { colorFormat= GraphicsFormat.R32_SFloat, clearBuffer = true, clearColor = Color.black, name = "LightVolumeCount" }); + passData.colorAccumulationBuffer = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true) + { colorFormat = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.black, name = "LightVolumeColorAccumulation" }); + passData.debugLightVolumesTexture = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true) + { colorFormat = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.black, enableRandomWrite = true, name = "LightVolumeDebugLightVolumesTexture" }); passData.depthBuffer = builder.UseDepthBuffer(depthBuffer, DepthAccess.ReadWrite); passData.destination = builder.WriteTexture(destination); diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs index caa18d08333..b17034843db 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs @@ -235,7 +235,7 @@ LightingOutput RenderDeferredLighting( RenderGraph renderGraph, // TODO RENDERGRAPH: Check how to avoid this kind of pattern. // Unfortunately, the low level needs this texture to always be bound with UAV enabled, so in order to avoid effectively creating the full resolution texture here, // we need to create a small dummy texture. - passData.sssDiffuseLightingBuffer = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(1, 1, true, true) { colorFormat = GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite = true } )); + passData.sssDiffuseLightingBuffer = builder.CreateTransientTexture(new TextureDesc(1, 1, true, true) { colorFormat = GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite = true } ); } passData.depthBuffer = builder.ReadTexture(depthStencilBuffer); passData.depthTexture = builder.ReadTexture(depthPyramidTexture); @@ -274,7 +274,7 @@ LightingOutput RenderDeferredLighting( RenderGraph renderGraph, resources.tileListBuffer = context.resources.GetComputeBuffer(data.tileListBuffer); resources.dispatchIndirectBuffer = context.resources.GetComputeBuffer(data.dispatchIndirectBuffer); - // RENDERGRAPH TODO: try to find a better way to bind this. + // TODO RENDERGRAPH: try to find a better way to bind this. // Issue is that some GBuffers have several names (for example normal buffer is both NormalBuffer and GBuffer1) // So it's not possible to use auto binding via dependency to shaderTagID // Should probably get rid of auto binding and go explicit all the way (might need to wait for us to remove non rendergraph code path). @@ -362,8 +362,8 @@ TextureHandle RenderSSR( RenderGraph renderGraph, // In practice, these textures are sparse (mostly black). Therefore, clearing them is fast (due to CMASK), // and much faster than fully overwriting them from within SSR shaders. - passData.hitPointsTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) - { colorFormat = GraphicsFormat.R16G16_UNorm, clearBuffer = true, clearColor = Color.clear, enableRandomWrite = true, name = "SSR_Hit_Point_Texture" })); + passData.hitPointsTexture = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true) + { colorFormat = GraphicsFormat.R16G16_UNorm, clearBuffer = true, clearColor = Color.clear, enableRandomWrite = true, name = "SSR_Hit_Point_Texture" }); passData.lightingTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) { colorFormat = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.clear, enableRandomWrite = true, name = "SSR_Lighting_Texture" }, HDShaderIDs._SsrLightingTexture)); //passData.hitPointsTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs index ffb4c42da01..c6bafbc3155 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs @@ -492,7 +492,7 @@ class ResolveStencilPassData public BuildCoarseStencilAndResolveParameters parameters; public TextureHandle inputDepth; public TextureHandle resolvedStencil; - public ComputeBuffer coarseStencilBuffer; + public ComputeBufferHandle coarseStencilBuffer; } void BuildCoarseStencilAndResolveIfNeeded(RenderGraph renderGraph, HDCamera hdCamera, ref PrepassOutput output) @@ -501,8 +501,9 @@ void BuildCoarseStencilAndResolveIfNeeded(RenderGraph renderGraph, HDCamera hdCa { passData.parameters = PrepareBuildCoarseStencilParameters(hdCamera); passData.inputDepth = output.depthBuffer; - passData.coarseStencilBuffer = m_SharedRTManager.GetCoarseStencilBuffer(); - passData.resolvedStencil = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) { colorFormat = GraphicsFormat.R8G8_UInt, enableRandomWrite = true, name = "StencilBufferResolved" })); + passData.coarseStencilBuffer = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_SharedRTManager.GetCoarseStencilBuffer())); + if (passData.parameters.resolveIsNecessary) + passData.resolvedStencil = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) { colorFormat = GraphicsFormat.R8G8_UInt, enableRandomWrite = true, name = "StencilBufferResolved" })); builder.SetRenderFunc( (ResolveStencilPassData data, RenderGraphContext context) => { @@ -510,7 +511,7 @@ void BuildCoarseStencilAndResolveIfNeeded(RenderGraph renderGraph, HDCamera hdCa BuildCoarseStencilAndResolveIfNeeded(data.parameters, res.GetTexture(data.inputDepth), res.GetTexture(data.resolvedStencil), - data.coarseStencilBuffer, + res.GetComputeBuffer(data.coarseStencilBuffer), context.cmd); } ); diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs index 74384efdf78..2c0c9a1b3fd 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs @@ -211,14 +211,14 @@ void ExecuteWithRenderGraph( RenderRequest renderRequest, hdCamera.ExecuteCaptureActions(m_RenderGraph, colorBuffer); postProcessDest = RenderDebug( m_RenderGraph, - hdCamera, + hdCamera, postProcessDest, - prepassOutput.depthBuffer, - prepassOutput.depthPyramidTexture, - m_DebugFullScreenTexture, - colorPickerTexture, - shadowResult, - cullingResults); + prepassOutput.depthBuffer, + prepassOutput.depthPyramidTexture, + m_DebugFullScreenTexture, + colorPickerTexture, + shadowResult, + cullingResults); BlitFinalCameraTexture(m_RenderGraph, hdCamera, postProcessDest, backBuffer, prepassOutput.resolvedMotionVectorsBuffer, prepassOutput.resolvedNormalBuffer); @@ -459,10 +459,12 @@ void RenderForwardTransparent( RenderGraph renderGraph, bool renderMotionVecForTransparent = NeedMotionVectorForTransparent(hdCamera.frameSettings); - TextureHandle mrt1; + passData.renderTargetCount = 2; + passData.renderTarget[0] = builder.WriteTexture(colorBuffer); + if (renderMotionVecForTransparent) { - mrt1 = motionVectorBuffer; + passData.renderTarget[1] = builder.WriteTexture(motionVectorBuffer); // TODO RENDERGRAPH // WORKAROUND VELOCITY-MSAA // This is a workaround for velocity with MSAA. Currently motion vector resolve is not implemented with MSAA @@ -475,13 +477,9 @@ void RenderForwardTransparent( RenderGraph renderGraph, // It doesn't really matter what gets bound here since the color mask state set will prevent this from ever being written to. However, we still need to bind something // to avoid warnings about unbound render targets. The following rendertarget could really be anything if renderVelocitiesForTransparent // Create a new target here should reuse existing already released one - mrt1 = renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) { colorFormat = GraphicsFormat.R8G8B8A8_SRGB, name = "Transparency Velocity Dummy" }); + passData.renderTarget[1] = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true) { colorFormat = GraphicsFormat.R8G8B8A8_SRGB, name = "Transparency Velocity Dummy" }); } - passData.renderTargetCount = 2; - passData.renderTarget[0] = builder.WriteTexture(colorBuffer); - passData.renderTarget[1] = builder.WriteTexture(mrt1); - if (colorPyramid != null && hdCamera.frameSettings.IsEnabled(FrameSettingsField.Refraction) && !preRefractionPass) { builder.ReadTexture(colorPyramid.Value); @@ -783,16 +781,16 @@ void RenderForwardError(RenderGraph renderGraph, class RenderSkyPassData { - public VisualEnvironment visualEnvironment; - public Light sunLight; - public HDCamera hdCamera; + public VisualEnvironment visualEnvironment; + public Light sunLight; + public HDCamera hdCamera; public TextureHandle volumetricLighting; public TextureHandle colorBuffer; public TextureHandle depthStencilBuffer; public TextureHandle intermediateBuffer; - public DebugDisplaySettings debugDisplaySettings; - public SkyManager skyManager; - public int frameCount; + public DebugDisplaySettings debugDisplaySettings; + public SkyManager skyManager; + public int frameCount; } void RenderSky(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle colorBuffer, TextureHandle volumetricLighting, TextureHandle depthStencilBuffer, TextureHandle depthTexture) @@ -810,7 +808,7 @@ void RenderSky(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle colorBu passData.volumetricLighting = builder.ReadTexture(volumetricLighting); passData.colorBuffer = builder.WriteTexture(colorBuffer); passData.depthStencilBuffer = builder.WriteTexture(depthStencilBuffer); - passData.intermediateBuffer = builder.WriteTexture(renderGraph.CreateTexture(colorBuffer)); + passData.intermediateBuffer = builder.CreateTransientTexture(colorBuffer); passData.debugDisplaySettings = m_CurrentDebugDisplaySettings; passData.skyManager = m_SkyManager; passData.frameCount = m_FrameCount; @@ -928,8 +926,8 @@ class RenderDistortionPassData public Vector4 size; } - void RenderDistortion( RenderGraph renderGraph, - HDCamera hdCamera, + void RenderDistortion( RenderGraph renderGraph, + HDCamera hdCamera, TextureHandle colorBuffer, TextureHandle depthStencilBuffer, TextureHandle colorPyramidBuffer, diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.SubsurfaceScattering.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.SubsurfaceScattering.cs index 1830c6829c9..1204288ec02 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.SubsurfaceScattering.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.SubsurfaceScattering.cs @@ -53,9 +53,9 @@ void RenderSubsurfaceScattering(RenderGraph renderGraph, HDCamera hdCamera, Text passData.sssBuffer = builder.ReadTexture(lightingBuffers.sssBuffer); if (passData.parameters.needTemporaryBuffer) { - passData.cameraFilteringBuffer = builder.WriteTexture(renderGraph.CreateTexture( + passData.cameraFilteringBuffer = builder.CreateTransientTexture( new TextureDesc(Vector2.one, true, true) - { colorFormat = GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite = true, clearBuffer = true, clearColor = Color.clear, name = "SSSCameraFiltering" })); + { colorFormat = GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite = true, clearBuffer = true, clearColor = Color.clear, name = "SSSCameraFiltering" }); } builder.SetRenderFunc( diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs index 9c3b4df7b05..82f45408e04 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs @@ -1267,8 +1267,10 @@ void CopyDepthBufferIfNeeded(HDCamera hdCamera, CommandBuffer cmd) struct BuildCoarseStencilAndResolveParameters { - public HDCamera hdCamera; - public ComputeShader resolveStencilCS; + public HDCamera hdCamera; + public ComputeShader resolveStencilCS; + public int resolveKernel; + public bool resolveIsNecessary; } BuildCoarseStencilAndResolveParameters PrepareBuildCoarseStencilParameters(HDCamera hdCamera) @@ -1276,6 +1278,19 @@ BuildCoarseStencilAndResolveParameters PrepareBuildCoarseStencilParameters(HDCam var parameters = new BuildCoarseStencilAndResolveParameters(); parameters.hdCamera = hdCamera; parameters.resolveStencilCS = defaultResources.shaders.resolveStencilCS; + + bool MSAAEnabled = hdCamera.frameSettings.IsEnabled(FrameSettingsField.MSAA); + + // The following features require a copy of the stencil, if none are active, no need to do the resolve. + bool resolveIsNecessary = GetFeatureVariantsEnabled(hdCamera.frameSettings); + resolveIsNecessary = resolveIsNecessary || hdCamera.IsSSREnabled() + || hdCamera.IsTransparentSSREnabled(); + // We need the resolve only with msaa + parameters.resolveIsNecessary = resolveIsNecessary && MSAAEnabled; + + int kernel = SampleCountToPassIndex(MSAAEnabled ? hdCamera.msaaSamples : MSAASamples.None); + parameters.resolveKernel = parameters.resolveIsNecessary ? kernel + 3 : kernel; // We have a different variant if we need to resolve to non-MSAA stencil + return parameters; } @@ -1293,31 +1308,18 @@ static void BuildCoarseStencilAndResolveIfNeeded(BuildCoarseStencilAndResolvePar { using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.CoarseStencilGeneration))) { - var hdCamera = parameters.hdCamera; - bool MSAAEnabled = hdCamera.frameSettings.IsEnabled(FrameSettingsField.MSAA); - - // The following features require a copy of the stencil, if none are active, no need to do the resolve. - bool resolveIsNecessary = GetFeatureVariantsEnabled(hdCamera.frameSettings); - resolveIsNecessary = resolveIsNecessary || hdCamera.IsSSREnabled() - || hdCamera.IsTransparentSSREnabled(); - - // We need the resolve only with msaa - resolveIsNecessary = resolveIsNecessary && MSAAEnabled; - ComputeShader cs = parameters.resolveStencilCS; - int kernel = SampleCountToPassIndex(MSAAEnabled ? hdCamera.msaaSamples : MSAASamples.None); - kernel = resolveIsNecessary ? kernel + 3 : kernel; // We have a different variant if we need to resolve to non-MSAA stencil - cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._CoarseStencilBuffer, coarseStencilBuffer); - cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._StencilTexture, depthStencilBuffer, 0, RenderTextureSubElement.Stencil); + cmd.SetComputeBufferParam(cs, parameters.resolveKernel, HDShaderIDs._CoarseStencilBuffer, coarseStencilBuffer); + cmd.SetComputeTextureParam(cs, parameters.resolveKernel, HDShaderIDs._StencilTexture, depthStencilBuffer, 0, RenderTextureSubElement.Stencil); - if (resolveIsNecessary) + if (parameters.resolveIsNecessary) { - cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputStencilBuffer, resolvedStencilBuffer); + cmd.SetComputeTextureParam(cs, parameters.resolveKernel, HDShaderIDs._OutputStencilBuffer, resolvedStencilBuffer); } - int coarseStencilWidth = HDUtils.DivRoundUp(hdCamera.actualWidth, 8); - int coarseStencilHeight = HDUtils.DivRoundUp(hdCamera.actualHeight, 8); - cmd.DispatchCompute(cs, kernel, coarseStencilWidth, coarseStencilHeight, hdCamera.viewCount); + int coarseStencilWidth = HDUtils.DivRoundUp(parameters.hdCamera.actualWidth, 8); + int coarseStencilHeight = HDUtils.DivRoundUp(parameters.hdCamera.actualHeight, 8); + cmd.DispatchCompute(cs, parameters.resolveKernel, coarseStencilWidth, coarseStencilHeight, parameters.hdCamera.viewCount); } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Settings/FrameSettings.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Settings/FrameSettings.cs index 1a6d9f69243..f64263a22bd 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Settings/FrameSettings.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Settings/FrameSettings.cs @@ -315,7 +315,7 @@ public enum FrameSettingsField [FrameSettingsField(2, displayedName: "SS Ambient Occlusion", positiveDependencies: new[] { AsyncCompute }, tooltip: "When enabled, HDRP calculates screen space ambient occlusion asynchronously.")] SSAOAsync = 43, // TODO: Enable thing when the render graph will be the default renderer. - // [FrameSettingsField(2, displayedName: "Contact Shadows", positiveDependencies: new[] { AsyncCompute }, tooltip: "When enabled, HDRP calculates Contact Shadows asynchronously.")] + //[FrameSettingsField(2, displayedName: "Contact Shadows", positiveDependencies: new[] { AsyncCompute }, tooltip: "When enabled, HDRP calculates Contact Shadows asynchronously.")] /// When enabled, HDRP calculates Contact Shadows asynchronously. ContactShadowsAsync = 44, /// When enabled, HDRP calculates volumetric voxelization asynchronously. @@ -670,7 +670,7 @@ public int GetResolvedSssSampleBudget(HDRenderPipelineAsset hdrp) internal bool SSRRunsAsync() => SystemInfo.supportsAsyncCompute && bitDatas[(int)FrameSettingsField.AsyncCompute] && bitDatas[(int)FrameSettingsField.SSRAsync]; internal bool SSAORunsAsync() => SystemInfo.supportsAsyncCompute && bitDatas[(int)FrameSettingsField.AsyncCompute] && bitDatas[(int)FrameSettingsField.SSAOAsync]; // TODO: Re-enable this when the render graph will be used by default. - internal bool ContactShadowsRunAsync() => SystemInfo.supportsAsyncCompute && bitDatas[(int)FrameSettingsField.AsyncCompute] && /* bitDatas[(int)FrameSettingsField.ContactShadowsAsync] */ false; + internal bool ContactShadowsRunAsync() => SystemInfo.supportsAsyncCompute && bitDatas[(int)FrameSettingsField.AsyncCompute] && /*bitDatas[(int)FrameSettingsField.ContactShadowsAsync]*/ false; internal bool VolumeVoxelizationRunsAsync() => SystemInfo.supportsAsyncCompute && bitDatas[(int)FrameSettingsField.AsyncCompute] && bitDatas[(int)FrameSettingsField.VolumeVoxelizationsAsync]; /// Override a frameSettings according to a mask.