Skip to content

Commit 8709afc

Browse files
Adding purge of unused resources in render graph. (#872)
1 parent 1c4599f commit 8709afc

File tree

3 files changed

+112
-53
lines changed

3 files changed

+112
-53
lines changed

com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ public ref struct RenderGraphContext
4242
public struct RenderGraphExecuteParams
4343
{
4444
///<summary>Rendering width.</summary>
45-
public int renderingWidth;
45+
public int renderingWidth;
4646
///<summary>Rendering height.</summary>
47-
public int renderingHeight;
47+
public int renderingHeight;
4848
///<summary>Number of MSAA samples.</summary>
49-
public MSAASamples msaaSamples;
49+
public MSAASamples msaaSamples;
50+
///<summary>Index of the current frame being rendered.</summary>
51+
public int currentFrameIndex;
5052
}
5153

5254
class RenderGraphDebugParams
@@ -362,8 +364,7 @@ public void Execute(ScriptableRenderContext renderContext, CommandBuffer cmd, in
362364
{
363365
m_Logger.Initialize();
364366

365-
// Update RTHandleSystem with size for this rendering pass.
366-
m_Resources.SetRTHandleReferenceSize(parameters.renderingWidth, parameters.renderingHeight, parameters.msaaSamples);
367+
m_Resources.BeginRender(parameters.renderingWidth, parameters.renderingHeight, parameters.msaaSamples, parameters.currentFrameIndex);
367368

368369
LogFrameInformation(parameters.renderingWidth, parameters.renderingHeight);
369370

com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs

Lines changed: 102 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,85 @@ public override int GetHashCode()
322322
}
323323
#endregion
324324

325+
class RenderGraphTexturePool
326+
{
327+
// 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).
328+
Dictionary<int, List<(RTHandle resource, int frameIndex)>> m_ResourcePool = new Dictionary<int, List<(RTHandle resource, int frameIndex)>>();
329+
static int s_CurrentFrameIndex;
330+
331+
public void ReleaseResource(int hash, RTHandle rt, int currentFrameIndex)
332+
{
333+
if (!m_ResourcePool.TryGetValue(hash, out var list))
334+
{
335+
list = new List<(RTHandle rt, int frameIndex)>();
336+
m_ResourcePool.Add(hash, list);
337+
}
338+
339+
list.Add((rt, currentFrameIndex));
340+
}
341+
342+
public bool TryGetResource(int hashCode, out RTHandle rt)
343+
{
344+
if (m_ResourcePool.TryGetValue(hashCode, out var list) && list.Count > 0)
345+
{
346+
rt = list[list.Count - 1].resource;
347+
list.RemoveAt(list.Count - 1); // O(1) since it's the last element.
348+
return true;
349+
}
350+
351+
rt = null;
352+
return false;
353+
}
354+
355+
public void PurgeUnusedResources(int currentFrameIndex)
356+
{
357+
// Update the frame index for the lambda. Static because we don't want to capture.
358+
s_CurrentFrameIndex = currentFrameIndex;
359+
360+
foreach(var kvp in m_ResourcePool)
361+
{
362+
var list = kvp.Value;
363+
list.RemoveAll(obj =>
364+
{
365+
if (obj.frameIndex < s_CurrentFrameIndex)
366+
{
367+
obj.resource.Release();
368+
return true;
369+
}
370+
return false;
371+
});
372+
}
373+
}
374+
375+
public void Cleanup()
376+
{
377+
foreach (var kvp in m_ResourcePool)
378+
{
379+
foreach (var res in kvp.Value)
380+
{
381+
res.resource.Release();
382+
}
383+
}
384+
}
385+
386+
public void LogResources(RenderGraphLogger logger)
387+
{
388+
List<string> allocationList = new List<string>();
389+
foreach (var kvp in m_ResourcePool)
390+
{
391+
foreach (var res in kvp.Value)
392+
{
393+
allocationList.Add(res.resource.rt.name);
394+
}
395+
}
396+
397+
allocationList.Sort();
398+
int index = 0;
399+
foreach (var element in allocationList)
400+
logger.LogLine("[{0}] {1}", index++, element);
401+
}
402+
}
403+
325404
/// <summary>
326405
/// The RenderGraphResourceRegistry holds all resource allocated during Render Graph execution.
327406
/// </summary>
@@ -393,17 +472,19 @@ internal ComputeBufferResource(ComputeBuffer computeBuffer, bool imported)
393472
#endregion
394473

395474
DynamicArray<TextureResource> m_TextureResources = new DynamicArray<TextureResource>();
396-
Dictionary<int, Stack<RTHandle>> m_TexturePool = new Dictionary<int, Stack<RTHandle>>();
475+
RenderGraphTexturePool m_TexturePool = new RenderGraphTexturePool();
397476
DynamicArray<RendererListResource> m_RendererListResources = new DynamicArray<RendererListResource>();
398477
DynamicArray<ComputeBufferResource> m_ComputeBufferResources = new DynamicArray<ComputeBufferResource>();
399478
RTHandleSystem m_RTHandleSystem = new RTHandleSystem();
400479
RenderGraphDebugParams m_RenderGraphDebug;
401480
RenderGraphLogger m_Logger;
481+
int m_CurrentFrameIndex;
402482

403483
RTHandle m_CurrentBackbuffer;
404484

405485
// Diagnostic only
406-
List<(int, RTHandle)> m_AllocatedTextures = new List<(int, RTHandle)>();
486+
// This list allows us to determine if all textures were correctly released in the frame.
487+
List<(int, RTHandle)> m_FrameAllocatedTextures = new List<(int, RTHandle)>();
407488

408489
#region Public Interface
409490
/// <summary>
@@ -470,8 +551,10 @@ internal RenderGraphResourceRegistry(bool supportMSAA, MSAASamples initialSample
470551
m_Logger = logger;
471552
}
472553

473-
internal void SetRTHandleReferenceSize(int width, int height, MSAASamples msaaSamples)
554+
internal void BeginRender(int width, int height, MSAASamples msaaSamples, int currentFrameIndex)
474555
{
556+
m_CurrentFrameIndex = currentFrameIndex;
557+
// Update RTHandleSystem with size for this rendering pass.
475558
m_RTHandleSystem.SetReferenceSize(width, height, msaaSamples);
476559
}
477560

@@ -618,7 +701,7 @@ void CreateTextureForPass(ref TextureResource resource)
618701
#if DEVELOPMENT_BUILD || UNITY_EDITOR
619702
if (hashCode != -1)
620703
{
621-
m_AllocatedTextures.Add((hashCode, resource.rt));
704+
m_FrameAllocatedTextures.Add((hashCode, resource.rt));
622705
}
623706
#endif
624707

@@ -675,16 +758,10 @@ internal void ReleaseTexture(RenderGraphContext rgContext, TextureHandle resourc
675758

676759
void ReleaseTextureResource(int hash, RTHandle rt)
677760
{
678-
if (!m_TexturePool.TryGetValue(hash, out var stack))
679-
{
680-
stack = new Stack<RTHandle>();
681-
m_TexturePool.Add(hash, stack);
682-
}
683-
684-
stack.Push(rt);
761+
m_TexturePool.ReleaseResource(hash, rt, m_CurrentFrameIndex);
685762

686763
#if DEVELOPMENT_BUILD || UNITY_EDITOR
687-
m_AllocatedTextures.Remove((hash, rt));
764+
m_FrameAllocatedTextures.Remove((hash, rt));
688765
#endif
689766
}
690767

@@ -746,14 +823,7 @@ void ValidateRendererListDesc(in RendererListDesc desc)
746823

747824
bool TryGetRenderTarget(int hashCode, out RTHandle rt)
748825
{
749-
if (m_TexturePool.TryGetValue(hashCode, out var stack) && stack.Count > 0)
750-
{
751-
rt = stack.Pop();
752-
return true;
753-
}
754-
755-
rt = null;
756-
return false;
826+
return m_TexturePool.TryGetResource(hashCode, out rt);
757827
}
758828

759829
internal void CreateRendererLists(List<RendererListHandle> rendererLists)
@@ -778,11 +848,11 @@ internal void Clear(bool onException)
778848
m_ComputeBufferResources.Clear();
779849

780850
#if DEVELOPMENT_BUILD || UNITY_EDITOR
781-
if (m_AllocatedTextures.Count != 0 && !onException)
851+
if (m_FrameAllocatedTextures.Count != 0 && !onException)
782852
{
783-
string logMessage = "RenderGraph: Not all textures were released.";
853+
string logMessage = "RenderGraph: Not all textures were released. This can be caused by a textures being allocated but never read by any pass.";
784854

785-
List<(int, RTHandle)> tempList = new List<(int, RTHandle)>(m_AllocatedTextures);
855+
List<(int, RTHandle)> tempList = new List<(int, RTHandle)>(m_FrameAllocatedTextures);
786856
foreach (var value in tempList)
787857
{
788858
logMessage = $"{logMessage}\n\t{value.Item2.name}";
@@ -792,10 +862,15 @@ internal void Clear(bool onException)
792862
Debug.LogWarning(logMessage);
793863
}
794864

795-
// If an error occurred during execution, it's expected that textures are not all release so we clear the trakcing list.
865+
// If an error occurred during execution, it's expected that textures are not all released so we clear the tracking list.
796866
if (onException)
797-
m_AllocatedTextures.Clear();
867+
m_FrameAllocatedTextures.Clear();
798868
#endif
869+
870+
// TODO RENDERGRAPH: Might not be ideal to purge stale resources every frame.
871+
// In case users enable/disable features along a level it might provoke performance spikes when things are reallocated...
872+
// Will be much better when we have actual resource aliasing and we can manage memory more efficiently.
873+
m_TexturePool.PurgeUnusedResources(m_CurrentFrameIndex);
799874
}
800875

801876
internal void ResetRTHandleReferenceSize(int width, int height)
@@ -805,13 +880,7 @@ internal void ResetRTHandleReferenceSize(int width, int height)
805880

806881
internal void Cleanup()
807882
{
808-
foreach (var value in m_TexturePool)
809-
{
810-
foreach (var rt in value.Value)
811-
{
812-
m_RTHandleSystem.Release(rt);
813-
}
814-
}
883+
m_TexturePool.Cleanup();
815884
}
816885

817886
void LogTextureCreation(RTHandle rt, bool cleared)
@@ -836,19 +905,7 @@ void LogResources()
836905
{
837906
m_Logger.LogLine("==== Allocated Resources ====\n");
838907

839-
List<string> allocationList = new List<string>();
840-
foreach (var stack in m_TexturePool)
841-
{
842-
foreach (var rt in stack.Value)
843-
{
844-
allocationList.Add(rt.rt.name);
845-
}
846-
}
847-
848-
allocationList.Sort();
849-
int index = 0;
850-
foreach (var element in allocationList)
851-
m_Logger.LogLine("[{0}] {1}", index++, element);
908+
m_TexturePool.LogResources(m_Logger);
852909
}
853910
}
854911

com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,18 +251,19 @@ void ExecuteWithRenderGraph( RenderRequest renderRequest,
251251

252252
RenderGizmos(m_RenderGraph, hdCamera, colorBuffer, GizmoSubset.PostImageEffects);
253253

254-
ExecuteRenderGraph(m_RenderGraph, hdCamera, m_MSAASamples, renderContext, commandBuffer );
254+
ExecuteRenderGraph(m_RenderGraph, hdCamera, m_MSAASamples, m_FrameCount, renderContext, commandBuffer );
255255

256256
aovRequest.Execute(commandBuffer, aovBuffers, RenderOutputProperties.From(hdCamera));
257257
}
258258

259-
static void ExecuteRenderGraph(RenderGraph renderGraph, HDCamera hdCamera, MSAASamples msaaSample, ScriptableRenderContext renderContext, CommandBuffer cmd)
259+
static void ExecuteRenderGraph(RenderGraph renderGraph, HDCamera hdCamera, MSAASamples msaaSample, int frameIndex, ScriptableRenderContext renderContext, CommandBuffer cmd)
260260
{
261261
var renderGraphParams = new RenderGraphExecuteParams()
262262
{
263263
renderingWidth = hdCamera.actualWidth,
264264
renderingHeight = hdCamera.actualHeight,
265-
msaaSamples = msaaSample
265+
msaaSamples = msaaSample,
266+
currentFrameIndex = frameIndex
266267
};
267268

268269
renderGraph.Execute(renderContext, cmd, renderGraphParams);

0 commit comments

Comments
 (0)