diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs index 7a7d2e8e094..53c4ca169f3 100644 --- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs @@ -120,43 +120,57 @@ public void Reset() [DebuggerDisplay("RenderPass: {pass.name} (Index:{pass.index} Async:{enableAsyncCompute})")] internal struct CompiledPassInfo { - 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 bool allowPassPruning { get { return pass.allowPassPruning; } } + public RenderGraphPass pass; + public List[] resourceCreateList; + public List[] resourceReleaseList; + 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 bool allowPassPruning { get { return pass.allowPassPruning; } } #if DEVELOPMENT_BUILD || UNITY_EDITOR // This members are only here to ease debugging. - public List debugTextureReads; - public List debugTextureWrites; + public List[] debugResourceReads; + public List[] debugResourceWrites; #endif public void Reset(RenderGraphPass pass) { this.pass = pass; - if (textureCreateList == null) + if (resourceCreateList == null) { - textureCreateList = new List(); - textureReleaseList = new List(); + resourceCreateList = new List[(int)RenderGraphResourceType.Count]; + resourceReleaseList = new List[(int)RenderGraphResourceType.Count]; + for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) + { + resourceCreateList[i] = new List(); + resourceReleaseList[i] = new List(); + } #if DEVELOPMENT_BUILD || UNITY_EDITOR - debugTextureReads = new List(); - debugTextureWrites = new List(); + debugResourceReads = new List[(int)RenderGraphResourceType.Count]; + debugResourceWrites = new List[(int)RenderGraphResourceType.Count]; + for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) + { + debugResourceReads[i] = new List(); + debugResourceWrites[i] = new List(); + } #endif } - textureCreateList.Clear(); - textureReleaseList.Clear(); + for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) + { + resourceCreateList[i].Clear(); + resourceReleaseList[i].Clear(); + } + refCount = 0; pruned = false; hasSideEffect = false; @@ -165,27 +179,29 @@ public void Reset(RenderGraphPass pass) needGraphicsFence = false; #if DEVELOPMENT_BUILD || UNITY_EDITOR - debugTextureReads.Clear(); - debugTextureWrites.Clear(); + for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) + { + debugResourceReads[i].Clear(); + debugResourceWrites[i].Clear(); + } #endif } } - RenderGraphResourceRegistry m_Resources; - RenderGraphObjectPool m_RenderGraphPool = new RenderGraphObjectPool(); - List m_RenderPasses = new List(64); - List m_RendererLists = new List(32); - RenderGraphDebugParams m_DebugParameters = new RenderGraphDebugParams(); - RenderGraphLogger m_Logger = new RenderGraphLogger(); - RenderGraphDefaultResources m_DefaultResources = new RenderGraphDefaultResources(); - Dictionary m_DefaultProfilingSamplers = new Dictionary(); - bool m_ExecutionExceptionWasRaised; + RenderGraphResourceRegistry m_Resources; + RenderGraphObjectPool m_RenderGraphPool = new RenderGraphObjectPool(); + List m_RenderPasses = new List(64); + List m_RendererLists = new List(32); + RenderGraphDebugParams m_DebugParameters = new RenderGraphDebugParams(); + RenderGraphLogger m_Logger = new RenderGraphLogger(); + RenderGraphDefaultResources m_DefaultResources = new RenderGraphDefaultResources(); + Dictionary m_DefaultProfilingSamplers = new Dictionary(); + bool m_ExecutionExceptionWasRaised; // Compiled Render Graph info. - DynamicArray m_CompiledTextureInfos = new DynamicArray(); - DynamicArray m_CompiledBufferInfos = new DynamicArray(); - DynamicArray m_CompiledPassInfos = new DynamicArray(); - Stack m_PruningStack = new Stack(); + DynamicArray[] m_CompiledResourcesInfos = new DynamicArray[(int)RenderGraphResourceType.Count]; + DynamicArray m_CompiledPassInfos = new DynamicArray(); + Stack m_PruningStack = new Stack(); #region Public Interface @@ -212,6 +228,11 @@ public RenderGraphDefaultResources defaultResources public RenderGraph(bool supportMSAA, MSAASamples initialSampleCount) { m_Resources = new RenderGraphResourceRegistry(supportMSAA, initialSampleCount, m_DebugParameters, m_Logger); + + for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) + { + m_CompiledResourcesInfos[i] = new DynamicArray(); + } } /// @@ -278,10 +299,8 @@ public TextureHandle ImportBackbuffer(RenderTargetIdentifier rt) /// Texture descriptor. /// Optional property that allows you to specify a Shader property name to use for automatic resource binding. /// A new TextureHandle. - public TextureHandle CreateTexture(TextureDesc desc, int shaderProperty = 0) + public TextureHandle CreateTexture(in TextureDesc desc, int shaderProperty = 0) { - if (m_DebugParameters.tagResourceNamesWithRG) - desc.name = string.Format("{0}_RenderGraph", desc.name); return m_Resources.CreateTexture(desc, shaderProperty); } @@ -293,20 +312,17 @@ public TextureHandle CreateTexture(TextureDesc desc, int shaderProperty = 0) /// A new TextureHandle. public TextureHandle CreateTexture(TextureHandle texture, int shaderProperty = 0) { - var desc = m_Resources.GetTextureResourceDesc(texture); - if (m_DebugParameters.tagResourceNamesWithRG) - desc.name = string.Format("{0}_RenderGraph", desc.name); - return m_Resources.CreateTexture(desc, shaderProperty); + return m_Resources.CreateTexture(m_Resources.GetTextureResourceDesc(texture.handle), shaderProperty); } /// /// Gets the descriptor of the specified Texture resource. /// - /// + /// Texture resource from which the descriptor is requested. /// The input texture descriptor. public TextureDesc GetTextureDesc(TextureHandle texture) { - return m_Resources.GetTextureResourceDesc(texture); + return m_Resources.GetTextureResourceDesc(texture.handle); } /// @@ -330,6 +346,36 @@ public ComputeBufferHandle ImportComputeBuffer(ComputeBuffer computeBuffer) return m_Resources.ImportComputeBuffer(computeBuffer); } + /// + /// Create a new Render Graph Compute Buffer resource. + /// + /// Compute Buffer descriptor. + /// A new ComputeBufferHandle. + public ComputeBufferHandle CreateComputeBuffer(in ComputeBufferDesc desc) + { + return m_Resources.CreateComputeBuffer(desc); + } + + /// + /// Create a new Render Graph Compute Buffer resource using the descriptor from another compute buffer. + /// + /// Compute Buffer from which the descriptor should be used. + /// A new ComputeBufferHandle. + public ComputeBufferHandle CreateComputeBuffer(in ComputeBufferHandle computeBuffer) + { + return m_Resources.CreateComputeBuffer(m_Resources.GetComputeBufferResourceDesc(computeBuffer.handle)); + } + + /// + /// Gets the descriptor of the specified Compute Buffer resource. + /// + /// Compute Buffer resource from which the descriptor is requested. + /// The input compute buffer descriptor. + public ComputeBufferDesc GetComputeBufferDesc(in ComputeBufferHandle computeBuffer) + { + return m_Resources.GetComputeBufferResourceDesc(computeBuffer.handle); + } + /// /// Add a new Render Pass to the current Render Graph. /// @@ -391,99 +437,88 @@ public void Execute(ScriptableRenderContext renderContext, CommandBuffer cmd, in } #endregion - #region Internal Interface + #region Private Interface + + // Internal for testing purpose only + internal DynamicArray GetCompiledPassInfos() { return m_CompiledPassInfos; } + private RenderGraph() { } + // Internal for testing purpose only internal void ClearCompiledGraph() { ClearRenderPasses(); m_Resources.Clear(m_ExecutionExceptionWasRaised); m_DefaultResources.Clear(); m_RendererLists.Clear(); - m_CompiledBufferInfos.Clear(); - m_CompiledTextureInfos.Clear(); + for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) + m_CompiledResourcesInfos[i].Clear(); m_CompiledPassInfos.Clear(); } + void InitResourceInfosData(DynamicArray resourceInfos, int count) + { + resourceInfos.Resize(count); + for (int i = 0; i < resourceInfos.size; ++i) + resourceInfos[i].Reset(); + } + 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(); + InitResourceInfosData(m_CompiledResourcesInfos[(int)RenderGraphResourceType.Texture], m_Resources.GetTextureResourceCount()); + InitResourceInfosData(m_CompiledResourcesInfos[(int)RenderGraphResourceType.ComputeBuffer], m_Resources.GetComputeBufferResourceCount()); + m_CompiledPassInfos.Resize(m_RenderPasses.Count); for (int i = 0; i < m_CompiledPassInfos.size; ++i) m_CompiledPassInfos[i].Reset(m_RenderPasses[i]); } - internal DynamicArray GetCompiledPassInfos() { return m_CompiledPassInfos; } - void CountReferences() { for (int passIndex = 0; passIndex < m_CompiledPassInfos.size; ++passIndex) { ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[passIndex]; - var textureRead = passInfo.pass.textureReadList; - foreach (TextureHandle texture in textureRead) + for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) { - ref CompiledResourceInfo info = ref m_CompiledTextureInfos[texture]; - info.consumers.Add(passIndex); - info.refCount++; + var resourceRead = passInfo.pass.resourceReadLists[type]; + foreach (var resource in resourceRead) + { + ref CompiledResourceInfo info = ref m_CompiledResourcesInfos[type][resource]; + info.consumers.Add(passIndex); + info.refCount++; #if DEVELOPMENT_BUILD || UNITY_EDITOR - passInfo.debugTextureReads.Add(m_Resources.GetTextureResourceDesc(texture).name); + passInfo.debugResourceReads[type].Add(m_Resources.GetResourceName(resource)); #endif - } + } - var textureWrite = passInfo.pass.textureWriteList; - foreach (TextureHandle texture in textureWrite) - { - ref CompiledResourceInfo info = ref m_CompiledTextureInfos[texture]; - info.producers.Add(passIndex); - passInfo.refCount++; + var resourceWrite = passInfo.pass.resourceWriteLists[type]; + foreach (var resource in resourceWrite) + { + ref CompiledResourceInfo info = ref m_CompiledResourcesInfos[type][resource]; + 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; + // 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.IsResourceImported(resource)) + passInfo.hasSideEffect = true; #if DEVELOPMENT_BUILD || UNITY_EDITOR - passInfo.debugTextureWrites.Add(m_Resources.GetTextureResourceDesc(texture).name); + passInfo.debugResourceWrites[type].Add(m_Resources.GetResourceName(resource)); #endif - } - - foreach (TextureHandle texture in passInfo.pass.transientTextureList) - { - ref CompiledResourceInfo info = ref m_CompiledTextureInfos[texture]; - info.refCount++; - info.consumers.Add(passIndex); - info.producers.Add(passIndex); - } - - // 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.consumers.Add(passIndex); - info.refCount++; - } - - var bufferWrite = passInfo.pass.bufferWriteList; - foreach (ComputeBufferHandle buffer in bufferWrite) - { - ref CompiledResourceInfo info = ref m_CompiledBufferInfos[buffer]; - info.producers.Add(passIndex); - passInfo.refCount++; + } - // Writing to an imported compute buffer is considered as a side effect because we don't know what users will do with it outside of render graph. - if (m_Resources.IsComputeBufferImported(buffer)) - passInfo.hasSideEffect = true; + foreach (int resourceIndex in passInfo.pass.transientResourceList[type]) + { + ref CompiledResourceInfo info = ref m_CompiledResourcesInfos[type][resourceIndex]; + info.refCount++; + info.consumers.Add(passIndex); + info.producers.Add(passIndex); + } } } } @@ -492,9 +527,9 @@ void PruneOutputlessPasses() { // Gather passes that don't produce anything and prune them. m_PruningStack.Clear(); - for (int i = 0; i < m_CompiledPassInfos.size; ++i) + for (int pass = 0; pass < m_CompiledPassInfos.size; ++pass) { - ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[i]; + ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[pass]; if (passInfo.refCount == 0 && !passInfo.hasSideEffect && passInfo.allowPassPruning) { @@ -503,85 +538,65 @@ void PruneOutputlessPasses() // We don't need to go recursively here because we decrement ref count of read resources // so the subsequent passes of pruning will detect those and remove the related passes. passInfo.pruned = true; - foreach (var index in passInfo.pass.textureReadList) + for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) { - m_CompiledTextureInfos[index].refCount--; - } + foreach (var index in passInfo.pass.resourceReadLists[type]) + { + m_CompiledResourcesInfos[type][index].refCount--; - foreach (var index in passInfo.pass.bufferReadList) - { - m_CompiledBufferInfos[index].refCount--; + } } } } } - void PruneUnusedPasses(bool textureResources) + void PruneUnusedPasses() { - DynamicArray resourceUsageList = textureResources ? m_CompiledTextureInfos : m_CompiledBufferInfos; + // TODO RENDERGRAPH: temporarily remove pruning of passes without product. + // Many passes are used just to set global variables so we don't want to force users to disallow pruning on those explicitly every time. + // This will prune passes with no outputs. + //PruneOutputlessPasses(); - // Gather resources that are never read. - m_PruningStack.Clear(); - for (int i = 0; i < resourceUsageList.size; ++i) + // This will prune all passes that produce resource that are never read. + for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) { - if (resourceUsageList[i].refCount == 0) + DynamicArray resourceUsageList = m_CompiledResourcesInfos[type]; + + // Gather resources that are never read. + m_PruningStack.Clear(); + for (int i = 0; i < resourceUsageList.size; ++i) { - m_PruningStack.Push(i); + if (resourceUsageList[i].refCount == 0) + { + m_PruningStack.Push(i); + } } - } - while (m_PruningStack.Count != 0) - { - var unusedResource = resourceUsageList[m_PruningStack.Pop()]; - foreach (var producerIndex in unusedResource.producers) + while (m_PruningStack.Count != 0) { - ref var producerInfo = ref m_CompiledPassInfos[producerIndex]; - producerInfo.refCount--; - if (producerInfo.refCount == 0 && !producerInfo.hasSideEffect && producerInfo.allowPassPruning) + var unusedResource = resourceUsageList[m_PruningStack.Pop()]; + foreach (var producerIndex in unusedResource.producers) { - // Producer is not necessary anymore as it produces zero resources - // Prune it and decrement refCount of all the textures it reads. - producerInfo.pruned = true; - - // Once again, can't share code because C# (can't have struct inheritance) - // Making all those List could help but we lose a lot of explicitness in the API... - if (textureResources) - { - foreach (var textureIndex in producerInfo.pass.textureReadList) - { - ref CompiledResourceInfo resourceInfo = ref resourceUsageList[textureIndex]; - resourceInfo.refCount--; - // If a resource is not used anymore, add it to the stack to be processed in subsequent iteration. - if (resourceInfo.refCount == 0) - m_PruningStack.Push(textureIndex); - } - } - else + ref var producerInfo = ref m_CompiledPassInfos[producerIndex]; + producerInfo.refCount--; + if (producerInfo.refCount == 0 && !producerInfo.hasSideEffect && producerInfo.allowPassPruning) { - foreach (var bufferIndex in producerInfo.pass.bufferReadList) + // 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 resourceIndex in producerInfo.pass.resourceReadLists[type]) { - ref CompiledResourceInfo resourceInfo = ref resourceUsageList[bufferIndex]; + ref CompiledResourceInfo resourceInfo = ref resourceUsageList[resourceIndex]; resourceInfo.refCount--; // If a resource is not used anymore, add it to the stack to be processed in subsequent iteration. if (resourceInfo.refCount == 0) - m_PruningStack.Push(bufferIndex); + m_PruningStack.Push(resourceIndex); } } } } } - } - - void PruneUnusedPasses() - { - // TODO RENDERGRAPH: temporarily remove pruning of passes without product. - // Many passes are used just to set global variables so we don't want to force users to disallow pruning on those explicitly every time. - // This will prune passes with no outputs. - //PruneOutputlessPasses(); - - // This will prune all passes that produce resource that are never read. - PruneUnusedPasses(textureResources: true); - PruneUnusedPasses(textureResources: false); LogPrunedPasses(); } @@ -708,77 +723,77 @@ void UpdateResourceAllocationAndSynchronization() if (passInfo.pruned) continue; - foreach (TextureHandle texture in passInfo.pass.textureReadList) + for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) { - UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, m_CompiledTextureInfos[texture]); - } + var resourcesInfo = m_CompiledResourcesInfos[type]; + foreach (int resource in passInfo.pass.resourceReadLists[type]) + { + UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, resourcesInfo[resource]); + } - foreach (TextureHandle texture in passInfo.pass.textureWriteList) - { - UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, m_CompiledTextureInfos[texture]); - } + foreach (int resource in passInfo.pass.resourceWriteLists[type]) + { + UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, resourcesInfo[resource]); + } - 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]); } // Gather all renderer lists m_RendererLists.AddRange(passInfo.pass.usedRendererListList); } - // Now push textures to the release list of the pass that reads it last. - for (int i = 0; i < m_CompiledTextureInfos.size; ++i) + for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) { - CompiledResourceInfo textureInfo = m_CompiledTextureInfos[i]; + var resourceInfos = m_CompiledResourcesInfos[type]; + // Now push resources to the release list of the pass that reads it last. + for (int i = 0; i < resourceInfos.size; ++i) + { + CompiledResourceInfo resourceInfo = resourceInfos[i]; - // Texture creation - int firstWriteIndex = GetFirstValidWriteIndex(textureInfo); - // This can happen for imported textures (for example an imported dummy black texture will never be written to but does not need creation anyway) - if (firstWriteIndex != -1) - m_CompiledPassInfos[firstWriteIndex].textureCreateList.Add(new TextureHandle(i)); + // Resource creation + int firstWriteIndex = GetFirstValidWriteIndex(resourceInfo); + // Index -1 can happen for imported resources (for example an imported dummy black texture will never be written to but does not need creation anyway) + if (firstWriteIndex != -1) + m_CompiledPassInfos[firstWriteIndex].resourceCreateList[type].Add(i); - // Texture release - // Sometimes, a texture can be written by a pass after the last pass that reads it. - // In this case, we need to extend its lifetime to this pass otherwise the pass would get an invalid texture. - int lastReadPassIndex = Math.Max(GetLatestValidReadIndex(textureInfo), GetLatestValidWriteIndex(textureInfo)); + // Texture release + // Sometimes, a texture can be written by a pass after the last pass that reads it. + // In this case, we need to extend its lifetime to this pass otherwise the pass would get an invalid texture. + int lastReadPassIndex = Math.Max(GetLatestValidReadIndex(resourceInfo), GetLatestValidWriteIndex(resourceInfo)); - if (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[lastReadPassIndex].enableAsyncCompute) + if (lastReadPassIndex != -1) { - int currentPassIndex = 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) + // 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[lastReadPassIndex].enableAsyncCompute) { - currentPassIndex++; - if(m_CompiledPassInfos[currentPassIndex].enableAsyncCompute) - firstWaitingPassIndex = m_CompiledPassInfos[currentPassIndex].syncFromPassIndex; - } + int currentPassIndex = 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; + } - // 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)); + // 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.resourceReleaseList[type].Add(i); - // Fail safe in case render graph is badly formed. - if (currentPassIndex == m_CompiledPassInfos.size) + // Fail safe in case render graph is badly formed. + if (currentPassIndex == m_CompiledPassInfos.size) + { + RenderGraphPass invalidPass = m_RenderPasses[lastReadPassIndex]; + throw new InvalidOperationException($"Asynchronous pass {invalidPass.name} was never synchronized on the graphics pipeline."); + } + } + else { - RenderGraphPass invalidPass = m_RenderPasses[lastReadPassIndex]; - throw new InvalidOperationException($"Asynchronous pass {invalidPass.name} was never synchronized on the graphics pipeline."); + ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[lastReadPassIndex]; + passInfo.resourceReleaseList[type].Add(i); } } - else - { - ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[lastReadPassIndex]; - passInfo.textureReleaseList.Add(new TextureHandle(i)); - } } } @@ -786,6 +801,7 @@ void UpdateResourceAllocationAndSynchronization() m_Resources.CreateRendererLists(m_RendererLists); } + // Internal for testing purpose only // Traverse the render graph: // - Determines when resources are created/released // - Determines async compute pass synchronization @@ -894,11 +910,14 @@ void PreRenderPassExecute(in CompiledPassInfo passInfo, ref RenderGraphContext r // 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); + m_Resources.PreRenderPassSetGlobalTextures(rgContext, pass.resourceReadLists[(int)RenderGraphResourceType.Texture]); - foreach (var texture in passInfo.textureCreateList) + foreach (var texture in passInfo.resourceCreateList[(int)RenderGraphResourceType.Texture]) m_Resources.CreateAndClearTexture(rgContext, texture); + foreach (var buffer in passInfo.resourceCreateList[(int)RenderGraphResourceType.ComputeBuffer]) + m_Resources.CreateComputeBuffer(rgContext, buffer); + PreRenderPassSetRenderTargets(passInfo, rgContext); // Flush first the current command buffer on the render context. @@ -935,12 +954,14 @@ void PostRenderPassExecute(CommandBuffer mainCmd, ref CompiledPassInfo passInfo, } if (m_DebugParameters.unbindGlobalTextures) - m_Resources.PostRenderPassUnbindGlobalTextures(rgContext, pass.textureReadList); + m_Resources.PostRenderPassUnbindGlobalTextures(rgContext, pass.resourceReadLists[(int)RenderGraphResourceType.Texture]); m_RenderGraphPool.ReleaseAllTempAlloc(); - foreach (var texture in passInfo.textureReleaseList) + foreach (var texture in passInfo.resourceReleaseList[(int)RenderGraphResourceType.Texture]) m_Resources.ReleaseTexture(rgContext, texture); + foreach (var buffer in passInfo.resourceReleaseList[(int)RenderGraphResourceType.ComputeBuffer]) + m_Resources.ReleaseComputeBuffer(rgContext, buffer); } void ClearRenderPasses() diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs index fa44c72f73e..4b93e5392ac 100644 --- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs @@ -20,10 +20,9 @@ public struct RenderGraphBuilder : IDisposable /// 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(TextureHandle input, int index) + public TextureHandle UseColorBuffer(in TextureHandle input, int index) { - CheckTransientTexture(input); - + CheckTransientResource(input.handle); m_RenderPass.SetColorBuffer(input, index); return input; } @@ -34,10 +33,9 @@ public TextureHandle UseColorBuffer(TextureHandle input, int index) /// 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(TextureHandle input, DepthAccess flags) + public TextureHandle UseDepthBuffer(in TextureHandle input, DepthAccess flags) { - CheckTransientTexture(input); - + CheckTransientResource(input.handle); m_RenderPass.SetDepthBuffer(input, flags); return input; } @@ -47,11 +45,10 @@ public TextureHandle UseDepthBuffer(TextureHandle input, DepthAccess flags) /// /// The Texture resource to read from during the pass. /// An updated resource handle to the input resource. - public TextureHandle ReadTexture(TextureHandle input) + public TextureHandle ReadTexture(in TextureHandle input) { - CheckTransientTexture(input); - - m_RenderPass.AddTextureRead(input); + CheckTransientResource(input.handle); + m_RenderPass.AddResourceRead(input.handle); return input; } @@ -60,12 +57,11 @@ public TextureHandle ReadTexture(TextureHandle input) /// /// The Texture resource to write to during the pass. /// An updated resource handle to the input resource. - public TextureHandle WriteTexture(TextureHandle input) + public TextureHandle WriteTexture(in TextureHandle input) { - CheckTransientTexture(input); - - // TODO: Manage resource "version" for debugging purpose - m_RenderPass.AddTextureWrite(input); + CheckTransientResource(input.handle); + // TODO RENDERGRAPH: Manage resource "version" for debugging purpose + m_RenderPass.AddResourceWrite(input.handle); return input; } @@ -78,20 +74,21 @@ public TextureHandle WriteTexture(TextureHandle input) public TextureHandle CreateTransientTexture(in TextureDesc desc) { var result = m_Resources.CreateTexture(desc, 0, m_RenderPass.index); - m_RenderPass.AddTransientTexture(result); + 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(TextureHandle texture) + public TextureHandle CreateTransientTexture(in TextureHandle texture) { - var desc = m_Resources.GetTextureResourceDesc(texture); + var desc = m_Resources.GetTextureResourceDesc(texture.handle); var result = m_Resources.CreateTexture(desc, 0, m_RenderPass.index); - m_RenderPass.AddTransientTexture(result); + m_RenderPass.AddTransientResource(result.handle); return result; } @@ -100,7 +97,7 @@ public TextureHandle CreateTransientTexture(TextureHandle texture) /// /// The Renderer List resource to use during the pass. /// An updated resource handle to the input resource. - public RendererListHandle UseRendererList(RendererListHandle input) + public RendererListHandle UseRendererList(in RendererListHandle input) { m_RenderPass.UseRendererList(input); return input; @@ -111,9 +108,10 @@ public RendererListHandle UseRendererList(RendererListHandle input) /// /// The Compute Buffer resource to read from during the pass. /// An updated resource handle to the input resource. - public ComputeBufferHandle ReadComputeBuffer(ComputeBufferHandle input) + public ComputeBufferHandle ReadComputeBuffer(in ComputeBufferHandle input) { - m_RenderPass.AddBufferRead(input); + CheckTransientResource(input.handle); + m_RenderPass.AddResourceRead(input.handle); return input; } @@ -122,12 +120,40 @@ public ComputeBufferHandle ReadComputeBuffer(ComputeBufferHandle input) /// /// The Compute Buffer resource to write to during the pass. /// An updated resource handle to the input resource. - public ComputeBufferHandle WriteComputeBuffer(ComputeBufferHandle input) + public ComputeBufferHandle WriteComputeBuffer(in ComputeBufferHandle input) { - m_RenderPass.AddBufferWrite(input); + CheckTransientResource(input.handle); + m_RenderPass.AddResourceWrite(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. @@ -185,12 +211,18 @@ void Dispose(bool disposing) m_Disposed = true; } - void CheckTransientTexture(TextureHandle input) + void CheckTransientResource(in ResourceHandle res) { - if (input.IsValid() && input.transientPassIndex != -1 && input.transientPassIndex != m_RenderPass.index) +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (res.IsValid()) { - throw new ArgumentException($"Trying to use a transient texture (pass index {input.transientPassIndex}) in a different pass (pass index {m_RenderPass.index}."); + int transientIndex = m_Resources.GetResourceTransientIndex(res); + 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}."); + } } +#endif } #endregion } diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs index 0d282cc3713..3bc1715b7ca 100644 --- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs @@ -26,23 +26,34 @@ public RenderFunc GetExecuteDelegate() public int colorBufferMaxIndex { get; protected set; } = -1; public int refCount { get; protected set; } - public List textureReadList = new List(); - public List textureWriteList = new List(); - public List transientTextureList = new List(); - public List bufferReadList = new List(); - public List bufferWriteList = new List(); + public List[] resourceReadLists = new List[(int)RenderGraphResourceType.Count]; + public List[] resourceWriteLists = new List[(int)RenderGraphResourceType.Count]; + public List[] transientResourceList = new List[(int)RenderGraphResourceType.Count]; + public List usedRendererListList = new List(); + public RenderGraphPass() + { + for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) + { + resourceReadLists[i] = new List(); + resourceWriteLists[i] = new List(); + transientResourceList[i] = new List(); + } + } + public void Clear() { name = ""; index = -1; customSampler = null; - textureReadList.Clear(); - textureWriteList.Clear(); - bufferReadList.Clear(); - bufferWriteList.Clear(); - transientTextureList.Clear(); + for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) + { + resourceReadLists[i].Clear(); + resourceWriteLists[i].Clear(); + transientResourceList[i].Clear(); + } + usedRendererListList.Clear(); enableAsyncCompute = false; allowPassPruning = true; @@ -57,31 +68,19 @@ public void Clear() } } - public void AddTextureWrite(TextureHandle texture) - { - textureWriteList.Add(texture); - refCount++; - } - - public void AddTextureRead(TextureHandle texture) - { - textureReadList.Add(texture); - } - - public void AddBufferWrite(ComputeBufferHandle buffer) + public void AddResourceWrite(in ResourceHandle res) { - bufferWriteList.Add(buffer); - refCount++; + resourceWriteLists[res.iType].Add(res); } - public void AddTransientTexture(TextureHandle texture) + public void AddResourceRead(in ResourceHandle res) { - transientTextureList.Add(texture); + resourceReadLists[res.iType].Add(res); } - public void AddBufferRead(ComputeBufferHandle buffer) + public void AddTransientResource(in ResourceHandle res) { - bufferReadList.Add(buffer); + transientResourceList[res.iType].Add(res); } public void UseRendererList(RendererListHandle rendererList) @@ -104,16 +103,16 @@ public void SetColorBuffer(TextureHandle resource, int index) Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0); colorBufferMaxIndex = Math.Max(colorBufferMaxIndex, index); colorBuffers[index] = resource; - AddTextureWrite(resource); + AddResourceWrite(resource.handle); } public void SetDepthBuffer(TextureHandle resource, DepthAccess flags) { depthBuffer = resource; if ((flags & DepthAccess.Read) != 0) - AddTextureRead(resource); + AddResourceRead(resource.handle); if ((flags & DepthAccess.Write) != 0) - AddTextureWrite(resource); + AddResourceWrite(resource.handle); } } diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourcePool.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourcePool.cs new file mode 100644 index 00000000000..29f1a46c9e7 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourcePool.cs @@ -0,0 +1,202 @@ +using System.Collections.Generic; +using UnityEngine.Rendering; + +namespace UnityEngine.Experimental.Rendering.RenderGraphModule +{ + + abstract class RenderGraphResourcePool where Type : class + { + // Dictionary tracks resources by hash and stores resources with same hash in a List (list instead of a stack because we need to be able to remove stale allocations). + protected Dictionary> m_ResourcePool = new Dictionary>(); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + // Diagnostic only + // This list allows us to determine if all resources were correctly released in the frame. + List<(int, Type)> m_FrameAllocatedResources = new List<(int, Type)>(); +#endif + + protected static int s_CurrentFrameIndex; + + // Release the GPU resource itself + abstract protected void ReleaseInternalResource(Type res); + abstract protected string GetResourceName(Type res); + abstract protected string GetResourceTypeName(); + + public void ReleaseResource(int hash, Type resource, int currentFrameIndex) + { + if (!m_ResourcePool.TryGetValue(hash, out var list)) + { + list = new List<(Type resource, int frameIndex)>(); + m_ResourcePool.Add(hash, list); + } + + list.Add((resource, currentFrameIndex)); + } + + public bool TryGetResource(int hashCode, out Type resource) + { + if (m_ResourcePool.TryGetValue(hashCode, out var list) && list.Count > 0) + { + resource = list[list.Count - 1].resource; + list.RemoveAt(list.Count - 1); // O(1) since it's the last element. + return true; + } + + resource = null; + return false; + } + + abstract public void PurgeUnusedResources(int currentFrameIndex); + + public void Cleanup() + { + foreach (var kvp in m_ResourcePool) + { + foreach (var res in kvp.Value) + { + ReleaseInternalResource(res.resource); + } + } + } + + public void RegisterFrameAllocation(int hash, Type value) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (hash != -1) + m_FrameAllocatedResources.Add((hash, value)); +#endif + } + + public void UnregisterFrameAllocation(int hash, Type value) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (hash != -1) + m_FrameAllocatedResources.Remove((hash, value)); +#endif + } + + public void CheckFrameAllocation(bool onException, int frameIndex) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_FrameAllocatedResources.Count != 0 && !onException) + { + string logMessage = $"RenderGraph: Not all resources of type {GetResourceTypeName()} were released. This can be caused by a resources being allocated but never read by any pass."; + + foreach (var value in m_FrameAllocatedResources) + { + logMessage = $"{logMessage}\n\t{GetResourceName(value.Item2)}"; + ReleaseResource(value.Item1, value.Item2, frameIndex); + } + + Debug.LogWarning(logMessage); + } + + // If an error occurred during execution, it's expected that textures are not all released so we clear the tracking list. + m_FrameAllocatedResources.Clear(); +#endif + } + + + public void LogResources(RenderGraphLogger logger) + { + List allocationList = new List(); + foreach (var kvp in m_ResourcePool) + { + foreach (var res in kvp.Value) + { + allocationList.Add(GetResourceName(res.resource)); + } + } + + logger.LogLine($"== {GetResourceTypeName()} Resources =="); + allocationList.Sort(); + int index = 0; + foreach (var element in allocationList) + logger.LogLine("[{0}] {1}", index++, element); + } + } + + class TexturePool : RenderGraphResourcePool + { + protected override void ReleaseInternalResource(RTHandle res) + { + res.Release(); + } + + protected override string GetResourceName(RTHandle res) + { + return res.rt.name; + } + + override protected string GetResourceTypeName() + { + return "Texture"; + } + + // Another C# nicety. + // We need to re-implement the whole thing every time because: + // - obj.resource.Release is Type specific so it cannot be called on a generic (and there's no shared interface for resources like RTHandle, ComputeBuffers etc) + // - We can't use a virtual release function because it will capture this in the lambda for RemoveAll generating GCAlloc in the process. + override public void PurgeUnusedResources(int currentFrameIndex) + { + // Update the frame index for the lambda. Static because we don't want to capture. + s_CurrentFrameIndex = currentFrameIndex; + + foreach (var kvp in m_ResourcePool) + { + var list = kvp.Value; + list.RemoveAll(obj => + { + if (obj.frameIndex < s_CurrentFrameIndex) + { + obj.resource.Release(); + return true; + } + return false; + }); + } + } + } + + class ComputeBufferPool : RenderGraphResourcePool + { + protected override void ReleaseInternalResource(ComputeBuffer res) + { + res.Release(); + } + + protected override string GetResourceName(ComputeBuffer res) + { + return "ComputeBufferNameNotAvailable"; // res.name is a setter only :( + } + + override protected string GetResourceTypeName() + { + return "ComputeBuffer"; + } + + // Another C# nicety. + // We need to re-implement the whole thing every time because: + // - obj.resource.Release is Type specific so it cannot be called on a generic (and there's no shared interface for resources like RTHandle, ComputeBuffers etc) + // - We can't use a virtual release function because it will capture this in the lambda for RemoveAll generating GCAlloc in the process. + override public void PurgeUnusedResources(int currentFrameIndex) + { + // Update the frame index for the lambda. Static because we don't want to capture. + s_CurrentFrameIndex = currentFrameIndex; + + foreach (var kvp in m_ResourcePool) + { + var list = kvp.Value; + list.RemoveAll(obj => + { + if (obj.frameIndex < s_CurrentFrameIndex) + { + obj.resource.Release(); + return true; + } + return false; + }); + } + } + } +} diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourcePool.cs.meta b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourcePool.cs.meta new file mode 100644 index 00000000000..29e7cc7ce46 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourcePool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4998ae5b70f43844eb7716decb537ba5 +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 ade0f6c4643..6b5ea8acdab 100644 --- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs @@ -5,448 +5,73 @@ namespace UnityEngine.Experimental.Rendering.RenderGraphModule { - #region Resource Descriptors - // BEHOLD C# COPY PASTA - // Struct can't be inherited and can't have default member values - // Hence the copy paste and the ugly IsValid implementation. - /// - /// Texture resource handle. + /// The RenderGraphResourceRegistry holds all resource allocated during Render Graph execution. /// - [DebuggerDisplay("Texture ({handle})")] - public struct TextureHandle + public class RenderGraphResourceRegistry { - bool m_IsValid; - internal int handle { get; private set; } - 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. - /// - /// Texture handle to convert. - /// The integer representation of the handle. - public static implicit operator int(TextureHandle handle) { return handle.handle; } - /// - /// Return true if the handle is valid. - /// - /// True if the handle is valid. - public bool IsValid() => m_IsValid; - - /// - /// Equals Override. - /// - /// Other handle to test against. - /// True if both handle are equals. - public override bool Equals(System.Object obj) - { - //Check for null and compare run-time types. - if ((obj == null) || !this.GetType().Equals(obj.GetType())) - { - return false; - } - else - { - TextureHandle texture = (TextureHandle)obj; - return texture.handle == handle && texture.m_IsValid == m_IsValid; - } - } + static readonly ShaderTagId s_EmptyName = new ShaderTagId(""); - /// - /// GetHashCode override. - /// - /// - public override int GetHashCode() + class IRenderGraphResource { - return (handle << 2) ^ (m_IsValid ? 333 : 444); - } - } - - /// - /// Renderer List resource handle. - /// - [DebuggerDisplay("RendererList ({handle})")] - public struct RendererListHandle - { - bool m_IsValid; - internal int handle { get; private set; } - internal RendererListHandle(int handle) { this.handle = handle; m_IsValid = true; } - /// - /// Conversion to int. - /// - /// Renderer List handle to convert. - /// The integer representation of the handle. - public static implicit operator int(RendererListHandle handle) { return handle.handle; } - /// - /// Return true if the handle is valid. - /// - /// True if the handle is valid. - public bool IsValid() => m_IsValid; - } + public bool imported; + public int cachedHash; + public int shaderProperty; + public int transientPassIndex; + public bool wasReleased; - /// - /// Compute Buffer resource handle. - /// - [DebuggerDisplay("ComputeBuffer ({handle})")] - public struct ComputeBufferHandle - { - bool m_IsValid; - internal int handle { get; private set; } - internal ComputeBufferHandle(int handle) { this.handle = handle; m_IsValid = true; } - /// - /// Conversion to int. - /// - /// Compute Buffer handle to convert. - /// The integer representation of the handle. - public static implicit operator int(ComputeBufferHandle handle) { return handle.handle; } - /// - /// Return true if the handle is valid. - /// - /// True if the handle is valid. - public bool IsValid() => m_IsValid; - } - - /// - /// The mode that determines the size of a Texture. - /// - public enum TextureSizeMode - { - ///Explicit size. - Explicit, - ///Size automatically scaled by a Vector. - Scale, - ///Size automatically scaled by a Functor. - Functor - } - -#if UNITY_2020_2_OR_NEWER - /// - /// Subset of the texture desc containing information for fast memory allocation (when platform supports it) - /// - public struct FastMemoryDesc - { - ///Whether the texture will be in fast memory. - public bool inFastMemory; - ///Flag to determine what parts of the render target is spilled if not fully resident in fast memory. - public FastMemoryFlags flags; - ///How much of the render target is to be switched into fast memory (between 0 and 1). - public float residencyFraction; - } -#endif - - /// - /// Descriptor used to create texture resources - /// - public struct TextureDesc - { - ///Texture sizing mode. - public TextureSizeMode sizeMode; - ///Texture width. - public int width; - ///Texture height. - public int height; - ///Number of texture slices.. - public int slices; - ///Texture scale. - public Vector2 scale; - ///Texture scale function. - public ScaleFunc func; - ///Depth buffer bit depth. - public DepthBits depthBufferBits; - ///Color format. - public GraphicsFormat colorFormat; - ///Filtering mode. - public FilterMode filterMode; - ///Addressing mode. - public TextureWrapMode wrapMode; - ///Texture dimension. - public TextureDimension dimension; - ///Enable random UAV read/write on the texture. - public bool enableRandomWrite; - ///Texture needs mip maps. - public bool useMipMap; - ///Automatically generate mip maps. - public bool autoGenerateMips; - ///Texture is a shadow map. - public bool isShadowMap; - ///Anisotropic filtering level. - public int anisoLevel; - ///Mip map bias. - public float mipMapBias; - ///Textre is multisampled. Only supported for Scale and Functor size mode. - public bool enableMSAA; - ///Number of MSAA samples. Only supported for Explicit size mode. - public MSAASamples msaaSamples; - ///Bind texture multi sampled. - public bool bindTextureMS; - ///Texture uses dynamic scaling. - public bool useDynamicScale; - ///Memory less flag. - public RenderTextureMemoryless memoryless; - ///Texture name. - public string name; -#if UNITY_2020_2_OR_NEWER - ///Descriptor to determine how the texture will be in fast memory on platform that supports it. - public FastMemoryDesc fastMemoryDesc; -#endif - - // Initial state. Those should not be used in the hash - ///Texture needs to be cleared on first use. - public bool clearBuffer; - ///Clear color. - public Color clearColor; - - void InitDefaultValues(bool dynamicResolution, bool xrReady) - { - useDynamicScale = dynamicResolution; - // XR Ready - if (xrReady) + public virtual void Reset() { - slices = TextureXR.slices; - dimension = TextureXR.dimension; - } - else - { - slices = 1; - dimension = TextureDimension.Tex2D; + imported = false; + cachedHash = -1; + shaderProperty = 0; + transientPassIndex = -1; + wasReleased = false; } - } - - /// - /// TextureDesc constructor for a texture using explicit size - /// - /// Texture width - /// Texture height - /// Use dynamic resolution - /// Set this to true if the Texture is a render texture in an XR setting. - public TextureDesc(int width, int height, bool dynamicResolution = false, bool xrReady = false) - : this() - { - // Size related init - sizeMode = TextureSizeMode.Explicit; - this.width = width; - this.height = height; - // Important default values not handled by zero construction in this() - msaaSamples = MSAASamples.None; - InitDefaultValues(dynamicResolution, xrReady); - } - - /// - /// TextureDesc constructor for a texture using a fixed scaling - /// - /// RTHandle scale used for this texture - /// Use dynamic resolution - /// Set this to true if the Texture is a render texture in an XR setting. - public TextureDesc(Vector2 scale, bool dynamicResolution = false, bool xrReady = false) - : this() - { - // Size related init - sizeMode = TextureSizeMode.Scale; - this.scale = scale; - // Important default values not handled by zero construction in this() - msaaSamples = MSAASamples.None; - dimension = TextureDimension.Tex2D; - InitDefaultValues(dynamicResolution, xrReady); - } - /// - /// TextureDesc constructor for a texture using a functor for scaling - /// - /// Function used to determnine the texture size - /// Use dynamic resolution - /// Set this to true if the Texture is a render texture in an XR setting. - public TextureDesc(ScaleFunc func, bool dynamicResolution = false, bool xrReady = false) - : this() - { - // Size related init - sizeMode = TextureSizeMode.Functor; - this.func = func; - // Important default values not handled by zero construction in this() - msaaSamples = MSAASamples.None; - dimension = TextureDimension.Tex2D; - InitDefaultValues(dynamicResolution, xrReady); - } - - /// - /// Copy constructor - /// - /// - public TextureDesc(TextureDesc input) - { - this = input; - } - - /// - /// Hash function - /// - /// The texture descriptor hash. - public override int GetHashCode() - { - int hashCode = 17; - - unchecked + public virtual string GetName() { - switch (sizeMode) - { - case TextureSizeMode.Explicit: - hashCode = hashCode * 23 + width; - hashCode = hashCode * 23 + height; - hashCode = hashCode * 23 + (int)msaaSamples; - break; - case TextureSizeMode.Functor: - if (func != null) - hashCode = hashCode * 23 + func.GetHashCode(); - hashCode = hashCode * 23 + (enableMSAA ? 1 : 0); - break; - case TextureSizeMode.Scale: - hashCode = hashCode * 23 + scale.x.GetHashCode(); - hashCode = hashCode * 23 + scale.y.GetHashCode(); - hashCode = hashCode * 23 + (enableMSAA ? 1 : 0); - break; - } - - hashCode = hashCode * 23 + mipMapBias.GetHashCode(); - hashCode = hashCode * 23 + slices; - hashCode = hashCode * 23 + (int)depthBufferBits; - hashCode = hashCode * 23 + (int)colorFormat; - hashCode = hashCode * 23 + (int)filterMode; - hashCode = hashCode * 23 + (int)wrapMode; - hashCode = hashCode * 23 + (int)dimension; - hashCode = hashCode * 23 + (int)memoryless; - hashCode = hashCode * 23 + anisoLevel; - hashCode = hashCode * 23 + (enableRandomWrite ? 1 : 0); - hashCode = hashCode * 23 + (useMipMap ? 1 : 0); - hashCode = hashCode * 23 + (autoGenerateMips ? 1 : 0); - hashCode = hashCode * 23 + (isShadowMap ? 1 : 0); - hashCode = hashCode * 23 + (bindTextureMS ? 1 : 0); - hashCode = hashCode * 23 + (useDynamicScale ? 1 : 0); + return ""; } - - return hashCode; } - } - #endregion - - class RenderGraphTexturePool - { - // Dictionary tracks resources by hash and stores resources with same hash in a List (list instead of a stack because we need to be able to remove stale allocations). - Dictionary> m_ResourcePool = new Dictionary>(); - static int s_CurrentFrameIndex; - public void ReleaseResource(int hash, RTHandle rt, int currentFrameIndex) + #region Resources + [DebuggerDisplay("Resource ({ResType}:{GetName()})")] + class RenderGraphResource + : IRenderGraphResource + where DescType : struct + where ResType : class { - if (!m_ResourcePool.TryGetValue(hash, out var list)) - { - list = new List<(RTHandle rt, int frameIndex)>(); - m_ResourcePool.Add(hash, list); - } + public DescType desc; + public ResType resource; - list.Add((rt, currentFrameIndex)); - } - - public bool TryGetResource(int hashCode, out RTHandle rt) - { - if (m_ResourcePool.TryGetValue(hashCode, out var list) && list.Count > 0) + protected RenderGraphResource() { - rt = list[list.Count - 1].resource; - list.RemoveAt(list.Count - 1); // O(1) since it's the last element. - return true; - } - - rt = null; - return false; - } - - public void PurgeUnusedResources(int currentFrameIndex) - { - // Update the frame index for the lambda. Static because we don't want to capture. - s_CurrentFrameIndex = currentFrameIndex; - foreach(var kvp in m_ResourcePool) - { - var list = kvp.Value; - list.RemoveAll(obj => - { - if (obj.frameIndex < s_CurrentFrameIndex) - { - obj.resource.Release(); - return true; - } - return false; - }); } - } - public void Cleanup() - { - foreach (var kvp in m_ResourcePool) + public override void Reset() { - foreach (var res in kvp.Value) - { - res.resource.Release(); - } + base.Reset(); + resource = null; } } - public void LogResources(RenderGraphLogger logger) + [DebuggerDisplay("TextureResource ({desc.name})")] + class TextureResource : RenderGraphResource { - List allocationList = new List(); - foreach (var kvp in m_ResourcePool) + public override string GetName() { - foreach (var res in kvp.Value) - { - allocationList.Add(res.resource.rt.name); - } + return desc.name; } - - allocationList.Sort(); - int index = 0; - foreach (var element in allocationList) - logger.LogLine("[{0}] {1}", index++, element); } - } - /// - /// The RenderGraphResourceRegistry holds all resource allocated during Render Graph execution. - /// - public class RenderGraphResourceRegistry - { - static readonly ShaderTagId s_EmptyName = new ShaderTagId(""); - - #region Resources - internal struct TextureResource + [DebuggerDisplay("ComputeBufferResource ({desc.name})")] + class ComputeBufferResource : RenderGraphResource { - public TextureDesc desc; - public bool imported; - public RTHandle rt; - public int cachedHash; - public int shaderProperty; - public bool wasReleased; - - internal TextureResource(RTHandle rt, int shaderProperty) - : this() - { - Reset(); - - this.rt = rt; - imported = true; - this.shaderProperty = shaderProperty; - } - - internal TextureResource(in TextureDesc desc, int shaderProperty) - : this() + public override string GetName() { - Reset(); - - this.desc = desc; - this.shaderProperty = shaderProperty; - } - - void Reset() - { - imported = false; - rt = null; - cachedHash = -1; - wasReleased = false; + return desc.name; } } @@ -462,23 +87,13 @@ internal RendererListResource(in RendererListDesc desc) } } - internal struct ComputeBufferResource - { - public ComputeBuffer computeBuffer; - public bool imported; - - internal ComputeBufferResource(ComputeBuffer computeBuffer, bool imported) - { - this.computeBuffer = computeBuffer; - this.imported = imported; - } - } #endregion - DynamicArray m_TextureResources = new DynamicArray(); - RenderGraphTexturePool m_TexturePool = new RenderGraphTexturePool(); + DynamicArray[] m_Resources = new DynamicArray[(int)RenderGraphResourceType.Count]; + + TexturePool m_TexturePool = new TexturePool(); + ComputeBufferPool m_ComputeBufferPool = new ComputeBufferPool(); DynamicArray m_RendererListResources = new DynamicArray(); - DynamicArray m_ComputeBufferResources = new DynamicArray(); RTHandleSystem m_RTHandleSystem = new RTHandleSystem(); RenderGraphDebugParams m_RenderGraphDebug; RenderGraphLogger m_Logger; @@ -486,10 +101,6 @@ internal ComputeBufferResource(ComputeBuffer computeBuffer, bool imported) RTHandle m_CurrentBackbuffer; - // Diagnostic only - // This list allows us to determine if all textures were correctly released in the frame. - List<(int, RTHandle)> m_FrameAllocatedTextures = new List<(int, RTHandle)>(); - #region Public Interface /// /// Returns the RTHandle associated with the provided resource handle. @@ -501,17 +112,7 @@ public RTHandle GetTexture(in TextureHandle handle) if (!handle.IsValid()) return null; -#if DEVELOPMENT_BUILD || UNITY_EDITOR - - var res = m_TextureResources[handle]; - - if (res.rt == null && !res.wasReleased) - throw new InvalidOperationException(string.Format("Trying to access texture \"{0}\" that was never created. Check that it was written at least once before trying to get it.", res.desc.name)); - - if (res.rt == null && res.wasReleased) - throw new InvalidOperationException(string.Format("Trying to access texture \"{0}\" that was already released. Check that the last pass where it's read is after this one.", res.desc.name)); -#endif - return m_TextureResources[handle].rt; + return GetTextureResource(handle.handle).resource; } /// @@ -537,7 +138,7 @@ public ComputeBuffer GetComputeBuffer(in ComputeBufferHandle handle) if (!handle.IsValid()) return null; - return m_ComputeBufferResources[handle].computeBuffer; + return GetComputeBufferResource(handle.handle).resource; } #endregion @@ -553,6 +154,25 @@ internal RenderGraphResourceRegistry(bool supportMSAA, MSAASamples initialSample m_RTHandleSystem.Initialize(Screen.width, Screen.height, supportMSAA, initialSampleCount); m_RenderGraphDebug = renderGraphDebug; m_Logger = logger; + + for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) + m_Resources[i] = new DynamicArray(); + } + + ResType GetResource(DynamicArray resourceArray, int index) + where DescType : struct + where ResType : class + { + var res = resourceArray[index] as RenderGraphResource; + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (res.resource == null && !res.wasReleased) + throw new InvalidOperationException(string.Format("Trying to access resource \"{0}\" that was never created. Check that it was written at least once before trying to get it.", res.GetName())); + + if (res.resource == null && res.wasReleased) + throw new InvalidOperationException(string.Format("Trying to access resource \"{0}\" that was already released. Check that the last pass where it's read is after this one.", res.GetName())); +#endif + return res.resource; } internal void BeginRender(int width, int height, MSAASamples msaaSamples, int currentFrameIndex) @@ -564,16 +184,40 @@ internal void BeginRender(int width, int height, MSAASamples msaaSamples, int cu internal RTHandleProperties GetRTHandleProperties() { return m_RTHandleSystem.rtHandleProperties; } - // Texture Creation/Import APIs are internal because creation should only go through RenderGraph - internal TextureHandle ImportTexture(RTHandle rt, int shaderProperty = 0) + void CheckHandleValidity(in ResourceHandle res) { - int newHandle = m_TextureResources.Add(new TextureResource(rt, shaderProperty)); - return new TextureHandle(newHandle); + var resources = m_Resources[res.iType]; + if (res.index >= resources.size) + throw new ArgumentException($"Trying to access resource of type {res.type} with an invalid resource index {res.index}"); + } + + internal string GetResourceName(in ResourceHandle res) + { + CheckHandleValidity(res); + return m_Resources[res.iType][res.index].GetName(); + } + + internal bool IsResourceImported(in ResourceHandle res) + { + CheckHandleValidity(res); + return m_Resources[res.iType][res.index].imported; + } + + internal int GetResourceTransientIndex(in ResourceHandle res) + { + CheckHandleValidity(res); + return m_Resources[res.iType][res.index].transientPassIndex; } - internal bool IsTextureImported(TextureHandle handle) + // Texture Creation/Import APIs are internal because creation should only go through RenderGraph + internal TextureHandle ImportTexture(RTHandle rt, int shaderProperty = 0) { - return handle.IsValid() ? GetTextureResource(handle).imported : false; + int newHandle = AddNewResource(m_Resources[(int)RenderGraphResourceType.Texture], out TextureResource texResource); + texResource.resource = rt; + texResource.imported = true; + texResource.shaderProperty = shaderProperty; + + return new TextureHandle(newHandle); } internal TextureHandle ImportBackbuffer(RenderTargetIdentifier rt) @@ -583,31 +227,50 @@ internal TextureHandle ImportBackbuffer(RenderTargetIdentifier rt) else m_CurrentBackbuffer = m_RTHandleSystem.Alloc(rt); - int newHandle = m_TextureResources.Add(new TextureResource(m_CurrentBackbuffer, 0)); + int newHandle = AddNewResource(m_Resources[(int)RenderGraphResourceType.Texture], out TextureResource texResource); + texResource.resource = m_CurrentBackbuffer; + texResource.imported = true; + return new TextureHandle(newHandle); } + int AddNewResource(DynamicArray resourceArray, out ResType outRes) where ResType : IRenderGraphResource, new() + { + // In order to not create garbage, instead of using Add, we keep the content of the array while resizing and we just reset the existing ref (or create it if it's null). + int result = resourceArray.size; + resourceArray.Resize(resourceArray.size + 1, true); + if (resourceArray[result] == null) + resourceArray[result] = new ResType(); + + outRes = resourceArray[result] as ResType; + outRes.Reset(); + return result; + } + 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, transientPassIndex); + int newHandle = AddNewResource(m_Resources[(int)RenderGraphResourceType.Texture], out TextureResource texResource); + texResource.desc = desc; + texResource.shaderProperty = shaderProperty; + texResource.transientPassIndex = transientPassIndex; + return new TextureHandle(newHandle); } internal int GetTextureResourceCount() { - return m_TextureResources.size; + return m_Resources[(int)RenderGraphResourceType.Texture].size; } - ref TextureResource GetTextureResource(TextureHandle res) + TextureResource GetTextureResource(in ResourceHandle handle) { - return ref m_TextureResources[res]; + return m_Resources[(int)RenderGraphResourceType.Texture][handle] as TextureResource; } - internal TextureDesc GetTextureResourceDesc(TextureHandle res) + internal TextureDesc GetTextureResourceDesc(in ResourceHandle handle) { - return m_TextureResources[res].desc; + return (m_Resources[(int)RenderGraphResourceType.Texture][handle] as TextureResource).desc; } internal RendererListHandle CreateRendererList(in RendererListDesc desc) @@ -620,37 +283,88 @@ internal RendererListHandle CreateRendererList(in RendererListDesc desc) internal ComputeBufferHandle ImportComputeBuffer(ComputeBuffer computeBuffer) { - int newHandle = m_ComputeBufferResources.Add(new ComputeBufferResource(computeBuffer, imported: true)); + int newHandle = AddNewResource(m_Resources[(int)RenderGraphResourceType.ComputeBuffer], out ComputeBufferResource bufferResource); + bufferResource.resource = computeBuffer; + bufferResource.imported = true; + + return new ComputeBufferHandle(newHandle); + } + + internal ComputeBufferHandle CreateComputeBuffer(in ComputeBufferDesc desc, int transientPassIndex = -1) + { + ValidateComputeBufferDesc(desc); + + int newHandle = AddNewResource(m_Resources[(int)RenderGraphResourceType.ComputeBuffer], out ComputeBufferResource bufferResource); + bufferResource.desc = desc; + bufferResource.transientPassIndex = transientPassIndex; + return new ComputeBufferHandle(newHandle); } - internal bool IsComputeBufferImported(ComputeBufferHandle handle) + internal ComputeBufferDesc GetComputeBufferResourceDesc(in ResourceHandle handle) { - return handle.IsValid() ? GetComputeBufferResource(handle).imported : false; + return (m_Resources[(int)RenderGraphResourceType.ComputeBuffer][handle] as ComputeBufferResource).desc; } internal int GetComputeBufferResourceCount() { - return m_ComputeBufferResources.size; + return m_Resources[(int)RenderGraphResourceType.ComputeBuffer].size; } - internal ref ComputeBufferResource GetComputeBufferResource(ComputeBufferHandle res) + ComputeBufferResource GetComputeBufferResource(in ResourceHandle handle) { - return ref m_ComputeBufferResources[res]; + return m_Resources[(int)RenderGraphResourceType.ComputeBuffer][handle] as ComputeBufferResource; } - internal void CreateAndClearTexture(RenderGraphContext rgContext, TextureHandle texture) + internal void CreateAndClearTexture(RenderGraphContext rgContext, int index) { - ref var resource = ref GetTextureResource(texture); + var resource = m_Resources[(int)RenderGraphResourceType.Texture][index] as TextureResource; + if (!resource.imported) { - CreateTextureForPass(ref resource); + var desc = resource.desc; + int hashCode = desc.GetHashCode(); + + if (resource.resource != null) + 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.resource = null; + if (!m_TexturePool.TryGetResource(hashCode, out resource.resource)) + { + string name = desc.name; + if (m_RenderGraphDebug.tagResourceNamesWithRG) + name = $"RenderGraph_{name}"; + + // Note: Name used here will be the one visible in the memory profiler so it means that whatever is the first pass that actually allocate the texture will set the name. + // TODO: Find a way to display name by pass. + switch (desc.sizeMode) + { + case TextureSizeMode.Explicit: + resource.resource = m_RTHandleSystem.Alloc(desc.width, desc.height, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite, + desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.msaaSamples, desc.bindTextureMS, desc.useDynamicScale, desc.memoryless, desc.name); + break; + case TextureSizeMode.Scale: + resource.resource = m_RTHandleSystem.Alloc(desc.scale, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite, + desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.enableMSAA, desc.bindTextureMS, desc.useDynamicScale, desc.memoryless, desc.name); + break; + case TextureSizeMode.Functor: + resource.resource = m_RTHandleSystem.Alloc(desc.func, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite, + desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.enableMSAA, desc.bindTextureMS, desc.useDynamicScale, desc.memoryless, desc.name); + break; + } + } + + //// Try to update name when re-using a texture. + //// TODO RENDERGRAPH: Check if that actually works. + //resource.rt.name = desc.name; + + resource.cachedHash = hashCode; #if UNITY_2020_2_OR_NEWER var fastMemDesc = resource.desc.fastMemoryDesc; if(fastMemDesc.inFastMemory) { - resource.rt.SwitchToFastMemory(rgContext.cmd, fastMemDesc.residencyFraction, fastMemDesc.flags); + resource.resource.SwitchToFastMemory(rgContext.cmd, fastMemDesc.residencyFraction, fastMemDesc.flags); } #endif @@ -662,116 +376,110 @@ internal void CreateAndClearTexture(RenderGraphContext rgContext, TextureHandle { 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); + CoreUtils.SetRenderTarget(rgContext.cmd, resource.resource, clearFlag, clearColor); } } - LogTextureCreation(resource.rt, resource.desc.clearBuffer || m_RenderGraphDebug.clearRenderTargetsAtCreation); + m_TexturePool.RegisterFrameAllocation(hashCode, resource.resource); + LogTextureCreation(resource.resource, resource.desc.clearBuffer || m_RenderGraphDebug.clearRenderTargetsAtCreation); } } - void CreateTextureForPass(ref TextureResource resource) + internal void CreateComputeBuffer(RenderGraphContext rgContext, int index) { - var desc = resource.desc; - int hashCode = desc.GetHashCode(); + var resource = m_Resources[(int)RenderGraphResourceType.ComputeBuffer][index] as ComputeBufferResource; + if (!resource.imported) + { + var desc = resource.desc; + 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 in the same pass.", resource.desc.name)); + if (resource.resource != null) + throw new InvalidOperationException(string.Format("Trying to create an already created Compute Buffer ({0}). Buffer was probably declared for writing more than once in the same pass.", resource.desc.name)); - resource.rt = null; - if (!TryGetRenderTarget(hashCode, out resource.rt)) - { - // Note: Name used here will be the one visible in the memory profiler so it means that whatever is the first pass that actually allocate the texture will set the name. - // TODO: Find a way to display name by pass. - switch (desc.sizeMode) + resource.resource = null; + if (!m_ComputeBufferPool.TryGetResource(hashCode, out resource.resource)) { - case TextureSizeMode.Explicit: - resource.rt = m_RTHandleSystem.Alloc(desc.width, desc.height, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite, - desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.msaaSamples, desc.bindTextureMS, desc.useDynamicScale, desc.memoryless, desc.name); - break; - case TextureSizeMode.Scale: - resource.rt = m_RTHandleSystem.Alloc(desc.scale, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite, - desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.enableMSAA, desc.bindTextureMS, desc.useDynamicScale, desc.memoryless, desc.name); - break; - case TextureSizeMode.Functor: - resource.rt = m_RTHandleSystem.Alloc(desc.func, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite, - desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.enableMSAA, desc.bindTextureMS, desc.useDynamicScale, desc.memoryless, desc.name); - break; + resource.resource = new ComputeBuffer(resource.desc.count, resource.desc.stride, resource.desc.type); + resource.resource.name = m_RenderGraphDebug.tagResourceNamesWithRG ? $"RenderGraph_{resource.desc.name}" : resource.desc.name; } - } + resource.cachedHash = hashCode; - //// Try to update name when re-using a texture. - //// TODO: Check if that actually works. - //resource.rt.name = desc.name; - -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (hashCode != -1) - { - m_FrameAllocatedTextures.Add((hashCode, resource.rt)); + m_ComputeBufferPool.RegisterFrameAllocation(hashCode, resource.resource); + LogComputeBufferCreation(resource.resource); } -#endif - - resource.cachedHash = hashCode; } - void SetGlobalTextures(RenderGraphContext rgContext, List textures, bool bindDummyTexture) + void SetGlobalTextures(RenderGraphContext rgContext, List textures, bool bindDummyTexture) { foreach (var resource in textures) { var resourceDesc = GetTextureResource(resource); if (resourceDesc.shaderProperty != 0) { - if (resourceDesc.rt != null) + if (resourceDesc.resource != null) { - rgContext.cmd.SetGlobalTexture(resourceDesc.shaderProperty, bindDummyTexture ? TextureXR.GetMagentaTexture() : resourceDesc.rt); + rgContext.cmd.SetGlobalTexture(resourceDesc.shaderProperty, bindDummyTexture ? TextureXR.GetMagentaTexture() : resourceDesc.resource); } } } } - internal void PreRenderPassSetGlobalTextures(RenderGraphContext rgContext, List textures) + internal void PreRenderPassSetGlobalTextures(RenderGraphContext rgContext, List textures) { SetGlobalTextures(rgContext, textures, false); } - internal void PostRenderPassUnbindGlobalTextures(RenderGraphContext rgContext, List textures) + internal void PostRenderPassUnbindGlobalTextures(RenderGraphContext rgContext, List textures) { SetGlobalTextures(rgContext, textures, true); } - internal void ReleaseTexture(RenderGraphContext rgContext, TextureHandle resource) + internal void ReleaseTexture(RenderGraphContext rgContext, int index) { - ref var resourceDesc = ref GetTextureResource(resource); - if (resourceDesc.rt == null) - throw new InvalidOperationException($"Tried to release a texture ({resourceDesc.desc.name}) that was never created. Check that there is at least one pass writing to it first."); + var resource = m_Resources[(int)RenderGraphResourceType.Texture][index] as TextureResource; - if (!resourceDesc.imported) + if (!resource.imported) { + if (resource.resource == null) + throw new InvalidOperationException($"Tried to release a texture ({resource.desc.name}) that was never created. Check that there is at least one pass writing to it first."); + if (m_RenderGraphDebug.clearRenderTargetsAtRelease) { 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 = resource.desc.depthBufferBits != DepthBits.None ? ClearFlag.Depth : ClearFlag.Color; + // Not ideal to do new TextureHandle here but GetTexture is a public API and we rather have it take an explicit TextureHandle parameters. + // Everywhere else internally int is better because it allows us to share more code. + CoreUtils.SetRenderTarget(rgContext.cmd, GetTexture(new TextureHandle(index)), clearFlag, Color.magenta); } } - LogTextureRelease(resourceDesc.rt); - ReleaseTextureResource(resourceDesc.cachedHash, resourceDesc.rt); - resourceDesc.cachedHash = -1; - resourceDesc.rt = null; - resourceDesc.wasReleased = true; + LogTextureRelease(resource.resource); + m_TexturePool.ReleaseResource(resource.cachedHash, resource.resource, m_CurrentFrameIndex); + m_TexturePool.UnregisterFrameAllocation(resource.cachedHash, resource.resource); + resource.cachedHash = -1; + resource.resource = null; + resource.wasReleased = true; } } - void ReleaseTextureResource(int hash, RTHandle rt) + internal void ReleaseComputeBuffer(RenderGraphContext rgContext, int index) { - m_TexturePool.ReleaseResource(hash, rt, m_CurrentFrameIndex); + var resource = m_Resources[(int)RenderGraphResourceType.ComputeBuffer][index] as ComputeBufferResource; -#if DEVELOPMENT_BUILD || UNITY_EDITOR - m_FrameAllocatedTextures.Remove((hash, rt)); -#endif + if (!resource.imported) + { + if (resource.resource == null) + throw new InvalidOperationException($"Tried to release a compute buffer ({resource.desc.name}) that was never created. Check that there is at least one pass writing to it first."); + + LogComputeBufferRelease(resource.resource); + m_ComputeBufferPool.ReleaseResource(resource.cachedHash, resource.resource, m_CurrentFrameIndex); + m_ComputeBufferPool.UnregisterFrameAllocation(resource.cachedHash, resource.resource); + resource.cachedHash = -1; + resource.resource = null; + resource.wasReleased = true; + } } void ValidateTextureDesc(in TextureDesc desc) @@ -830,9 +538,19 @@ void ValidateRendererListDesc(in RendererListDesc desc) #endif } - bool TryGetRenderTarget(int hashCode, out RTHandle rt) + void ValidateComputeBufferDesc(in ComputeBufferDesc desc) { - return m_TexturePool.TryGetResource(hashCode, out rt); +#if DEVELOPMENT_BUILD || UNITY_EDITOR + // TODO RENDERGRAPH: Check actual condition on stride. + if (desc.stride % 4 != 0) + { + throw new ArgumentException("Invalid Compute Buffer creation descriptor: Compute Buffer stride must be at least 4."); + } + if (desc.count == 0) + { + throw new ArgumentException("Invalid Compute Buffer creation descriptor: Compute Buffer count must be non zero."); + } +#endif } internal void CreateRendererLists(List rendererLists) @@ -852,34 +570,18 @@ internal void Clear(bool onException) { LogResources(); - m_TextureResources.Clear(); + for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) + m_Resources[i].Clear(); m_RendererListResources.Clear(); - m_ComputeBufferResources.Clear(); - -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_FrameAllocatedTextures.Count != 0 && !onException) - { - string logMessage = "RenderGraph: Not all textures were released. This can be caused by a textures being allocated but never read by any pass."; - - List<(int, RTHandle)> tempList = new List<(int, RTHandle)>(m_FrameAllocatedTextures); - foreach (var value in tempList) - { - logMessage = $"{logMessage}\n\t{value.Item2.name}"; - ReleaseTextureResource(value.Item1, value.Item2); - } - Debug.LogWarning(logMessage); - } - - // If an error occurred during execution, it's expected that textures are not all released so we clear the tracking list. - if (onException) - m_FrameAllocatedTextures.Clear(); -#endif + m_TexturePool.CheckFrameAllocation(onException, m_CurrentFrameIndex); + m_ComputeBufferPool.CheckFrameAllocation(onException, m_CurrentFrameIndex); // TODO RENDERGRAPH: Might not be ideal to purge stale resources every frame. // In case users enable/disable features along a level it might provoke performance spikes when things are reallocated... // Will be much better when we have actual resource aliasing and we can manage memory more efficiently. m_TexturePool.PurgeUnusedResources(m_CurrentFrameIndex); + m_ComputeBufferPool.PurgeUnusedResources(m_CurrentFrameIndex); } internal void ResetRTHandleReferenceSize(int width, int height) @@ -890,13 +592,14 @@ internal void ResetRTHandleReferenceSize(int width, int height) internal void Cleanup() { m_TexturePool.Cleanup(); + m_ComputeBufferPool.Cleanup(); } void LogTextureCreation(RTHandle rt, bool cleared) { if (m_RenderGraphDebug.logFrameInformation) { - m_Logger.LogLine("Created Texture: {0} (Cleared: {1})", rt.rt.name, cleared); + m_Logger.LogLine($"Created Texture: {rt.rt.name} (Cleared: {cleared})"); } } @@ -904,7 +607,23 @@ void LogTextureRelease(RTHandle rt) { if (m_RenderGraphDebug.logFrameInformation) { - m_Logger.LogLine("Released Texture: {0}", rt.rt.name); + m_Logger.LogLine($"Released Texture: {rt.rt.name}"); + } + } + + void LogComputeBufferCreation(ComputeBuffer buffer) + { + if (m_RenderGraphDebug.logFrameInformation) + { + m_Logger.LogLine($"Created ComputeBuffer: {buffer}"); + } + } + + void LogComputeBufferRelease(ComputeBuffer buffer) + { + if (m_RenderGraphDebug.logFrameInformation) + { + m_Logger.LogLine($"Released ComputeBuffer: {buffer}"); } } @@ -915,6 +634,7 @@ void LogResources() m_Logger.LogLine("==== Allocated Resources ====\n"); m_TexturePool.LogResources(m_Logger); + m_ComputeBufferPool.LogResources(m_Logger); } } diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResources.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResources.cs new file mode 100644 index 00000000000..73ee1dc8535 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResources.cs @@ -0,0 +1,372 @@ +using System.Diagnostics; +using UnityEngine.Rendering; + +namespace UnityEngine.Experimental.Rendering.RenderGraphModule +{ + // RendererList is a different case so not represented here. + internal enum RenderGraphResourceType + { + Texture = 0, + ComputeBuffer, + Count + } + + // Can't have a default constructor with handle = -1 hence the ugly IsValid implementation (where m_IsValid will be false by default). + internal struct ResourceHandle + { + bool m_IsValid; + + public int index { get; private set; } + public RenderGraphResourceType type { get; private set; } + public int iType { get { return (int)type; } } + + internal ResourceHandle(int value, RenderGraphResourceType type) + { + index = value; + this.type = type; + m_IsValid = true; + } + + public static implicit operator int(ResourceHandle handle) => handle.index; + public bool IsValid() => m_IsValid; + } + + // BEHOLD C# COPY PASTA + // Struct can't be inherited and can't have default member values or constructor + // Hence the copy paste and the ugly IsValid implementation. + + /// + /// Texture resource handle. + /// + [DebuggerDisplay("Texture ({handle})")] + public struct TextureHandle + { + internal ResourceHandle handle; + + internal TextureHandle(int handle) { this.handle = new ResourceHandle(handle, RenderGraphResourceType.Texture); } + + /// + /// Return true if the handle is valid. + /// + /// True if the handle is valid. + public bool IsValid() => handle.IsValid(); + } + + /// + /// Compute Buffer resource handle. + /// + [DebuggerDisplay("ComputeBuffer ({handle})")] + public struct ComputeBufferHandle + { + internal ResourceHandle handle; + + internal ComputeBufferHandle(int handle) { this.handle = new ResourceHandle(handle, RenderGraphResourceType.ComputeBuffer); } + + /// + /// Return true if the handle is valid. + /// + /// True if the handle is valid. + public bool IsValid() => handle.IsValid(); + } + + /// + /// Renderer List resource handle. + /// + [DebuggerDisplay("RendererList ({handle})")] + public struct RendererListHandle + { + bool m_IsValid; + internal int handle { get; private set; } + internal RendererListHandle(int handle) { this.handle = handle; m_IsValid = true; } + /// + /// Conversion to int. + /// + /// Renderer List handle to convert. + /// The integer representation of the handle. + public static implicit operator int(RendererListHandle handle) { return handle.handle; } + + /// + /// Return true if the handle is valid. + /// + /// True if the handle is valid. + public bool IsValid() => m_IsValid; + } + + /// + /// The mode that determines the size of a Texture. + /// + public enum TextureSizeMode + { + ///Explicit size. + Explicit, + ///Size automatically scaled by a Vector. + Scale, + ///Size automatically scaled by a Functor. + Functor + } + +#if UNITY_2020_2_OR_NEWER + /// + /// Subset of the texture desc containing information for fast memory allocation (when platform supports it) + /// + public struct FastMemoryDesc + { + ///Whether the texture will be in fast memory. + public bool inFastMemory; + ///Flag to determine what parts of the render target is spilled if not fully resident in fast memory. + public FastMemoryFlags flags; + ///How much of the render target is to be switched into fast memory (between 0 and 1). + public float residencyFraction; + } +#endif + + /// + /// Descriptor used to create texture resources + /// + public struct TextureDesc + { + ///Texture sizing mode. + public TextureSizeMode sizeMode; + ///Texture width. + public int width; + ///Texture height. + public int height; + ///Number of texture slices.. + public int slices; + ///Texture scale. + public Vector2 scale; + ///Texture scale function. + public ScaleFunc func; + ///Depth buffer bit depth. + public DepthBits depthBufferBits; + ///Color format. + public GraphicsFormat colorFormat; + ///Filtering mode. + public FilterMode filterMode; + ///Addressing mode. + public TextureWrapMode wrapMode; + ///Texture dimension. + public TextureDimension dimension; + ///Enable random UAV read/write on the texture. + public bool enableRandomWrite; + ///Texture needs mip maps. + public bool useMipMap; + ///Automatically generate mip maps. + public bool autoGenerateMips; + ///Texture is a shadow map. + public bool isShadowMap; + ///Anisotropic filtering level. + public int anisoLevel; + ///Mip map bias. + public float mipMapBias; + ///Textre is multisampled. Only supported for Scale and Functor size mode. + public bool enableMSAA; + ///Number of MSAA samples. Only supported for Explicit size mode. + public MSAASamples msaaSamples; + ///Bind texture multi sampled. + public bool bindTextureMS; + ///Texture uses dynamic scaling. + public bool useDynamicScale; + ///Memory less flag. + public RenderTextureMemoryless memoryless; + ///Texture name. + public string name; +#if UNITY_2020_2_OR_NEWER + ///Descriptor to determine how the texture will be in fast memory on platform that supports it. + public FastMemoryDesc fastMemoryDesc; +#endif + + // Initial state. Those should not be used in the hash + ///Texture needs to be cleared on first use. + public bool clearBuffer; + ///Clear color. + public Color clearColor; + + void InitDefaultValues(bool dynamicResolution, bool xrReady) + { + useDynamicScale = dynamicResolution; + // XR Ready + if (xrReady) + { + slices = TextureXR.slices; + dimension = TextureXR.dimension; + } + else + { + slices = 1; + dimension = TextureDimension.Tex2D; + } + } + + /// + /// TextureDesc constructor for a texture using explicit size + /// + /// Texture width + /// Texture height + /// Use dynamic resolution + /// Set this to true if the Texture is a render texture in an XR setting. + public TextureDesc(int width, int height, bool dynamicResolution = false, bool xrReady = false) + : this() + { + // Size related init + sizeMode = TextureSizeMode.Explicit; + this.width = width; + this.height = height; + // Important default values not handled by zero construction in this() + msaaSamples = MSAASamples.None; + InitDefaultValues(dynamicResolution, xrReady); + } + + /// + /// TextureDesc constructor for a texture using a fixed scaling + /// + /// RTHandle scale used for this texture + /// Use dynamic resolution + /// Set this to true if the Texture is a render texture in an XR setting. + public TextureDesc(Vector2 scale, bool dynamicResolution = false, bool xrReady = false) + : this() + { + // Size related init + sizeMode = TextureSizeMode.Scale; + this.scale = scale; + // Important default values not handled by zero construction in this() + msaaSamples = MSAASamples.None; + dimension = TextureDimension.Tex2D; + InitDefaultValues(dynamicResolution, xrReady); + } + + /// + /// TextureDesc constructor for a texture using a functor for scaling + /// + /// Function used to determnine the texture size + /// Use dynamic resolution + /// Set this to true if the Texture is a render texture in an XR setting. + public TextureDesc(ScaleFunc func, bool dynamicResolution = false, bool xrReady = false) + : this() + { + // Size related init + sizeMode = TextureSizeMode.Functor; + this.func = func; + // Important default values not handled by zero construction in this() + msaaSamples = MSAASamples.None; + dimension = TextureDimension.Tex2D; + InitDefaultValues(dynamicResolution, xrReady); + } + + /// + /// Copy constructor + /// + /// + public TextureDesc(TextureDesc input) + { + this = input; + } + + /// + /// Hash function + /// + /// The texture descriptor hash. + public override int GetHashCode() + { + int hashCode = 17; + + unchecked + { + switch (sizeMode) + { + case TextureSizeMode.Explicit: + hashCode = hashCode * 23 + width; + hashCode = hashCode * 23 + height; + hashCode = hashCode * 23 + (int)msaaSamples; + break; + case TextureSizeMode.Functor: + if (func != null) + hashCode = hashCode * 23 + func.GetHashCode(); + hashCode = hashCode * 23 + (enableMSAA ? 1 : 0); + break; + case TextureSizeMode.Scale: + hashCode = hashCode * 23 + scale.x.GetHashCode(); + hashCode = hashCode * 23 + scale.y.GetHashCode(); + hashCode = hashCode * 23 + (enableMSAA ? 1 : 0); + break; + } + + hashCode = hashCode * 23 + mipMapBias.GetHashCode(); + hashCode = hashCode * 23 + slices; + hashCode = hashCode * 23 + (int)depthBufferBits; + hashCode = hashCode * 23 + (int)colorFormat; + hashCode = hashCode * 23 + (int)filterMode; + hashCode = hashCode * 23 + (int)wrapMode; + hashCode = hashCode * 23 + (int)dimension; + hashCode = hashCode * 23 + (int)memoryless; + hashCode = hashCode * 23 + anisoLevel; + hashCode = hashCode * 23 + (enableRandomWrite ? 1 : 0); + hashCode = hashCode * 23 + (useMipMap ? 1 : 0); + hashCode = hashCode * 23 + (autoGenerateMips ? 1 : 0); + hashCode = hashCode * 23 + (isShadowMap ? 1 : 0); + hashCode = hashCode * 23 + (bindTextureMS ? 1 : 0); + hashCode = hashCode * 23 + (useDynamicScale ? 1 : 0); + } + + return hashCode; + } + } + + /// + /// Descriptor used to create compute buffer resources + /// + public struct ComputeBufferDesc + { + ///Number of elements in the buffer.. + public int count; + ///Size of one element in the buffer. Has to match size of buffer type in the shader. + public int stride; + ///Type of the buffer, default is ComputeBufferType.Default (structured buffer). + public ComputeBufferType type; + /// Compute Buffer name. + public string name; + + /// + /// ComputeBufferDesc constructor. + /// + /// Number of elements in the buffer. + /// Size of one element in the buffer. + public ComputeBufferDesc(int count, int stride) + : this() + { + this.count = count; + this.stride = stride; + type = ComputeBufferType.Default; + } + + /// + /// ComputeBufferDesc constructor. + /// + /// Number of elements in the buffer. + /// Size of one element in the buffer. + /// Type of the buffer. + public ComputeBufferDesc(int count, int stride, ComputeBufferType type) + : this() + { + this.count = count; + this.stride = stride; + this.type = type; + } + + /// + /// Hash function + /// + /// The texture descriptor hash. + public override int GetHashCode() + { + int hashCode = 17; + + hashCode = hashCode * 23 + count; + hashCode = hashCode * 23 + stride; + hashCode = hashCode * 23 + (int)type; + + return hashCode; + } + } + +} diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResources.cs.meta b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResources.cs.meta new file mode 100644 index 00000000000..4ddcdca9fb0 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResources.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b7ac9a635582ab4998d4179483bacc5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs b/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs index 65f1c853130..ab01a3f3542 100644 --- a/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs +++ b/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs @@ -284,8 +284,8 @@ public void SimpleCreateReleaseTexture() var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); Assert.AreEqual(4, compiledPasses.size); - Assert.Contains(texture, compiledPasses[0].textureCreateList); - Assert.Contains(texture, compiledPasses[3].textureReleaseList); + Assert.Contains(texture.handle.index, compiledPasses[0].resourceCreateList[(int)RenderGraphResourceType.Texture]); + Assert.Contains(texture.handle.index, compiledPasses[3].resourceReleaseList[(int)RenderGraphResourceType.Texture]); } [Test] @@ -326,8 +326,8 @@ public void TransientCreateReleaseInSamePass() var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); Assert.AreEqual(1, compiledPasses.size); - Assert.Contains(texture, compiledPasses[0].textureCreateList); - Assert.Contains(texture, compiledPasses[0].textureReleaseList); + Assert.Contains(texture.handle.index, compiledPasses[0].resourceCreateList[(int)RenderGraphResourceType.Texture]); + Assert.Contains(texture.handle.index, compiledPasses[0].resourceReleaseList[(int)RenderGraphResourceType.Texture]); } // Texture that should be released during an async pass should have their release delayed until the first pass that syncs with the compute pipe. @@ -396,8 +396,8 @@ public void AsyncPassReleaseTextureOnGraphicsPipe() var compiledPasses = m_RenderGraph.GetCompiledPassInfos(); Assert.AreEqual(6, compiledPasses.size); - Assert.Contains(texture0, compiledPasses[4].textureReleaseList); - Assert.Contains(texture2, compiledPasses[4].textureReleaseList); + Assert.Contains(texture0.handle.index, compiledPasses[4].resourceReleaseList[(int)RenderGraphResourceType.Texture]); + Assert.Contains(texture2.handle.index, compiledPasses[4].resourceReleaseList[(int)RenderGraphResourceType.Texture]); } [Test] diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs index 0fefe0b3611..d8a69720218 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs @@ -379,6 +379,8 @@ public void Cleanup() } } + // TODO RENDERGRAPH: When we remove the old pass, we need to remove/refactor this class + // With render graph it's only useful for 2 buffers and a boolean value. class TileAndClusterData { // Internal to light list building @@ -412,7 +414,7 @@ public void Initialize(bool allocateTileBuffers, bool clusterNeedsDepth) globalLightListAtomic = new ComputeBuffer(1, sizeof(uint)); } - public void AllocateResolutionDependentBuffers(HDCamera hdCamera, int width, int height, int viewCount, int maxLightOnScreen) + public void AllocateNonRenderGraphResolutionDependentBuffers(HDCamera hdCamera, int width, int height, int viewCount, int maxLightOnScreen) { if (hasTileBuffers) { @@ -459,32 +461,60 @@ public void AllocateResolutionDependentBuffers(HDCamera hdCamera, int width, int // The bounds and light volumes are view-dependent, and AABB is additionally projection dependent. AABBBoundsBuffer = new ComputeBuffer(viewCount * 2 * maxLightOnScreen, 4 * sizeof(float)); + + // Make sure to invalidate the content of the buffers + listsAreClear = false; + } + + + public void AllocateResolutionDependentBuffers(HDCamera hdCamera, int width, int height, int viewCount, int maxLightOnScreen, bool renderGraphEnabled) + { convexBoundsBuffer = new ComputeBuffer(viewCount * maxLightOnScreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(SFiniteLightBound))); lightVolumeDataBuffer = new ComputeBuffer(viewCount * maxLightOnScreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(LightVolumeData))); + if (!renderGraphEnabled) + AllocateNonRenderGraphResolutionDependentBuffers(hdCamera, width, height, viewCount, maxLightOnScreen); + // Make sure to invalidate the content of the buffers listsAreClear = false; } - public void ReleaseResolutionDependentBuffers() + public void ReleaseNonRenderGraphResolutionDependentBuffers() { CoreUtils.SafeRelease(lightList); CoreUtils.SafeRelease(tileList); CoreUtils.SafeRelease(tileFeatureFlags); + lightList = null; + tileList = null; + tileFeatureFlags = null; // enableClustered CoreUtils.SafeRelease(perVoxelLightLists); CoreUtils.SafeRelease(perVoxelOffset); CoreUtils.SafeRelease(perTileLogBaseTweak); + perVoxelLightLists = null; + perVoxelOffset = null; + perTileLogBaseTweak = null; // enableBigTilePrepass CoreUtils.SafeRelease(bigTileLightList); + bigTileLightList = null; // LightList building CoreUtils.SafeRelease(AABBBoundsBuffer); + CoreUtils.SafeRelease(dispatchIndirectBuffer); + AABBBoundsBuffer = null; + dispatchIndirectBuffer = null; + } + + public void ReleaseResolutionDependentBuffers() + { CoreUtils.SafeRelease(convexBoundsBuffer); CoreUtils.SafeRelease(lightVolumeDataBuffer); - CoreUtils.SafeRelease(dispatchIndirectBuffer); + convexBoundsBuffer = null; + lightVolumeDataBuffer = null; + + ReleaseNonRenderGraphResolutionDependentBuffers(); } public void Cleanup() @@ -1041,23 +1071,30 @@ void LightLoopNewFrame(CommandBuffer cmd, HDCamera hdCamera) } } - void LightLoopReleaseResolutionDependentBuffers() + static int NumLightIndicesPerClusteredTile() { - m_TileAndClusterData.ReleaseResolutionDependentBuffers(); + return 32 * (1 << k_Log2NumClusters); // total footprint for all layers of the tile (measured in light index entries) + } + + void LightLoopAllocResolutionDependentBuffers(HDCamera hdCamera, int width, int height) + { + m_TileAndClusterData.AllocateResolutionDependentBuffers(hdCamera, width, height, m_MaxViewCount, m_MaxLightsOnScreen, m_EnableRenderGraph); if (m_ProbeVolumeClusterData != null) - m_ProbeVolumeClusterData.ReleaseResolutionDependentBuffers(); + m_ProbeVolumeClusterData.AllocateResolutionDependentBuffers(hdCamera, width, height, m_MaxViewCount, k_MaxVisibleProbeVolumeCount, m_EnableRenderGraph); } - static int NumLightIndicesPerClusteredTile() + void LightLoopReleaseResolutionDependentBuffers() { - return 32 * (1 << k_Log2NumClusters); // total footprint for all layers of the tile (measured in light index entries) + m_TileAndClusterData.ReleaseResolutionDependentBuffers(); + if (m_ProbeVolumeClusterData != null) + m_ProbeVolumeClusterData.ReleaseResolutionDependentBuffers(); } - void LightLoopAllocResolutionDependentBuffers(HDCamera hdCamera, int width, int height) + void LightLoopCleanupNonRenderGraphResources() { - m_TileAndClusterData.AllocateResolutionDependentBuffers(hdCamera, width, height, m_MaxViewCount, m_MaxLightsOnScreen); + m_TileAndClusterData.ReleaseNonRenderGraphResolutionDependentBuffers(); if (m_ProbeVolumeClusterData != null) - m_ProbeVolumeClusterData.AllocateResolutionDependentBuffers(hdCamera, width, height, m_MaxViewCount, k_MaxVisibleProbeVolumeCount); + m_ProbeVolumeClusterData.ReleaseNonRenderGraphResolutionDependentBuffers(); } internal static Matrix4x4 WorldToCamera(Camera camera) @@ -1883,7 +1920,7 @@ internal bool GetEnvLightData(CommandBuffer cmd, HDCamera hdCamera, in Processed } atlasScaleOffset = scaleOffset; - + m_TextureCaches.env2DAtlasScaleOffset[fetchIndex] = scaleOffset; m_TextureCaches.env2DCaptureVP[fetchIndex] = vp; @@ -2748,7 +2785,7 @@ bool PrepareLightsForGPU(CommandBuffer cmd, HDCamera hdCamera, CullingResults cu } else { - + m_lightList.lightsPerView[viewIndex].lightVolumes.Add(volumeData); m_lightList.lightsPerView[viewIndex].bounds.Add(bound); } @@ -3297,6 +3334,11 @@ unsafe BuildGPULightListParameters PrepareBuildGPULightListParameters( HDCamera parameters.runLightList = parameters.totalLightCount > 0; parameters.clearLightLists = false; + // TODO RENDERGRAPH: This logic is flawed with Render Graph. + // In theory buffers memory might be reused from another usage entirely so keeping track of its "cleared" state does not represent the truth of their content. + // In practice though, when resolution stays the same, buffers will be the same reused from one frame to another + // because for now buffers are pooled based on their parameters. When we do proper aliasing though, we might end up with any random chunk of memory. + // Always build the light list in XR mode to avoid issues with multi-pass if (hdCamera.xr.enabled) { @@ -3314,7 +3356,7 @@ unsafe BuildGPULightListParameters PrepareBuildGPULightListParameters( HDCamera } parameters.viewCount = hdCamera.viewCount; - parameters.enableFeatureVariants = GetFeatureVariantsEnabled(hdCamera.frameSettings); + parameters.enableFeatureVariants = GetFeatureVariantsEnabled(hdCamera.frameSettings) && tileAndClusterData.hasTileBuffers; parameters.computeMaterialVariants = hdCamera.frameSettings.IsEnabled(FrameSettingsField.ComputeMaterialVariants); parameters.computeLightVariants = hdCamera.frameSettings.IsEnabled(FrameSettingsField.ComputeLightVariants); parameters.lightList = m_lightList; @@ -3345,7 +3387,7 @@ unsafe BuildGPULightListParameters PrepareBuildGPULightListParameters( HDCamera parameters.numBigTilesY = (h + 63) / 64; // Fptl - parameters.runFPTL = hdCamera.frameSettings.fptl; + parameters.runFPTL = hdCamera.frameSettings.fptl && tileAndClusterData.hasTileBuffers; parameters.buildPerTileLightListShader = buildPerTileLightListShader; parameters.buildPerTileLightListShader.shaderKeywords = null; if (hdCamera.frameSettings.IsEnabled(FrameSettingsField.BigTilePrepass)) @@ -4057,15 +4099,14 @@ static void RenderPixelDeferredLighting(in DeferredLightingParameters parameters struct LightLoopDebugOverlayParameters { - public Material debugViewTilesMaterial; - public TileAndClusterData tileAndClusterData; - public HDShadowManager shadowManager; - public int debugSelectedLightShadowIndex; - public int debugSelectedLightShadowCount; - public Material debugShadowMapMaterial; - public Material debugBlitMaterial; - public LightCookieManager cookieManager; - public PlanarReflectionProbeCache planarProbeCache; + public Material debugViewTilesMaterial; + public HDShadowManager shadowManager; + public int debugSelectedLightShadowIndex; + public int debugSelectedLightShadowCount; + public Material debugShadowMapMaterial; + public Material debugBlitMaterial; + public LightCookieManager cookieManager; + public PlanarReflectionProbeCache planarProbeCache; } LightLoopDebugOverlayParameters PrepareLightLoopDebugOverlayParameters() @@ -4073,7 +4114,6 @@ LightLoopDebugOverlayParameters PrepareLightLoopDebugOverlayParameters() var parameters = new LightLoopDebugOverlayParameters(); parameters.debugViewTilesMaterial = m_DebugViewTilesMaterial; - parameters.tileAndClusterData = m_TileAndClusterData; parameters.shadowManager = m_ShadowManager; parameters.debugSelectedLightShadowIndex = m_DebugSelectedLightShadowIndex; parameters.debugSelectedLightShadowCount = m_DebugSelectedLightShadowCount; @@ -4085,7 +4125,16 @@ LightLoopDebugOverlayParameters PrepareLightLoopDebugOverlayParameters() return parameters; } - static void RenderLightLoopDebugOverlay(in DebugParameters debugParameters, CommandBuffer cmd, ref float x, ref float y, float overlaySize, RTHandle depthTexture) + static void RenderLightLoopDebugOverlay(in DebugParameters debugParameters, + CommandBuffer cmd, + ref float x, + ref float y, + float overlaySize, + ComputeBuffer tileBuffer, + ComputeBuffer lightListBuffer, + ComputeBuffer perVoxelLightListBuffer, + ComputeBuffer dispatchIndirectBuffer, + RTHandle depthTexture) { var hdCamera = debugParameters.hdCamera; var parameters = debugParameters.lightingOverlayParameters; @@ -4111,8 +4160,8 @@ static void RenderLightLoopDebugOverlay(in DebugParameters debugParameters, Comm parameters.debugViewTilesMaterial.SetInt(HDShaderIDs._ViewTilesFlags, (int)lightingDebug.tileClusterDebugByCategory); parameters.debugViewTilesMaterial.SetVector(HDShaderIDs._MousePixelCoord, HDUtils.GetMouseCoordinates(hdCamera)); parameters.debugViewTilesMaterial.SetVector(HDShaderIDs._MouseClickPixelCoord, HDUtils.GetMouseClickCoordinates(hdCamera)); - parameters.debugViewTilesMaterial.SetBuffer(HDShaderIDs.g_TileList, parameters.tileAndClusterData.tileList); - parameters.debugViewTilesMaterial.SetBuffer(HDShaderIDs.g_DispatchIndirectBuffer, parameters.tileAndClusterData.dispatchIndirectBuffer); + parameters.debugViewTilesMaterial.SetBuffer(HDShaderIDs.g_TileList, tileBuffer); + parameters.debugViewTilesMaterial.SetBuffer(HDShaderIDs.g_DispatchIndirectBuffer, dispatchIndirectBuffer); parameters.debugViewTilesMaterial.EnableKeyword("USE_FPTL_LIGHTLIST"); parameters.debugViewTilesMaterial.DisableKeyword("USE_CLUSTERED_LIGHTLIST"); parameters.debugViewTilesMaterial.DisableKeyword("SHOW_LIGHT_CATEGORIES"); @@ -4132,7 +4181,7 @@ static void RenderLightLoopDebugOverlay(in DebugParameters debugParameters, Comm parameters.debugViewTilesMaterial.SetInt(HDShaderIDs._ViewTilesFlags, (int)lightingDebug.tileClusterDebugByCategory); parameters.debugViewTilesMaterial.SetVector(HDShaderIDs._MousePixelCoord, HDUtils.GetMouseCoordinates(hdCamera)); parameters.debugViewTilesMaterial.SetVector(HDShaderIDs._MouseClickPixelCoord, HDUtils.GetMouseClickCoordinates(hdCamera)); - parameters.debugViewTilesMaterial.SetBuffer(HDShaderIDs.g_vLightListGlobal, bUseClustered ? parameters.tileAndClusterData.perVoxelLightLists : parameters.tileAndClusterData.lightList); + parameters.debugViewTilesMaterial.SetBuffer(HDShaderIDs.g_vLightListGlobal, bUseClustered ? perVoxelLightListBuffer : lightListBuffer); parameters.debugViewTilesMaterial.SetTexture(HDShaderIDs._CameraDepthTexture, depthTexture); parameters.debugViewTilesMaterial.EnableKeyword(bUseClustered ? "USE_CLUSTERED_LIGHTLIST" : "USE_FPTL_LIGHTLIST"); parameters.debugViewTilesMaterial.DisableKeyword(!bUseClustered ? "USE_CLUSTERED_LIGHTLIST" : "USE_FPTL_LIGHTLIST"); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DBufferManager.cs b/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DBufferManager.cs index e9bcf7e2ea9..5aec9fdae0c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DBufferManager.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DBufferManager.cs @@ -5,7 +5,6 @@ namespace UnityEngine.Rendering.HighDefinition class DBufferManager : MRTBufferManager { ComputeBuffer m_PropertyMaskBuffer; - int m_PropertyMaskBufferSize; ComputeShader m_ClearPropertyMaskBufferShader; int m_ClearPropertyMaskBufferKernel; @@ -42,12 +41,11 @@ public ComputeShader clearPropertyMaskBufferShader } } - public int propertyMaskBufferSize + public int GetPropertyMaskBufferSize(int width, int height) { - get - { - return m_PropertyMaskBufferSize; - } + int propertyMaskBufferSize = ((width + 7) / 8) * ((height + 7) / 8); + propertyMaskBufferSize = ((propertyMaskBufferSize + 63) / 64) * 64; + return propertyMaskBufferSize; } public override void CreateBuffers() @@ -78,11 +76,9 @@ public void ReleaseResolutionDependentBuffers() } } - public void AllocResolutionDependentBuffers(HDCamera hdCamera, int width, int height) + public void AllocResolutionDependentBuffers(int width, int height) { - m_PropertyMaskBufferSize = ((width + 7) / 8) * ((height + 7) / 8); - m_PropertyMaskBufferSize = ((m_PropertyMaskBufferSize + 63) / 64) * 64; // round off to nearest multiple of 64 for ease of use in CS - m_PropertyMaskBuffer = new ComputeBuffer(m_PropertyMaskBufferSize, 4); + m_PropertyMaskBuffer = new ComputeBuffer(GetPropertyMaskBufferSize(width, height), 4); } override public void DestroyBuffers() diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/SubsurfaceScattering/SubsurfaceScatteringManager.cs b/com.unity.render-pipelines.high-definition/Runtime/Material/SubsurfaceScattering/SubsurfaceScatteringManager.cs index 250732fcf0e..422795362e6 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/SubsurfaceScattering/SubsurfaceScatteringManager.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/SubsurfaceScattering/SubsurfaceScatteringManager.cs @@ -223,8 +223,6 @@ struct SubsurfaceScatteringParameters public int numTilesX; public int numTilesY; public int numTilesZ; - public ComputeBuffer coarseStencilBuffer; - } struct SubsurfaceScatteringResources @@ -258,7 +256,6 @@ SubsurfaceScatteringParameters PrepareSubsurfaceScatteringParameters(HDCamera hd parameters.numTilesX = ((int)hdCamera.screenSize.x + 15) / 16; parameters.numTilesY = ((int)hdCamera.screenSize.y + 15) / 16; parameters.numTilesZ = hdCamera.viewCount; - parameters.coarseStencilBuffer = m_SharedRTManager.GetCoarseStencilBuffer(); parameters.sampleBudget = hdCamera.frameSettings.sssResolvedSampleBudget; return parameters; @@ -418,7 +415,7 @@ void RenderSubsurfaceScattering(HDCamera hdCamera, CommandBuffer cmd, RTHandle c resources.depthStencilBuffer = depthStencilBufferRT; resources.depthTexture = depthTextureRT; resources.cameraFilteringBuffer = m_SSSCameraFilteringBuffer; - resources.coarseStencilBuffer = parameters.coarseStencilBuffer; + resources.coarseStencilBuffer = m_SharedRTManager.GetCoarseStencilBuffer(); resources.sssBuffer = m_SSSColor; // For Jimenez we always need an extra buffer, for Disney it depends on platform 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 57f0b89069c..6ddde304438 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 @@ -75,15 +75,20 @@ class RenderDebugOverlayPassData public TextureHandle colorBuffer; public TextureHandle depthBuffer; public TextureHandle depthPyramidTexture; + public ComputeBufferHandle tileList; + public ComputeBufferHandle lightList; + public ComputeBufferHandle perVoxelLightList; + public ComputeBufferHandle dispatchIndirect; public ShadowResult shadowTextures; } - void RenderDebugOverlays( RenderGraph renderGraph, - in DebugParameters debugParameters, - TextureHandle colorBuffer, - TextureHandle depthBuffer, - TextureHandle depthPyramidTexture, - in ShadowResult shadowResult) + void RenderDebugOverlays( RenderGraph renderGraph, + in DebugParameters debugParameters, + TextureHandle colorBuffer, + TextureHandle depthBuffer, + TextureHandle depthPyramidTexture, + in BuildGPULightListOutput lightLists, + in ShadowResult shadowResult) { using (var builder = renderGraph.AddRenderPass("DebugOverlay", out var passData)) { @@ -92,6 +97,10 @@ void RenderDebugOverlays( RenderGraph renderGraph, passData.depthBuffer = builder.UseDepthBuffer(depthBuffer, DepthAccess.ReadWrite); passData.depthPyramidTexture = builder.ReadTexture(depthPyramidTexture); passData.shadowTextures = HDShadowManager.ReadShadowResult(shadowResult, builder); + passData.tileList = builder.ReadComputeBuffer(lightLists.tileList); + passData.lightList = builder.ReadComputeBuffer(lightLists.lightList); + passData.perVoxelLightList = builder.ReadComputeBuffer(lightLists.perVoxelLightLists); + passData.dispatchIndirect = builder.ReadComputeBuffer(lightLists.dispatchIndirectBuffer); builder.SetRenderFunc( (RenderDebugOverlayPassData data, RenderGraphContext ctx) => @@ -111,9 +120,14 @@ void RenderDebugOverlays( RenderGraph renderGraph, shadowAtlases.cachedPunctualShadowAtlas = data.shadowTextures.cachedPunctualShadowResult.IsValid() ? ctx.resources.GetTexture(data.shadowTextures.cachedPunctualShadowResult) : null; shadowAtlases.cachedAreaShadowAtlas = data.shadowTextures.cachedAreaShadowResult.IsValid() ? ctx.resources.GetTexture(data.shadowTextures.cachedAreaShadowResult) : null; + ComputeBuffer tileBuffer = ctx.resources.GetComputeBuffer(data.tileList); + ComputeBuffer lightListBuffer = ctx.resources.GetComputeBuffer(data.lightList); + ComputeBuffer perVoxelLightListBuffer = ctx.resources.GetComputeBuffer(data.perVoxelLightList); + ComputeBuffer dispatchIndirectBuffer = ctx.resources.GetComputeBuffer(data.dispatchIndirect); + RenderSkyReflectionOverlay(debugParams, ctx.cmd, ctx.renderGraphPool.GetTempMaterialPropertyBlock(), ref x, ref y, overlaySize); RenderRayCountOverlay(debugParams, ctx.cmd, ref x, ref y, overlaySize); - RenderLightLoopDebugOverlay(debugParams, ctx.cmd, ref x, ref y, overlaySize, ctx.resources.GetTexture(data.depthPyramidTexture)); + RenderLightLoopDebugOverlay(debugParams, ctx.cmd, ref x, ref y, overlaySize, tileBuffer, lightListBuffer, perVoxelLightListBuffer, dispatchIndirectBuffer, ctx.resources.GetTexture(data.depthPyramidTexture)); RenderShadowsDebugOverlay(debugParams, shadowAtlases, ctx.cmd, ref x, ref y, overlaySize, ctx.renderGraphPool.GetTempMaterialPropertyBlock()); DecalSystem.instance.RenderDebugOverlay(debugParams.hdCamera, ctx.cmd, debugParams.debugDisplaySettings, ref x, ref y, overlaySize, debugParams.hdCamera.actualWidth); }); @@ -169,15 +183,16 @@ static void RenderLightVolumes(RenderGraph renderGraph, in DebugParameters debug } } - TextureHandle RenderDebug( RenderGraph renderGraph, - HDCamera hdCamera, - TextureHandle colorBuffer, - TextureHandle depthBuffer, - TextureHandle depthPyramidTexture, - TextureHandle fullScreenDebugTexture, - TextureHandle colorPickerDebugTexture, - in ShadowResult shadowResult, - CullingResults cullResults) + TextureHandle RenderDebug( RenderGraph renderGraph, + HDCamera hdCamera, + TextureHandle colorBuffer, + TextureHandle depthBuffer, + TextureHandle depthPyramidTexture, + TextureHandle fullScreenDebugTexture, + TextureHandle colorPickerDebugTexture, + in BuildGPULightListOutput lightLists, + in ShadowResult shadowResult, + CullingResults cullResults) { // We don't want any overlay for these kind of rendering if (hdCamera.camera.cameraType == CameraType.Reflection || hdCamera.camera.cameraType == CameraType.Preview) @@ -212,7 +227,7 @@ TextureHandle RenderDebug( RenderGraph renderGraph, RenderLightVolumes(renderGraph, debugParameters, output, depthBuffer, cullResults); } - RenderDebugOverlays(renderGraph, debugParameters, output, depthBuffer, depthPyramidTexture, shadowResult); + RenderDebugOverlays(renderGraph, debugParameters, output, depthBuffer, depthPyramidTexture, lightLists, shadowResult); return output; } 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 2caf386b69a..9c09f71213b 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 @@ -27,33 +27,38 @@ static void ReadLightingBuffers(LightingBuffers buffers, RenderGraphBuilder buil class BuildGPULightListPassData { - public LightLoopGlobalParameters lightLoopGlobalParameters; - public BuildGPULightListParameters buildGPULightListParameters; public TextureHandle depthBuffer; public TextureHandle stencilTexture; public TextureHandle[] gBuffer = new TextureHandle[RenderGraph.kMaxMRTCount]; public int gBufferCount; - // These buffers are not used outside of BuildGPULight list so they don't need to be known by the render graph. - public ComputeBuffer lightVolumeDataBuffer; - public ComputeBuffer convexBoundsBuffer; - public ComputeBuffer AABBBoundsBuffer; - public ComputeBuffer globalLightListAtomic; + // Buffers filled with the CPU outside of render graph. + public ComputeBufferHandle convexBoundsBuffer; + public ComputeBufferHandle AABBBoundsBuffer; + + // Transient buffers that are not used outside of BuildGPULight list so they don't need to go outside the pass. + public ComputeBufferHandle globalLightListAtomic; + public ComputeBufferHandle lightVolumeDataBuffer; public BuildGPULightListOutput output = new BuildGPULightListOutput(); } struct BuildGPULightListOutput { + // Tile + public ComputeBufferHandle lightList; + public ComputeBufferHandle tileList; public ComputeBufferHandle tileFeatureFlags; public ComputeBufferHandle dispatchIndirectBuffer; - public ComputeBufferHandle perVoxelOffset; - public ComputeBufferHandle perTileLogBaseTweak; - public ComputeBufferHandle tileList; + + // Big Tile public ComputeBufferHandle bigTileLightList; + + // Cluster + public ComputeBufferHandle perVoxelOffset; public ComputeBufferHandle perVoxelLightLists; - public ComputeBufferHandle lightList; + public ComputeBufferHandle perTileLogBaseTweak; } static BuildGPULightListResources PrepareBuildGPULightListResources(RenderGraphContext context, BuildGPULightListPassData data) @@ -69,10 +74,10 @@ static BuildGPULightListResources PrepareBuildGPULightListResources(RenderGraphC buildLightListResources.gBuffer[i] = context.resources.GetTexture(data.gBuffer[i]); } - buildLightListResources.lightVolumeDataBuffer = data.lightVolumeDataBuffer; - buildLightListResources.convexBoundsBuffer = data.convexBoundsBuffer; - buildLightListResources.AABBBoundsBuffer = data.AABBBoundsBuffer; - buildLightListResources.globalLightListAtomic = data.globalLightListAtomic; + buildLightListResources.lightVolumeDataBuffer = context.resources.GetComputeBuffer(data.lightVolumeDataBuffer); + buildLightListResources.convexBoundsBuffer = context.resources.GetComputeBuffer(data.convexBoundsBuffer); + buildLightListResources.AABBBoundsBuffer = context.resources.GetComputeBuffer(data.AABBBoundsBuffer); + buildLightListResources.globalLightListAtomic = context.resources.GetComputeBuffer(data.globalLightListAtomic); buildLightListResources.tileFeatureFlags = context.resources.GetComputeBuffer(data.output.tileFeatureFlags); buildLightListResources.dispatchIndirectBuffer = context.resources.GetComputeBuffer(data.output.dispatchIndirectBuffer); @@ -86,14 +91,21 @@ static BuildGPULightListResources PrepareBuildGPULightListResources(RenderGraphC return buildLightListResources; } - BuildGPULightListOutput BuildGPULightList(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle depthStencilBuffer, TextureHandle stencilBufferCopy, GBufferOutput gBuffer) + BuildGPULightListOutput BuildGPULightList( RenderGraph renderGraph, + HDCamera hdCamera, + TileAndClusterData tileAndClusterData, + int totalLightCount, + int maxLightOnScreen, + ref ShaderVariablesLightList constantBuffer, + TextureHandle depthStencilBuffer, + TextureHandle stencilBufferCopy, + GBufferOutput gBuffer) { using (var builder = renderGraph.AddRenderPass("Build Light List", out var passData, ProfilingSampler.Get(HDProfileId.BuildLightList))) { builder.EnableAsyncCompute(hdCamera.frameSettings.BuildLightListRunsAsync()); - passData.lightLoopGlobalParameters = PrepareLightLoopGlobalParameters(hdCamera, m_TileAndClusterData); - passData.buildGPULightListParameters = PrepareBuildGPULightListParameters(hdCamera, m_TileAndClusterData, ref m_ShaderVariablesLightListCB, m_TotalLightCount); + passData.buildGPULightListParameters = PrepareBuildGPULightListParameters(hdCamera, tileAndClusterData, ref constantBuffer, totalLightCount); passData.depthBuffer = builder.ReadTexture(depthStencilBuffer); passData.stencilTexture = builder.ReadTexture(stencilBufferCopy); if (passData.buildGPULightListParameters.computeMaterialVariants && passData.buildGPULightListParameters.enableFeatureVariants) @@ -103,19 +115,64 @@ BuildGPULightListOutput BuildGPULightList(RenderGraph renderGraph, HDCamera hdCa passData.gBufferCount = gBuffer.gBufferCount; } - passData.lightVolumeDataBuffer = m_TileAndClusterData.lightVolumeDataBuffer; - passData.convexBoundsBuffer = m_TileAndClusterData.convexBoundsBuffer; - passData.AABBBoundsBuffer = m_TileAndClusterData.AABBBoundsBuffer; - passData.globalLightListAtomic = m_TileAndClusterData.globalLightListAtomic; + // Here we use m_MaxViewCount/m_MaxWidthHeight to avoid always allocating buffers of different sizes for each camera. + // This way we'll be reusing them more often. + + // Those buffer are filled with the CPU outside of the render graph. + passData.convexBoundsBuffer = builder.ReadComputeBuffer(renderGraph.ImportComputeBuffer(tileAndClusterData.convexBoundsBuffer)); + passData.lightVolumeDataBuffer = builder.ReadComputeBuffer(renderGraph.ImportComputeBuffer(tileAndClusterData.lightVolumeDataBuffer)); - passData.output.tileFeatureFlags = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_TileAndClusterData.tileFeatureFlags)); - passData.output.dispatchIndirectBuffer = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_TileAndClusterData.dispatchIndirectBuffer)); - passData.output.perVoxelOffset = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_TileAndClusterData.perVoxelOffset)); - passData.output.perTileLogBaseTweak = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_TileAndClusterData.perTileLogBaseTweak)); - passData.output.tileList = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_TileAndClusterData.tileList)); - passData.output.bigTileLightList = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_TileAndClusterData.bigTileLightList)); - passData.output.perVoxelLightLists = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_TileAndClusterData.perVoxelLightLists)); - passData.output.lightList = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_TileAndClusterData.lightList)); + passData.globalLightListAtomic = builder.CreateTransientComputeBuffer(new ComputeBufferDesc(1, sizeof(uint)) { name = "LightListAtomic"}); + passData.AABBBoundsBuffer = builder.CreateTransientComputeBuffer(new ComputeBufferDesc(m_MaxViewCount * 2 * maxLightOnScreen, 4 * sizeof(float)) { name = "AABBBoundBuffer" }); + + var nrTilesX = (m_MaxCameraWidth + LightDefinitions.s_TileSizeFptl - 1) / LightDefinitions.s_TileSizeFptl; + var nrTilesY = (m_MaxCameraHeight + LightDefinitions.s_TileSizeFptl - 1) / LightDefinitions.s_TileSizeFptl; + var nrTiles = nrTilesX * nrTilesY * m_MaxViewCount; + const int capacityUShortsPerTile = 32; + const int dwordsPerTile = (capacityUShortsPerTile + 1) >> 1; // room for 31 lights and a nrLights value. + + if (tileAndClusterData.hasTileBuffers) + { + // note that nrTiles include the viewCount in allocation below + // Tile buffers + passData.output.lightList = builder.WriteComputeBuffer( + renderGraph.CreateComputeBuffer(new ComputeBufferDesc((int)LightCategory.Count * dwordsPerTile * nrTiles, sizeof(uint)) { name = "LightList" })); + passData.output.tileList = builder.WriteComputeBuffer( + renderGraph.CreateComputeBuffer(new ComputeBufferDesc(LightDefinitions.s_NumFeatureVariants * nrTiles, sizeof(uint)) { name = "TileList" })); + passData.output.tileFeatureFlags = builder.WriteComputeBuffer( + renderGraph.CreateComputeBuffer(new ComputeBufferDesc(nrTiles, sizeof(uint)) { name = "TileFeatureFlags" })); + // DispatchIndirect: Buffer with arguments has to have three integer numbers at given argsOffset offset: number of work groups in X dimension, number of work groups in Y dimension, number of work groups in Z dimension. + // DrawProceduralIndirect: Buffer with arguments has to have four integer numbers at given argsOffset offset: vertex count per instance, instance count, start vertex location, and start instance location + // Use use max size of 4 unit for allocation + passData.output.dispatchIndirectBuffer = builder.WriteComputeBuffer( + renderGraph.CreateComputeBuffer(new ComputeBufferDesc(m_MaxViewCount * LightDefinitions.s_NumFeatureVariants * 4, sizeof(uint), ComputeBufferType.IndirectArguments) { name = "DispatchIndirectBuffer" })); + } + + // Big Tile buffer + if (passData.buildGPULightListParameters.runBigTilePrepass) + { + var nrBigTilesX = (m_MaxCameraWidth + 63) / 64; + var nrBigTilesY = (m_MaxCameraHeight + 63) / 64; + var nrBigTiles = nrBigTilesX * nrBigTilesY * m_MaxViewCount; + // TODO: (Nick) In the case of Probe Volumes, this buffer could be trimmed down / tuned more specifically to probe volumes if we added a s_MaxNrBigTileProbeVolumesPlusOne value. + passData.output.bigTileLightList = builder.WriteComputeBuffer( + renderGraph.CreateComputeBuffer(new ComputeBufferDesc(LightDefinitions.s_MaxNrBigTileLightsPlusOne * nrBigTiles, sizeof(uint)) { name = "BigTiles" })); + } + + // Cluster buffers + var nrClustersX = (m_MaxCameraWidth + LightDefinitions.s_TileSizeClustered - 1) / LightDefinitions.s_TileSizeClustered; + var nrClustersY = (m_MaxCameraHeight + LightDefinitions.s_TileSizeClustered - 1) / LightDefinitions.s_TileSizeClustered; + var nrClusterTiles = nrClustersX * nrClustersY * m_MaxViewCount; + + passData.output.perVoxelOffset = builder.WriteComputeBuffer( + renderGraph.CreateComputeBuffer(new ComputeBufferDesc((int)LightCategory.Count * (1 << k_Log2NumClusters) * nrClusterTiles, sizeof(uint)) { name = "PerVoxelOffset" })); + passData.output.perVoxelLightLists = builder.WriteComputeBuffer( + renderGraph.CreateComputeBuffer(new ComputeBufferDesc(NumLightIndicesPerClusteredTile() * nrClusterTiles, sizeof(uint)) { name = "PerVoxelLightList" })); + if (tileAndClusterData.clusterNeedsDepth) + { + passData.output.perTileLogBaseTweak = builder.WriteComputeBuffer( + renderGraph.CreateComputeBuffer(new ComputeBufferDesc(nrClusterTiles, sizeof(float)) { name = "PerTileLogBaseTweak" })); + } builder.SetRenderFunc( (BuildGPULightListPassData data, RenderGraphContext context) => @@ -131,9 +188,6 @@ BuildGPULightListOutput BuildGPULightList(RenderGraph renderGraph, HDCamera hdCa VoxelLightListGeneration(data.buildGPULightListParameters, buildLightListResources, context.cmd); BuildDispatchIndirectArguments(data.buildGPULightListParameters, buildLightListResources, tileFlagsWritten, context.cmd); - - // TODO RENDERGRAPH WARNING: Note that the three sets of variables are bound here, but it should be handled differently. - PushLightLoopGlobalParams(data.lightLoopGlobalParameters, context.cmd); }); return passData.output; @@ -314,17 +368,14 @@ class RenderSSRPassData public TextureHandle hitPointsTexture; public TextureHandle lightingTexture; public TextureHandle clearCoatMask; + public ComputeBufferHandle coarseStencilBuffer; //public TextureHandle debugTexture; } - TextureHandle RenderSSR( RenderGraph renderGraph, - HDCamera hdCamera, - TextureHandle normalBuffer, - TextureHandle motionVectorsBuffer, - TextureHandle depthBuffer, - TextureHandle depthPyramid, - TextureHandle stencilBuffer, - TextureHandle clearCoatMask) + TextureHandle RenderSSR( RenderGraph renderGraph, + HDCamera hdCamera, + in PrepassOutput prepassOutput, + TextureHandle clearCoatMask) { var ssrBlackTexture = renderGraph.ImportTexture(TextureXR.GetBlackTexture(), HDShaderIDs._SsrLightingTexture); @@ -351,14 +402,16 @@ TextureHandle RenderSSR( RenderGraph renderGraph, var colorPyramid = renderGraph.ImportTexture(hdCamera.GetPreviousFrameRT((int)HDCameraFrameHistoryType.ColorBufferMipChain)); passData.parameters = PrepareSSRParameters(hdCamera, m_DepthBufferMipChainInfo, true); - passData.depthBuffer = builder.ReadTexture(depthBuffer); - passData.depthPyramid = builder.ReadTexture(depthPyramid); + passData.depthBuffer = builder.ReadTexture(prepassOutput.depthBuffer); + passData.depthPyramid = builder.ReadTexture(prepassOutput.depthPyramidTexture); passData.colorPyramid = builder.ReadTexture(colorPyramid); - passData.stencilBuffer = builder.ReadTexture(stencilBuffer); + passData.stencilBuffer = builder.ReadTexture(prepassOutput.stencilBuffer); passData.clearCoatMask = builder.ReadTexture(clearCoatMask); + passData.coarseStencilBuffer = builder.ReadComputeBuffer(prepassOutput.coarseStencilBuffer); - builder.ReadTexture(normalBuffer); - builder.ReadTexture(motionVectorsBuffer); + // TODO RENDERGRAPH: pass and bind properly those texture (once auto setglobal is gone) + builder.ReadTexture(prepassOutput.resolvedNormalBuffer); + builder.ReadTexture(prepassOutput.resolvedMotionVectorsBuffer); // 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. @@ -381,6 +434,7 @@ TextureHandle RenderSSR( RenderGraph renderGraph, res.GetTexture(data.clearCoatMask), res.GetTexture(data.colorPyramid), res.GetTexture(data.lightingTexture), + res.GetComputeBuffer(data.coarseStencilBuffer), context.cmd, context.renderContext); }); @@ -407,7 +461,7 @@ class RenderContactShadowPassData public ComputeBufferHandle lightList; } - TextureHandle RenderContactShadows(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle depthTexture, BuildGPULightListOutput lightLists, int firstMipOffsetY) + TextureHandle RenderContactShadows(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle depthTexture, in BuildGPULightListOutput lightLists, int firstMipOffsetY) { if (!WillRenderContactShadow()) return renderGraph.ImportTexture(TextureXR.GetClearTexture(), HDShaderIDs._ContactShadowTexture); 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 04beb0c9320..185ae58c160 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 @@ -53,28 +53,29 @@ struct PrepassOutput { // Buffers that may be output by the prepass. // They will be MSAA depending on the frame settings - public TextureHandle depthBuffer; - public TextureHandle depthAsColor; - public TextureHandle normalBuffer; - public TextureHandle motionVectorsBuffer; + public TextureHandle depthBuffer; + public TextureHandle depthAsColor; + public TextureHandle normalBuffer; + public TextureHandle motionVectorsBuffer; // GBuffer output. Will also contain a reference to the normal buffer (as it is shared between deferred and forward objects) - public GBufferOutput gbuffer; + public GBufferOutput gbuffer; - public DBufferOutput dbuffer; + public DBufferOutput dbuffer; // Additional buffers only for MSAA - public TextureHandle depthValuesMSAA; + public TextureHandle depthValuesMSAA; // Resolved buffers for MSAA. When MSAA is off, they will be the same reference as the buffers above. - public TextureHandle resolvedDepthBuffer; - public TextureHandle resolvedNormalBuffer; - public TextureHandle resolvedMotionVectorsBuffer; + public TextureHandle resolvedDepthBuffer; + public TextureHandle resolvedNormalBuffer; + public TextureHandle resolvedMotionVectorsBuffer; // Copy of the resolved depth buffer with mip chain - public TextureHandle depthPyramidTexture; + public TextureHandle depthPyramidTexture; - public TextureHandle stencilBuffer; + public TextureHandle stencilBuffer; + public ComputeBufferHandle coarseStencilBuffer; } TextureHandle CreateDepthBuffer(RenderGraph renderGraph, bool msaa) @@ -539,7 +540,8 @@ void BuildCoarseStencilAndResolveIfNeeded(RenderGraph renderGraph, HDCamera hdCa { passData.parameters = PrepareBuildCoarseStencilParameters(hdCamera); passData.inputDepth = output.depthBuffer; - passData.coarseStencilBuffer = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_SharedRTManager.GetCoarseStencilBuffer())); + passData.coarseStencilBuffer = builder.WriteComputeBuffer( + renderGraph.CreateComputeBuffer(new ComputeBufferDesc(HDUtils.DivRoundUp(m_MaxCameraWidth, 8) * HDUtils.DivRoundUp(m_MaxCameraHeight, 8) * m_MaxViewCount, sizeof(uint)) { name = "CoarseStencilBuffer" })); 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( @@ -562,6 +564,7 @@ void BuildCoarseStencilAndResolveIfNeeded(RenderGraph renderGraph, HDCamera hdCa { output.stencilBuffer = output.depthBuffer; } + output.coarseStencilBuffer = passData.coarseStencilBuffer; } } @@ -604,8 +607,11 @@ void SetupDBufferTargets(RenderGraph renderGraph, RenderDBufferPassData passData new TextureDesc(Vector2.one, true, true) { colorFormat = rtFormat[dbufferIndex], name = s_DBufferNames[dbufferIndex] }), dbufferIndex); } + int propertyMaskBufferSize = ((m_MaxCameraWidth + 7) / 8) * ((m_MaxCameraHeight + 7) / 8); + propertyMaskBufferSize = ((propertyMaskBufferSize + 63) / 64) * 64; // round off to nearest multiple of 64 for ease of use in CS + passData.depthStencilBuffer = builder.UseDepthBuffer(output.resolvedDepthBuffer, DepthAccess.Write); - passData.propertyMaskBuffer = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_DbufferManager.propertyMaskBuffer)); + passData.propertyMaskBuffer = builder.WriteComputeBuffer(renderGraph.CreateComputeBuffer(new ComputeBufferDesc(propertyMaskBufferSize, 4) { name = "DecalPropertyMask" })); output.dbuffer.dBufferCount = passData.dBufferCount; for (int i = 0; i < passData.dBufferCount; ++i) 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 a3c5b74b9f4..74ec48fefb7 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 @@ -39,6 +39,7 @@ void ExecuteWithRenderGraph( RenderRequest renderRequest, // Need this during debug render at the end outside of the main loop scope. // Once render graph move is implemented, we can probably remove the branch and this. ShadowResult shadowResult = new ShadowResult(); + BuildGPULightListOutput gpuLightListOutput = new BuildGPULightListOutput(); if (m_CurrentDebugDisplaySettings.IsDebugMaterialDisplayEnabled() || m_CurrentDebugDisplaySettings.IsMaterialValidationEnabled() || CoreUtils.IsSceneLightingDisabled(hdCamera.camera)) { @@ -66,7 +67,7 @@ void ExecuteWithRenderGraph( RenderRequest renderRequest, } else { - var gpuLightListOutput = BuildGPULightList(m_RenderGraph, hdCamera, prepassOutput.depthBuffer, prepassOutput.stencilBuffer, prepassOutput.gbuffer); + gpuLightListOutput = BuildGPULightList(m_RenderGraph, hdCamera, m_TileAndClusterData, m_TotalLightCount, m_MaxLightsOnScreen, ref m_ShaderVariablesLightListCB, prepassOutput.depthBuffer, prepassOutput.stencilBuffer, prepassOutput.gbuffer); lightingBuffers.ambientOcclusionBuffer = m_AmbientOcclusionSystem.Render(m_RenderGraph, hdCamera, prepassOutput.depthPyramidTexture, prepassOutput.motionVectorsBuffer, m_FrameCount); // Should probably be inside the AO render function but since it's a separate class it's currently not super clean to do. @@ -76,11 +77,7 @@ void ExecuteWithRenderGraph( RenderRequest renderRequest, var clearCoatMask = hdCamera.frameSettings.litShaderMode == LitShaderMode.Deferred ? prepassOutput.gbuffer.mrt[2] : m_RenderGraph.defaultResources.blackTextureXR; lightingBuffers.ssrLightingBuffer = RenderSSR(m_RenderGraph, hdCamera, - prepassOutput.resolvedNormalBuffer, - prepassOutput.resolvedMotionVectorsBuffer, - prepassOutput.depthBuffer, - prepassOutput.depthPyramidTexture, - prepassOutput.stencilBuffer, + prepassOutput, clearCoatMask); lightingBuffers.contactShadowsBuffer = RenderContactShadows(m_RenderGraph, hdCamera, msaa ? prepassOutput.depthValuesMSAA : prepassOutput.depthPyramidTexture, gpuLightListOutput, GetDepthBufferMipChainInfo().mipLevelOffsets[1].y); @@ -136,7 +133,7 @@ void ExecuteWithRenderGraph( RenderRequest renderRequest, var deferredLightingOutput = RenderDeferredLighting(m_RenderGraph, hdCamera, colorBuffer, prepassOutput.depthBuffer, prepassOutput.depthPyramidTexture, lightingBuffers, prepassOutput.gbuffer, shadowResult, gpuLightListOutput); - RenderForwardOpaque(m_RenderGraph, hdCamera, colorBuffer, lightingBuffers, prepassOutput.depthBuffer, shadowResult, prepassOutput.dbuffer, cullingResults); + RenderForwardOpaque(m_RenderGraph, hdCamera, colorBuffer, lightingBuffers, gpuLightListOutput, prepassOutput.depthBuffer, shadowResult, prepassOutput.dbuffer, cullingResults); // TODO RENDERGRAPH : Move this to the end after we do move semantic and graph pruning to avoid doing the rest of the frame for nothing aovRequest.PushCameraTexture(m_RenderGraph, AOVBuffers.Normals, hdCamera, prepassOutput.resolvedNormalBuffer, aovBuffers); @@ -161,7 +158,7 @@ void ExecuteWithRenderGraph( RenderRequest renderRequest, // No need for old stencil values here since from transparent on different features are tagged //ClearStencilBuffer(hdCamera, cmd); - colorBuffer = RenderTransparency(m_RenderGraph, hdCamera, colorBuffer, prepassOutput.depthBuffer, prepassOutput.motionVectorsBuffer, currentColorPyramid, prepassOutput.depthPyramidTexture, shadowResult, cullingResults); + colorBuffer = RenderTransparency(m_RenderGraph, hdCamera, colorBuffer, prepassOutput.depthBuffer, prepassOutput.motionVectorsBuffer, currentColorPyramid, prepassOutput.depthPyramidTexture, gpuLightListOutput, shadowResult, cullingResults); if (hdCamera.frameSettings.IsEnabled(FrameSettingsField.TransparentsWriteMotionVector)) { @@ -233,6 +230,7 @@ void ExecuteWithRenderGraph( RenderRequest renderRequest, prepassOutput.depthPyramidTexture, m_DebugFullScreenTexture, colorPickerTexture, + gpuLightListOutput, shadowResult, cullingResults); @@ -366,19 +364,36 @@ class ForwardPassData public TextureHandle[] renderTarget = new TextureHandle[3]; public int renderTargetCount; public TextureHandle depthBuffer; - public ComputeBuffer lightListBuffer; + public ComputeBufferHandle lightListBuffer; + public ComputeBufferHandle perVoxelOffset; + public ComputeBufferHandle perTileLogBaseTweak; public FrameSettings frameSettings; public bool decalsEnabled; public bool renderMotionVecForTransparent; public DBufferOutput? dbuffer; } - void PrepareForwardPassData(RenderGraph renderGraph, RenderGraphBuilder builder, ForwardPassData data, bool opaque, FrameSettings frameSettings, RendererListDesc rendererListDesc, TextureHandle depthBuffer, ShadowResult shadowResult, DBufferOutput? dbuffer = null) + void PrepareForwardPassData(RenderGraph renderGraph, + RenderGraphBuilder builder, + ForwardPassData data, + bool opaque, + FrameSettings frameSettings, + RendererListDesc rendererListDesc, + in BuildGPULightListOutput lightLists, + TextureHandle depthBuffer, + ShadowResult shadowResult, + DBufferOutput? dbuffer = null) { bool useFptl = frameSettings.IsEnabled(FrameSettingsField.FPTLForForwardOpaque) && opaque; data.frameSettings = frameSettings; - data.lightListBuffer = useFptl ? m_TileAndClusterData.lightList: m_TileAndClusterData.perVoxelLightLists; + data.lightListBuffer = builder.ReadComputeBuffer(useFptl ? lightLists.lightList : lightLists.perVoxelLightLists); + if (!useFptl) + { + data.perVoxelOffset = builder.ReadComputeBuffer(lightLists.perVoxelOffset); + if (lightLists.perTileLogBaseTweak.IsValid()) + data.perTileLogBaseTweak = builder.ReadComputeBuffer(lightLists.perTileLogBaseTweak); + } data.depthBuffer = builder.UseDepthBuffer(depthBuffer, DepthAccess.ReadWrite); data.rendererList = builder.UseRendererList(renderGraph.CreateRendererList(rendererListDesc)); // enable d-buffer flag value is being interpreted more like enable decals in general now that we have clustered @@ -399,9 +414,10 @@ void PrepareForwardPassData(RenderGraph renderGraph, RenderGraphBuilder builder, // (Thus why "Forward" and "ForwardOnly" are exclusive, else they will render two times" void RenderForwardOpaque( RenderGraph renderGraph, HDCamera hdCamera, - TextureHandle colorBuffer, + TextureHandle colorBuffer, in LightingBuffers lightingBuffers, - TextureHandle depthBuffer, + in BuildGPULightListOutput lightLists, + TextureHandle depthBuffer, ShadowResult shadowResult, DBufferOutput dbuffer, CullingResults cullResults) @@ -412,7 +428,7 @@ void RenderForwardOpaque( RenderGraph renderGraph, out var passData, debugDisplay ? ProfilingSampler.Get(HDProfileId.ForwardOpaqueDebug) : ProfilingSampler.Get(HDProfileId.ForwardOpaque))) { - PrepareForwardPassData(renderGraph, builder, passData, true, hdCamera.frameSettings, PrepareForwardOpaqueRendererList(cullResults, hdCamera), depthBuffer, shadowResult, dbuffer); + PrepareForwardPassData(renderGraph, builder, passData, true, hdCamera.frameSettings, PrepareForwardOpaqueRendererList(cullResults, hdCamera), lightLists, depthBuffer, shadowResult, dbuffer); // In case of forward SSS we will bind all the required target. It is up to the shader to write into it or not. if (hdCamera.frameSettings.IsEnabled(FrameSettingsField.SubsurfaceScattering)) @@ -445,7 +461,7 @@ void RenderForwardOpaque( RenderGraph renderGraph, context.resources.GetRendererList(data.rendererList), mrt, context.resources.GetTexture(data.depthBuffer), - data.lightListBuffer, + context.resources.GetComputeBuffer(data.lightListBuffer), true, context.renderContext, context.cmd); }); } @@ -453,11 +469,12 @@ void RenderForwardOpaque( RenderGraph renderGraph, void RenderForwardTransparent( RenderGraph renderGraph, HDCamera hdCamera, - TextureHandle colorBuffer, - TextureHandle motionVectorBuffer, - TextureHandle depthBuffer, - TextureHandle? colorPyramid, - ShadowResult shadowResult, + TextureHandle colorBuffer, + TextureHandle motionVectorBuffer, + TextureHandle depthBuffer, + TextureHandle? colorPyramid, + in BuildGPULightListOutput lightLists, + in ShadowResult shadowResult, CullingResults cullResults, bool preRefractionPass) { @@ -481,7 +498,7 @@ void RenderForwardTransparent( RenderGraph renderGraph, using (var builder = renderGraph.AddRenderPass(passName, out var passData, ProfilingSampler.Get(profilingId))) { - PrepareForwardPassData(renderGraph, builder, passData, false, hdCamera.frameSettings, PrepareForwardTransparentRendererList(cullResults, hdCamera, preRefractionPass), depthBuffer, shadowResult); + PrepareForwardPassData(renderGraph, builder, passData, false, hdCamera.frameSettings, PrepareForwardTransparentRendererList(cullResults, hdCamera, preRefractionPass), lightLists, depthBuffer, shadowResult); bool renderMotionVecForTransparent = NeedMotionVectorForTransparent(hdCamera.frameSettings); @@ -519,15 +536,19 @@ void RenderForwardTransparent( RenderGraph renderGraph, for (int i = 0; i < data.renderTargetCount; ++i) mrt[i] = context.resources.GetTexture(data.renderTarget[i]); + // Bind all global data/parameters for transparent forward pass context.cmd.SetGlobalInt(HDShaderIDs._ColorMaskTransparentVel, data.renderMotionVecForTransparent ? (int)ColorWriteMask.All : 0); if (data.decalsEnabled) DecalSystem.instance.SetAtlas(context.cmd); // for clustered decals + context.cmd.SetGlobalBuffer(HDShaderIDs.g_vLayeredOffsetsBuffer, context.resources.GetComputeBuffer(data.perVoxelOffset)); + context.cmd.SetGlobalBuffer(HDShaderIDs.g_logBaseBuffer, context.resources.GetComputeBuffer(data.perTileLogBaseTweak)); + RenderForwardRendererList( data.frameSettings, context.resources.GetRendererList(data.rendererList), mrt, context.resources.GetTexture(data.depthBuffer), - data.lightListBuffer, + context.resources.GetComputeBuffer(data.lightListBuffer), false, context.renderContext, context.cmd); }); } @@ -688,15 +709,16 @@ void UpsampleTransparent(RenderGraph renderGraph, HDCamera hdCamera, TextureHand } } - TextureHandle RenderTransparency( RenderGraph renderGraph, - HDCamera hdCamera, - TextureHandle colorBuffer, - TextureHandle depthStencilBuffer, - TextureHandle motionVectorsBuffer, - TextureHandle currentColorPyramid, - TextureHandle depthPyramid, - ShadowResult shadowResult, - CullingResults cullingResults) + TextureHandle RenderTransparency( RenderGraph renderGraph, + HDCamera hdCamera, + TextureHandle colorBuffer, + TextureHandle depthStencilBuffer, + TextureHandle motionVectorsBuffer, + TextureHandle currentColorPyramid, + TextureHandle depthPyramid, + in BuildGPULightListOutput lightLists, + ShadowResult shadowResult, + CullingResults cullingResults) { RenderTransparentDepthPrepass(renderGraph, hdCamera, depthStencilBuffer, cullingResults); @@ -714,7 +736,7 @@ TextureHandle RenderTransparency( RenderGraph renderGraph, //RenderCustomPass(renderContext, cmd, hdCamera, customPassCullingResults, CustomPassInjectionPoint.BeforePreRefraction); // Render pre-refraction objects - RenderForwardTransparent(renderGraph, hdCamera, colorBuffer, motionVectorsBuffer, depthStencilBuffer, null, shadowResult, cullingResults, true); + RenderForwardTransparent(renderGraph, hdCamera, colorBuffer, motionVectorsBuffer, depthStencilBuffer, null, lightLists, shadowResult, cullingResults, true); if (hdCamera.frameSettings.IsEnabled(FrameSettingsField.Refraction)) { @@ -727,7 +749,7 @@ TextureHandle RenderTransparency( RenderGraph renderGraph, //RenderCustomPass(renderContext, cmd, hdCamera, customPassCullingResults, CustomPassInjectionPoint.BeforeTransparent); // Render all type of transparent forward (unlit, lit, complex (hair...)) to keep the sorting between transparent objects. - RenderForwardTransparent(renderGraph, hdCamera, colorBuffer, motionVectorsBuffer, depthStencilBuffer, currentColorPyramid, shadowResult, cullingResults, false); + RenderForwardTransparent(renderGraph, hdCamera, colorBuffer, motionVectorsBuffer, depthStencilBuffer, currentColorPyramid, lightLists, shadowResult, cullingResults, false); // We push the motion vector debug texture here as transparent object can overwrite the motion vector texture content. if (m_Asset.currentPlatformRenderPipelineSettings.supportMotionVectors) 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 321cf6a5f5f..9815274982a 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 @@ -30,6 +30,7 @@ class SubsurfaceScaterringPassData public TextureHandle depthTexture; public TextureHandle cameraFilteringBuffer; public TextureHandle sssBuffer; + public ComputeBufferHandle coarseStencilBuffer; } void RenderSubsurfaceScattering(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle colorBuffer, @@ -51,6 +52,7 @@ void RenderSubsurfaceScattering(RenderGraph renderGraph, HDCamera hdCamera, Text passData.depthStencilBuffer = builder.ReadTexture(depthStencilBuffer); passData.depthTexture = builder.ReadTexture(depthTexture); passData.sssBuffer = builder.ReadTexture(lightingBuffers.sssBuffer); + passData.coarseStencilBuffer = builder.ReadComputeBuffer(prepassOutput.coarseStencilBuffer); if (passData.parameters.needTemporaryBuffer) { passData.cameraFilteringBuffer = builder.CreateTransientTexture( @@ -68,7 +70,7 @@ void RenderSubsurfaceScattering(RenderGraph renderGraph, HDCamera hdCamera, Text resources.depthTexture = context.resources.GetTexture(data.depthTexture); resources.cameraFilteringBuffer = context.resources.GetTexture(data.cameraFilteringBuffer); resources.sssBuffer = context.resources.GetTexture(data.sssBuffer); - resources.coarseStencilBuffer = data.parameters.coarseStencilBuffer; + resources.coarseStencilBuffer = context.resources.GetComputeBuffer(data.coarseStencilBuffer); RenderSubsurfaceScattering(data.parameters, resources, context.cmd); }); 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 f7e0df0198e..769dab7c893 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs @@ -991,6 +991,9 @@ void InitializeNonRenderGraphResources() m_AmbientOcclusionSystem.InitializeNonRenderGraphResources(); m_PostProcessSystem.InitializeNonRenderGraphResources(asset); s_lightVolumes.InitializeNonRenderGraphResources(); + + // Reset resolution dependent buffers. Tile, Coarse stencil etc... + m_MaxCameraWidth = m_MaxCameraHeight = m_MaxViewCount = 1; } void CleanupNonRenderGraphResources() @@ -1000,6 +1003,9 @@ void CleanupNonRenderGraphResources() m_AmbientOcclusionSystem.CleanupNonRenderGraphResources(); m_PostProcessSystem.CleanupNonRenderGraphResources(); s_lightVolumes.CleanupNonRenderGraphResources(); + LightLoopCleanupNonRenderGraphResources(); + m_SharedRTManager.DisposeCoarseStencilBuffer(); + m_DbufferManager.ReleaseResolutionDependentBuffers(); } void InitializeDebugMaterials() @@ -1213,8 +1219,11 @@ void Resize(HDCamera hdCamera) } LightLoopAllocResolutionDependentBuffers(hdCamera, m_MaxCameraWidth, m_MaxCameraHeight); - m_DbufferManager.AllocResolutionDependentBuffers(hdCamera, m_MaxCameraWidth, m_MaxCameraHeight); - m_SharedRTManager.AllocateCoarseStencilBuffer(m_MaxCameraWidth, m_MaxCameraHeight, hdCamera.viewCount); + if (!m_EnableRenderGraph) + { + m_DbufferManager.AllocResolutionDependentBuffers(m_MaxCameraWidth, m_MaxCameraHeight); + m_SharedRTManager.AllocateCoarseStencilBuffer(m_MaxCameraWidth, m_MaxCameraHeight, hdCamera.viewCount); + } } } @@ -2350,7 +2359,15 @@ AOVRequestData aovRequest if (m_EnableRenderGraph) { - ExecuteWithRenderGraph(renderRequest, aovRequest, aovBuffers, renderContext, cmd); + try + { + ExecuteWithRenderGraph(renderRequest, aovRequest, aovBuffers, renderContext, cmd); + } + catch(Exception e) + { + Debug.LogError("Error while building Render Graph."); + Debug.LogException(e); + } return; } @@ -3717,7 +3734,7 @@ RenderDBufferParameters PrepareRenderDBufferParameters() parameters.use4RTs = m_Asset.currentPlatformRenderPipelineSettings.decalSettings.perChannelMask; parameters.clearPropertyMaskBufferCS = m_DbufferManager.clearPropertyMaskBufferShader; parameters.clearPropertyMaskBufferKernel = m_DbufferManager.clearPropertyMaskBufferKernel; - parameters.propertyMaskBufferSize = m_DbufferManager.propertyMaskBufferSize; + parameters.propertyMaskBufferSize = m_DbufferManager.GetPropertyMaskBufferSize(m_MaxCameraWidth, m_MaxCameraHeight); return parameters; } @@ -4321,7 +4338,6 @@ struct RenderSSRParameters public int width, height, viewCount; public ComputeBuffer offsetBufferData; - public ComputeBuffer coarseStencilBuffer; public ShaderVariablesScreenSpaceReflection cb; } @@ -4361,7 +4377,6 @@ RenderSSRParameters PrepareSSRParameters(HDCamera hdCamera, in HDUtils.PackedMip cb._SsrDepthPyramidMaxMip = depthPyramid.mipLevelCount - 1; parameters.offsetBufferData = depthPyramid.GetOffsetBufferData(m_DepthPyramidMipLevelOffsetsBuffer); - parameters.coarseStencilBuffer = m_SharedRTManager.GetCoarseStencilBuffer(); return parameters; } @@ -4374,6 +4389,7 @@ static void RenderSSR( in RenderSSRParameters parameters, RTHandle clearCoatMask, RTHandle previousColorPyramid, RTHandle ssrLightingTexture, + ComputeBuffer coarseStencilBuffer, CommandBuffer cmd, ScriptableRenderContext renderContext) { @@ -4401,7 +4417,7 @@ static void RenderSSR( in RenderSSRParameters parameters, cmd.SetComputeTextureParam(cs, parameters.tracingKernel, HDShaderIDs._StencilTexture, stencilBuffer, 0, RenderTextureSubElement.Stencil); } - cmd.SetComputeBufferParam(cs, parameters.tracingKernel, HDShaderIDs._CoarseStencilBuffer, parameters.coarseStencilBuffer); + cmd.SetComputeBufferParam(cs, parameters.tracingKernel, HDShaderIDs._CoarseStencilBuffer, coarseStencilBuffer); cmd.SetComputeBufferParam(cs, parameters.tracingKernel, HDShaderIDs._DepthPyramidMipLevelOffsets, parameters.offsetBufferData); ConstantBuffer.Push(cmd, parameters.cb, cs, HDShaderIDs._ShaderVariablesScreenSpaceReflection); @@ -4450,7 +4466,7 @@ void RenderSSR(HDCamera hdCamera, CommandBuffer cmd, ScriptableRenderContext ren var parameters = PrepareSSRParameters(hdCamera, m_SharedRTManager.GetDepthBufferMipChainInfo(), false); RenderSSR(parameters, m_SharedRTManager.GetDepthStencilBuffer(), m_SharedRTManager.GetDepthTexture(), m_SsrHitPointTexture, m_SharedRTManager.GetStencilBuffer(hdCamera.frameSettings.IsEnabled(FrameSettingsField.MSAA)), clearCoatMask, previousColorPyramid, - m_SsrLightingTexture, cmd, renderContext); + m_SsrLightingTexture, m_SharedRTManager.GetCoarseStencilBuffer(), cmd, renderContext); if (!hdCamera.colorPyramidHistoryIsValid) { @@ -4490,7 +4506,7 @@ void RenderSSRTransparent(HDCamera hdCamera, CommandBuffer cmd, ScriptableRender // Evaluate the screen space reflection for the transparent pixels var previousColorPyramid = hdCamera.GetPreviousFrameRT((int)HDCameraFrameHistoryType.ColorBufferMipChain); var parameters = PrepareSSRParameters(hdCamera, m_SharedRTManager.GetDepthBufferMipChainInfo(), true); - RenderSSR(parameters, m_SharedRTManager.GetDepthStencilBuffer(), m_SharedRTManager.GetDepthTexture(), m_SsrHitPointTexture, m_SharedRTManager.GetStencilBuffer(), TextureXR.GetBlackTexture(), previousColorPyramid, m_SsrLightingTexture, cmd, renderContext); + RenderSSR(parameters, m_SharedRTManager.GetDepthStencilBuffer(), m_SharedRTManager.GetDepthTexture(), m_SsrHitPointTexture, m_SharedRTManager.GetStencilBuffer(), TextureXR.GetBlackTexture(), previousColorPyramid, m_SsrLightingTexture, m_SharedRTManager.GetCoarseStencilBuffer(), cmd, renderContext); // If color pyramid was not valid, we bind a black texture if (!hdCamera.colorPyramidHistoryIsValid) @@ -5034,7 +5050,7 @@ void RenderDebug(HDCamera hdCamera, CommandBuffer cmd, CullingResults cullResult RenderSkyReflectionOverlay(debugParams, cmd, m_SharedPropertyBlock, ref x, ref y, overlaySize); RenderRayCountOverlay(debugParams, cmd, ref x, ref y, overlaySize); - RenderLightLoopDebugOverlay(debugParams, cmd, ref x, ref y, overlaySize, m_SharedRTManager.GetDepthTexture()); + RenderLightLoopDebugOverlay(debugParams, cmd, ref x, ref y, overlaySize, m_TileAndClusterData.tileList, m_TileAndClusterData.lightList, m_TileAndClusterData.perVoxelLightLists, m_TileAndClusterData.dispatchIndirectBuffer, m_SharedRTManager.GetDepthTexture()); RenderProbeVolumeDebugOverlay(debugParams, cmd, ref x, ref y, overlaySize, m_DebugDisplayProbeVolumeMaterial); // TODO(Nicholas): renders as a black square in the upper right. HDShadowManager.ShadowDebugAtlasTextures atlases = debugParams.lightingOverlayParameters.shadowManager.GetDebugAtlasTextures();