Skip to content

Adding purge of unused resources in render graph. #872

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ public ref struct RenderGraphContext
public struct RenderGraphExecuteParams
{
///<summary>Rendering width.</summary>
public int renderingWidth;
public int renderingWidth;
///<summary>Rendering height.</summary>
public int renderingHeight;
public int renderingHeight;
///<summary>Number of MSAA samples.</summary>
public MSAASamples msaaSamples;
public MSAASamples msaaSamples;
///<summary>Index of the current frame being rendered.</summary>
public int currentFrameIndex;
}

class RenderGraphDebugParams
Expand Down Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, List<(RTHandle resource, int frameIndex)>> m_ResourcePool = new Dictionary<int, List<(RTHandle resource, int frameIndex)>>();
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<string> allocationList = new List<string>();
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);
}
}

/// <summary>
/// The RenderGraphResourceRegistry holds all resource allocated during Render Graph execution.
/// </summary>
Expand Down Expand Up @@ -393,17 +472,19 @@ internal ComputeBufferResource(ComputeBuffer computeBuffer, bool imported)
#endregion

DynamicArray<TextureResource> m_TextureResources = new DynamicArray<TextureResource>();
Dictionary<int, Stack<RTHandle>> m_TexturePool = new Dictionary<int, Stack<RTHandle>>();
RenderGraphTexturePool m_TexturePool = new RenderGraphTexturePool();
DynamicArray<RendererListResource> m_RendererListResources = new DynamicArray<RendererListResource>();
DynamicArray<ComputeBufferResource> m_ComputeBufferResources = new DynamicArray<ComputeBufferResource>();
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
/// <summary>
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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<RTHandle>();
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
}

Expand Down Expand Up @@ -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<RendererListHandle> rendererLists)
Expand All @@ -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}";
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -836,19 +905,7 @@ void LogResources()
{
m_Logger.LogLine("==== Allocated Resources ====\n");

List<string> allocationList = new List<string>();
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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down