Skip to content

[HDRP] Change RenderGraph Begin/Execute function pattern to avoid leaks #5929

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 3 commits into from
Oct 19, 2021
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 @@ -30,7 +30,9 @@ To initialize a `RenderGraph` instance, call the constructor with an optional na

### Starting a render graph

Before you add any render passes to the render graph, you first need to initialize the render graph. To do this, call the `Begin` method. For details about this method's parameters, see the [API documentation](../api/UnityEngine.Experimental.Rendering.RenderGraphModule.RenderGraph.html)
Before you add any render passes to the render graph, you first need to initialize the render graph. To do this, call the `RecordAndExecute` method. This method will return a disposable struct of type `RenderGraphExecution` that you can use with a scope. When the `RenderGraphExecution` struct exits the scope or its Dispose function is called, the render graph is executed.
This pattern ensures that the render graph is always executed correctly even in the case of an exception during the recording of the graph.
For details about this method's parameters, see the [API documentation](../api/UnityEngine.Experimental.Rendering.RenderGraphModule.RenderGraph.html)

```c#
var renderGraphParams = new RenderGraphExecuteParams()
Expand All @@ -40,7 +42,10 @@ var renderGraphParams = new RenderGraphExecuteParams()
currentFrameIndex = frameIndex
};

m_RenderGraph.Begin(renderGraphParams);
using (m_RenderGraph.RecordAndExecute(renderGraphParams))
{
// Add your passes here
}
```

### Creating resources for the render graph
Expand Down Expand Up @@ -233,16 +238,6 @@ TextureHandle MyRenderPass(RenderGraph renderGraph, TextureHandle inputTexture,
}
```

### Execution of the Render Graph

After you declare all the render passes, you then need to execute the render graph. To do this, call the Execute method.

```c#
m_RenderGraph.Execute();
```

This triggers the process that compiles and executes the render graph.

### Ending the frame

Over the course of your application, the render graph needs to allocate various resources. It might use these resources for a time but then might not need them. For the graph to free up those resources, call the `EndFrame()` method once a frame. This deallocates any resources that the render graph has not used since the last frame. This also executes all internal processing the render graph requires at the end of the frame.
Expand Down
42 changes: 38 additions & 4 deletions com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,28 @@ public struct RenderGraphParameters
public CommandBuffer commandBuffer;
}

/// <summary>
/// This struct is used to define the scope where the Render Graph is recorded before the execution.
/// When this struct goes out of scope or is disposed, the Render Graph will be automatically executed.
/// </summary>
/// <seealso cref="RenderGraph.RecordAndExecute(in RenderGraphParameters)"/>
public struct RenderGraphExecution : IDisposable
{
RenderGraph renderGraph;

/// <summary>
/// Internal constructor for RenderGraphExecution
/// </summary>
/// <param name="renderGraph">renderGraph</param>
internal RenderGraphExecution(RenderGraph renderGraph)
=> this.renderGraph = renderGraph;

/// <summary>
/// This function triggers the Render Graph to be executed.
/// </summary>
public void Dispose() => renderGraph.Execute();
}

class RenderGraphDebugParams
{
DebugUI.Widget[] m_DebugItems;
Expand Down Expand Up @@ -597,11 +619,21 @@ public ComputeBufferDesc GetComputeBufferDesc(in ComputeBufferHandle computeBuff
}

/// <summary>
/// Begin using the render graph.
/// Starts the recording of the the render graph and then automatically execute when the return value goes out of scope.
/// This must be called before adding any pass to the render graph.
/// </summary>
/// <param name="parameters">Parameters necessary for the render graph execution.</param>
public void Begin(in RenderGraphParameters parameters)
/// <example>
/// This shows how to increment an integer.
/// <code>
/// using (renderGraph.RecordAndExecute(parameters))
/// {
/// // Add your render graph passes here.
/// }
/// </code>
/// </example>
/// <seealso cref="RenderGraphExecution"/>
public RenderGraphExecution RecordAndExecute(in RenderGraphParameters parameters)
{
m_CurrentFrameIndex = parameters.currentFrameIndex;
m_CurrentExecutionName = parameters.executionName;
Expand Down Expand Up @@ -641,19 +673,21 @@ public void Begin(in RenderGraphParameters parameters)

m_Resources.BeginExecute(m_CurrentFrameIndex);
}

return new RenderGraphExecution(this);
}

/// <summary>
/// Execute the Render Graph in its current state.
/// </summary>
public void Execute()
internal void Execute()
{
m_ExecutionExceptionWasRaised = false;

try
{
if (m_RenderGraphContext.cmd == null)
throw new InvalidOperationException("RenderGraph.Begin was not called before executing the render graph.");
throw new InvalidOperationException("RenderGraph.RecordAndExecute was not called before executing the render graph.");


if (!m_DebugParameters.immediateMode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,6 @@ void RecordRenderGraph(RenderRequest renderRequest,
bool msaa = hdCamera.msaaEnabled;
var target = renderRequest.target;

m_RenderGraph.Begin(new RenderGraphParameters()
{
executionName = hdCamera.name,
currentFrameIndex = m_FrameCount,
rendererListCulling = m_GlobalSettings.rendererListCulling,
scriptableRenderContext = renderContext,
commandBuffer = commandBuffer
});

// We need to initialize the MipChainInfo here, so it will be available to any render graph pass that wants to use it during setup
// Be careful, ComputePackedMipChainInfo needs the render texture size and not the viewport size. Otherwise it would compute the wrong size.
hdCamera.depthBufferMipChainInfo.ComputePackedMipChainInfo(RTHandles.rtHandleProperties.currentRenderTargetSize);
Expand Down Expand Up @@ -336,11 +327,20 @@ void ExecuteWithRenderGraph(RenderRequest renderRequest,
ScriptableRenderContext renderContext,
CommandBuffer commandBuffer)
{
RecordRenderGraph(
renderRequest, aovRequest, aovBuffers,
aovCustomPassBuffers, renderContext, commandBuffer);
using (m_RenderGraph.RecordAndExecute(new RenderGraphParameters
{
executionName = renderRequest.hdCamera.name,
currentFrameIndex = m_FrameCount,
rendererListCulling = m_GlobalSettings.rendererListCulling,
scriptableRenderContext = renderContext,
commandBuffer = commandBuffer
}))
{
RecordRenderGraph(
renderRequest, aovRequest, aovBuffers,
aovCustomPassBuffers, renderContext, commandBuffer);
}

m_RenderGraph.Execute();

if (aovRequest.isValid)
{
Expand Down