From ccd615ddb64c151606fcb5d73014118a56787a41 Mon Sep 17 00:00:00 2001 From: Julien Ignace Date: Fri, 5 Jun 2020 15:54:16 +0200 Subject: [PATCH] Adding purge of unused resources in render graph. --- .../Runtime/RenderGraph/RenderGraph.cs | 11 +- .../RenderGraphResourceRegistry.cs | 147 ++++++++++++------ .../HDRenderPipeline.RenderGraph.cs | 7 +- 3 files changed, 112 insertions(+), 53 deletions(-) diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs index d6a8e05d272..7a7d2e8e094 100644 --- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs @@ -42,11 +42,13 @@ public ref struct RenderGraphContext public struct RenderGraphExecuteParams { ///Rendering width. - public int renderingWidth; + public int renderingWidth; ///Rendering height. - public int renderingHeight; + public int renderingHeight; ///Number of MSAA samples. - public MSAASamples msaaSamples; + public MSAASamples msaaSamples; + ///Index of the current frame being rendered. + public int currentFrameIndex; } class RenderGraphDebugParams @@ -362,8 +364,7 @@ public void Execute(ScriptableRenderContext renderContext, CommandBuffer cmd, in { m_Logger.Initialize(); - // Update RTHandleSystem with size for this rendering pass. - m_Resources.SetRTHandleReferenceSize(parameters.renderingWidth, parameters.renderingHeight, parameters.msaaSamples); + m_Resources.BeginRender(parameters.renderingWidth, parameters.renderingHeight, parameters.msaaSamples, parameters.currentFrameIndex); LogFrameInformation(parameters.renderingWidth, parameters.renderingHeight); diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs index 1af426e2518..8722df245d9 100644 --- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs +++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs @@ -322,6 +322,85 @@ public override int GetHashCode() } #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) + { + if (!m_ResourcePool.TryGetValue(hash, out var list)) + { + list = new List<(RTHandle rt, int frameIndex)>(); + m_ResourcePool.Add(hash, list); + } + + list.Add((rt, currentFrameIndex)); + } + + public bool TryGetResource(int hashCode, out RTHandle rt) + { + if (m_ResourcePool.TryGetValue(hashCode, out var list) && list.Count > 0) + { + 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) + { + foreach (var res in kvp.Value) + { + res.resource.Release(); + } + } + } + + public void LogResources(RenderGraphLogger logger) + { + List allocationList = new List(); + foreach (var kvp in m_ResourcePool) + { + foreach (var res in kvp.Value) + { + allocationList.Add(res.resource.rt.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. /// @@ -393,17 +472,19 @@ internal ComputeBufferResource(ComputeBuffer computeBuffer, bool imported) #endregion DynamicArray m_TextureResources = new DynamicArray(); - Dictionary> m_TexturePool = new Dictionary>(); + RenderGraphTexturePool m_TexturePool = new RenderGraphTexturePool(); DynamicArray m_RendererListResources = new DynamicArray(); DynamicArray m_ComputeBufferResources = new DynamicArray(); RTHandleSystem m_RTHandleSystem = new RTHandleSystem(); RenderGraphDebugParams m_RenderGraphDebug; RenderGraphLogger m_Logger; + int m_CurrentFrameIndex; RTHandle m_CurrentBackbuffer; // Diagnostic only - List<(int, RTHandle)> m_AllocatedTextures = new List<(int, RTHandle)>(); + // 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 /// @@ -470,8 +551,10 @@ internal RenderGraphResourceRegistry(bool supportMSAA, MSAASamples initialSample m_Logger = logger; } - internal void SetRTHandleReferenceSize(int width, int height, MSAASamples msaaSamples) + internal void BeginRender(int width, int height, MSAASamples msaaSamples, int currentFrameIndex) { + m_CurrentFrameIndex = currentFrameIndex; + // Update RTHandleSystem with size for this rendering pass. m_RTHandleSystem.SetReferenceSize(width, height, msaaSamples); } @@ -618,7 +701,7 @@ void CreateTextureForPass(ref TextureResource resource) #if DEVELOPMENT_BUILD || UNITY_EDITOR if (hashCode != -1) { - m_AllocatedTextures.Add((hashCode, resource.rt)); + m_FrameAllocatedTextures.Add((hashCode, resource.rt)); } #endif @@ -675,16 +758,10 @@ internal void ReleaseTexture(RenderGraphContext rgContext, TextureHandle resourc void ReleaseTextureResource(int hash, RTHandle rt) { - if (!m_TexturePool.TryGetValue(hash, out var stack)) - { - stack = new Stack(); - m_TexturePool.Add(hash, stack); - } - - stack.Push(rt); + m_TexturePool.ReleaseResource(hash, rt, m_CurrentFrameIndex); #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllocatedTextures.Remove((hash, rt)); + m_FrameAllocatedTextures.Remove((hash, rt)); #endif } @@ -746,14 +823,7 @@ void ValidateRendererListDesc(in RendererListDesc desc) bool TryGetRenderTarget(int hashCode, out RTHandle rt) { - if (m_TexturePool.TryGetValue(hashCode, out var stack) && stack.Count > 0) - { - rt = stack.Pop(); - return true; - } - - rt = null; - return false; + return m_TexturePool.TryGetResource(hashCode, out rt); } internal void CreateRendererLists(List rendererLists) @@ -778,11 +848,11 @@ internal void Clear(bool onException) m_ComputeBufferResources.Clear(); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_AllocatedTextures.Count != 0 && !onException) + if (m_FrameAllocatedTextures.Count != 0 && !onException) { - string logMessage = "RenderGraph: Not all textures were released."; + 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_AllocatedTextures); + List<(int, RTHandle)> tempList = new List<(int, RTHandle)>(m_FrameAllocatedTextures); foreach (var value in tempList) { logMessage = $"{logMessage}\n\t{value.Item2.name}"; @@ -792,10 +862,15 @@ internal void Clear(bool onException) Debug.LogWarning(logMessage); } - // If an error occurred during execution, it's expected that textures are not all release so we clear the trakcing list. + // If an error occurred during execution, it's expected that textures are not all released so we clear the tracking list. if (onException) - m_AllocatedTextures.Clear(); + m_FrameAllocatedTextures.Clear(); #endif + + // 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); } internal void ResetRTHandleReferenceSize(int width, int height) @@ -805,13 +880,7 @@ internal void ResetRTHandleReferenceSize(int width, int height) internal void Cleanup() { - foreach (var value in m_TexturePool) - { - foreach (var rt in value.Value) - { - m_RTHandleSystem.Release(rt); - } - } + m_TexturePool.Cleanup(); } void LogTextureCreation(RTHandle rt, bool cleared) @@ -836,19 +905,7 @@ void LogResources() { m_Logger.LogLine("==== Allocated Resources ====\n"); - List allocationList = new List(); - foreach (var stack in m_TexturePool) - { - foreach (var rt in stack.Value) - { - allocationList.Add(rt.rt.name); - } - } - - allocationList.Sort(); - int index = 0; - foreach (var element in allocationList) - m_Logger.LogLine("[{0}] {1}", index++, element); + m_TexturePool.LogResources(m_Logger); } } 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 f1f3a6f4bfa..706f7af6222 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 @@ -251,18 +251,19 @@ void ExecuteWithRenderGraph( RenderRequest renderRequest, RenderGizmos(m_RenderGraph, hdCamera, colorBuffer, GizmoSubset.PostImageEffects); - ExecuteRenderGraph(m_RenderGraph, hdCamera, m_MSAASamples, renderContext, commandBuffer ); + ExecuteRenderGraph(m_RenderGraph, hdCamera, m_MSAASamples, m_FrameCount, renderContext, commandBuffer ); aovRequest.Execute(commandBuffer, aovBuffers, RenderOutputProperties.From(hdCamera)); } - static void ExecuteRenderGraph(RenderGraph renderGraph, HDCamera hdCamera, MSAASamples msaaSample, ScriptableRenderContext renderContext, CommandBuffer cmd) + static void ExecuteRenderGraph(RenderGraph renderGraph, HDCamera hdCamera, MSAASamples msaaSample, int frameIndex, ScriptableRenderContext renderContext, CommandBuffer cmd) { var renderGraphParams = new RenderGraphExecuteParams() { renderingWidth = hdCamera.actualWidth, renderingHeight = hdCamera.actualHeight, - msaaSamples = msaaSample + msaaSamples = msaaSample, + currentFrameIndex = frameIndex }; renderGraph.Execute(renderContext, cmd, renderGraphParams);