This struct is usually meant to be allocated on the game thread (where there's no FRDGBuilder, which is Render Thread-only) and allows to queue successive operations (lambdas) onto a single render command, sharing the same FRDGBuilder. This allows to sequence a list of RDG passes from the game thread and makes it possible to interleave render thread operations (in a single render command) with game thread-initiated render commands. Here's an extensive use case :
FRDGBuilderRecorder Recorder;
// Start recording commands to a single graph builder : Recorder.StartRecording();
Recorder.EnqueueRDGCommand([](FRDGBuilder* GraphBuilder) { GraphBuilder->AddPass(); }); // Append a Pass_A ENQUEUE_RENDER_COMMAND(...) // Push Render_Command_A (immediately) Recorder.EnqueueRDGCommand([](FRDGBuilder* GraphBuilder) { GraphBuilder->AddPass(); }, { { SomeTexture, ERHIAccess::RTV } }); // Append a Pass_B and inform of the final state of a given texture to prevent the RDG from auto-transitioning to SRVMask at the end Recorder.EnqueueRenderCommand([](FRHICommandListImmediate& InRHICmdList) mutable { [...]; }); // Append a Render_Command_B to the graph builder Recorder.EnqueueRDGCommand([](FRDGBuilder* GraphBuilder) { GraphBuilder->AddPass(); }, { { }SomeTexture, ERHIAccess::CopySrc } }); // Append a Pass_C and inform of the final state of a given texture to prevent the RDG from auto-transitioning to SRVMask at the end
// Stop recording and issue a render command with all that's been recorded so far Recorder.StopRecordingAndFlush(RDG_EVENT_NAME("Pass ABC"));
// Enqueue some game-thread render commands (e.g. BP render) either directly or via the recorder in immediate mode : ENQUEUE_RENDER_COMMAND(...) // Render_Command_C Recorder.EnqueueRDGCommand([](FRDGBuilder* GraphBuilder) { GraphBuilder->AddPass(); }, { { SomeTexture, ERHIAccess::CopyDst } }); // Append a Pass_D and inform of the final state of a given texture to prevent the RDG from auto-transitioning to SRVMask at the end Recorder.EnqueueRenderCommand([RHIOperation](FRHICommandListImmediate& InRHICmdList) mutable { [...]; }); // Append a Render_Command_D
// Start recording commands again to a new single graph builder : Recorder.StartRecording(); Recorder.EnqueueRDGCommand([](FRDGBuilder* GraphBuilder) { GraphBuilder->AddPass(); }); // Append a Pass_E
// Stop recording and issue a render command with all that's been recorded so far Recorder.StopRecordingAndFlush(RDG_EVENT_NAME("Pass E"));
--> Will yield the following sequence on the render thread:
- Render_Command_A
- Render Command "Pass ABC" +- (RDGBuilder_0) +- RDGBuilder_0.Pass_A +- RDGBuilder_0.Pass_B +- RDGBuilder_0.LambdaPass (Render_Command_B) +- RDGBuilder_0.Pass_C +- RDGBuilder_0.SetTextureAccessFinal(OutputTexture, ERHIAccess::CopySrc); // Only the final state recorded for a given texture is set +- RDGBuilder_0.Execute
- Render_Command_C
- Render Command +- (RDGBuilder_1) +- RDGBuilder_1.Pass_D +- RDGBuilder_1.SetTextureAccessFinal(OutputTexture, ERHIAccess::CopyDst); +- RDGBuilder_1.Execute
- Render_Command_D
- Render Command "Pass E" +- (RDGBuilder_2) +- RDGBuilder_2.Pass_E +- RDGBuilder_2.Execute