keywords: UE5, RDG, Render Dependency Graph

Related article: [UE4]RHI (Rendering Hardware Interface) Notes

How to debug RDG (inspect the detail of GPU crash and CPU overhead)

Engine/Config/ConsoleVariables.ini

; Uncomment to get render graph executing passes as they get created to easily debug crashes caused by pass wiring logic.
r.RDG.ImmediateMode=1
; Uncomment to get render graph to emit warnings for inneficiencies that are normally too CPU costly, and have draw event names
; formatting even if frame are not emiting draw events.
r.RDG.Debug=1
How to profile RDG
  • SCOPED_NAMED_EVENT : Tracking for the CPU timeline, which was shown in the CPU thread (GameThread, RenderThread etc.) panels;
  • SCOPE_CYCLE_COUNTER : Tracking time overheads that was shown in stat console command, it’s more convenient than Insights to get a quick view of performance. For more details, see SCOPE_CYCLE_COUNTER Usage.
  • RDG_EVENT_SCOPE : Add a GPU tracking scope around passes. These are consumed by external profilers like RenderDoc, PIX (Windows), Snapdragon (Android), as well as RDG Insights.
  • RDG_GPU_STAT_SCOPE : Add a new scope for the console command: stat gpu and profilegpu .
  • RDG_CSV_STAT_EXCLUSIVE_SCOPE : Add a new scope for the CSV profiler.

Insights command:

MyGameSample\Binaries\Win64\MyGameSample.exe -trace=default,rdg
Pass and Dispatch (ComputeShader)

Relationship between Pass and Dispatch, take Compute Shader as example:

  • Pass was created and inserted into queue in the RenderThread, this’s the main job of RDG.
  • The input data of Pass was composed in the async threads of Task Graph, when input data was ready the RHIThread consume and execute (Dispatch or IndirectDispatch) these passes which were produced in RenderThread.
  • Most passes cannot be merged, especially in forward shading, so one Pass represents to one Dispatch in most situations.
RenderThread: AddPass
UnrealEditor-RenderCore.dll!TRDGHandleRegistry<TRDGHandle<FRDGPass,unsigned short>,0>::Insert(FRDGPass * Object) Line 425
[Inline Frame] UnrealEditor-RenderCore.dll!FRDGBuilder::AddPassInternal(FRDGEventName &&) Line 278
[Inline Frame] UnrealEditor-RenderCore.dll!FRDGBuilder::AddPass(FRDGEventName &&) Line 293
UnrealEditor-RenderCore.dll!FComputeShaderUtils::AddPass<FRDGScatterCopyCS>(FRDGBuilder & GraphBuilder, FRDGEventName && PassName, ERDGPassFlags PassFlags, const TShaderRefBase<FRDGScatterCopyCS,FShaderMapPointerTable> & ComputeShader, const FShaderParametersMetadata * ParametersMetadata, FRDGScatterCopyCS::FParameters * Parameters, UE::Math::TIntVector3<int> GroupCount) Line 564
[Inline Frame] UnrealEditor-RenderCore.dll!FComputeShaderUtils::AddPass(FRDGBuilder &) Line 632
UnrealEditor-RenderCore.dll!ScatterCopyResource(FRDGBuilder & GraphBuilder, FRDGViewableResource * DstResource, FRDGBufferSRV * ScatterBufferSRV, FRDGBufferSRV * UploadBufferSRV, const FScatterCopyParams & Params) Line 801
UnrealEditor-RenderCore.dll!FRDGScatterUploadBuffer::ResourceUploadToInternal(FRDGBuilder & GraphBuilder, FRDGViewableResource * DstResource) Line 902
[Inline Frame] UnrealEditor-Renderer.dll!FRDGScatterUploadBuffer::ResourceUploadTo(FRDGBuilder &) Line 427
[Inline Frame] UnrealEditor-Renderer.dll!FVirtualShadowMapArray::UploadProjectionData(FRDGBuilder &) Line 1185
UnrealEditor-Renderer.dll!FVirtualShadowMapArray::BuildPageAllocations(FRDGBuilder & GraphBuilder, const FMinimalSceneTextures & SceneTextures, const TArrayView<FViewInfo const ,int> & Views, const FEngineShowFlags & EngineShowFlags, const FSortedLightSetSceneInfo & SortedLightsInfo, const TArrayView<FVisibleLightInfo const ,int> & VisibleLightInfos, const FSingleLayerWaterPrePassResult * SingleLayerWaterPrePassResult, const FFrontLayerTranslucencyData & FrontLayerTranslucencyData) Line 1259
UnrealEditor-Renderer.dll!FDeferredShadingSceneRenderer::Render(FRDGBuilder & GraphBuilder) Line 3748
UnrealEditor-Renderer.dll!RenderViewFamilies_RenderThread(FRHICommandListImmediate & RHICmdList, const TArray<FSceneRenderer *,TSizedDefaultAllocator<32>> & SceneRenderers) Line 4575
UnrealEditor-Renderer.dll!FRendererModule::BeginRenderingViewFamilies::__l87::<lambda_3>::operator()(FRHICommandListImmediate & RHICmdList) Line 4849
UnrealEditor-Renderer.dll!TEnqueueUniqueRenderCommandType<`FRendererModule::BeginRenderingViewFamilies'::`87'::FDrawSceneCommandName,`FRendererModule::BeginRenderingViewFamilies'::`87'::<lambda_3>>::DoTask(ENamedThreads::Type CurrentThread, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 209
UnrealEditor-Renderer.dll!TGraphTask<TEnqueueUniqueRenderCommandType<`FRendererModule::BeginRenderingViewFamilies'::`87'::FDrawSceneCommandName,`FRendererModule::BeginRenderingViewFamilies'::`87'::<lambda_3>>>::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & NewTasks, ENamedThreads::Type CurrentThread, bool bDeleteOnCompletion) Line 1265
[Inline Frame] UnrealEditor-Core.dll!FBaseGraphTask::Execute(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & CurrentThread, ENamedThreads::Type) Line 866
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksNamedThread(int QueueIndex, bool bAllowStall) Line 758
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksUntilQuit(int QueueIndex) Line 649
UnrealEditor-RenderCore.dll!RenderingThreadMain(FEvent * TaskGraphBoundSyncEvent) Line 411
UnrealEditor-RenderCore.dll!FRenderingThread::Run() Line 564
UnrealEditor-Core.dll!FRunnableThreadWin::Run() Line 149
UnrealEditor-Core.dll!FRunnableThreadWin::GuardedRun() Line 71
Async Task Thread(Background Worker): After the calculation of input data, fire the Dispath on RenderThread

Compose the input data (e.g. textures that can be sampled) in async thread, then return into RenderThread to execute the pass (Dispatch or IndrectDispatch):

UnrealEditor-HairStrandsCore.dll!FComputeShaderUtils::Dispatch<FClearVelocityGridCS>(FRHIComputeCommandList & RHICmdList, const TShaderRefBase<FClearVelocityGridCS,FShaderMapPointerTable> & ComputeShader, const FShaderParametersMetadata * ParametersMetadata, const FClearVelocityGridCS::FParameters & Parameters, UE::Math::TIntVector3<int> GroupCount) Line 498
[Inline Frame] UnrealEditor-HairStrandsCore.dll!FComputeShaderUtils::AddPass::__l2::<lambda_1>::operator()(FRHIComputeCommandList &) Line 571
[Inline Frame] UnrealEditor-HairStrandsCore.dll!TRDGLambdaPass<void,`FComputeShaderUtils::AddPass<FClearVelocityGridCS>'::`2'::<lambda_1>>::ExecuteLambdaFunc(FRHIComputeCommandList &) Line 607
UnrealEditor-HairStrandsCore.dll!TRDGLambdaPass<void,`FComputeShaderUtils::AddPass<FClearVelocityGridCS>'::`2'::<lambda_1>>::Execute(FRHIComputeCommandList & RHICmdList) Line 616
UnrealEditor-RenderCore.dll!FRDGBuilder::ExecutePass(FRDGPass * Pass, FRHIComputeCommandList & RHICmdListPass) Line 2917
UnrealEditor-RenderCore.dll!FRDGBuilder::DispatchParallelExecute::__l13::<lambda_1>::operator()() Line 2729
UnrealEditor-RenderCore.dll!UE::Tasks::Private::FTaskBase::TryExecuteTask() Line 436
[Inline Frame] UnrealEditor-RenderCore.dll!UE::Tasks::Private::FTaskBase::Init::__l2::<lambda_1>::operator()() Line 157
[Inline Frame] UnrealEditor-RenderCore.dll!LowLevelTasks::FTask::Init::__l11::<lambda_1>::operator()(const bool) Line 499
[Inline Frame] UnrealEditor-RenderCore.dll!Invoke(LowLevelTasks::FTask::Init::__l11::<lambda_1> &) Line 47
[Inline Frame] UnrealEditor-RenderCore.dll!LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2'::<lambda_1>>'::`11'::<lambda_1>,0>::Call(void *) Line 162
UnrealEditor-RenderCore.dll!LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2'::<lambda_1>>'::`11'::<lambda_1>,0>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> & Destination, void * InlineData, unsigned int DestInlineSize, bool <Params_0>) Line 171
[Inline Frame] UnrealEditor-Core.dll!LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> &) Line 308
UnrealEditor-Core.dll!LowLevelTasks::FTask::ExecuteTask() Line 627
UnrealEditor-Core.dll!LowLevelTasks::FScheduler::ExecuteTask(LowLevelTasks::FTask * & InOutTask) Line 150
UnrealEditor-Core.dll!LowLevelTasks::FScheduler::TryExecuteTaskFrom<LowLevelTasks::TLocalQueueRegistry<1024>::TLocalQueue,&LowLevelTasks::TLocalQueueRegistry<1024>::TLocalQueue::DequeueGlobal,0>(LowLevelTasks::TLocalQueueRegistry<1024>::TLocalQueue * Queue, LowLevelTasks::TLocalQueueRegistry<1024>::FOutOfWork & OutOfWork, bool bPermitBackgroundWork, bool bDisableThrottleStealing) Line 350
UnrealEditor-Core.dll!LowLevelTasks::FScheduler::WorkerMain(LowLevelTasks::FSleepEvent * WorkerEvent, LowLevelTasks::TLocalQueueRegistry<1024>::TLocalQueue * WorkerLocalQueue, unsigned int WaitCycles, bool bPermitBackgroundWork) Line 378
[Inline Frame] UnrealEditor-Core.dll!LowLevelTasks::FScheduler::CreateWorker::__l2::<lambda>() Line 70
[Inline Frame] UnrealEditor-Core.dll!Invoke(LowLevelTasks::FScheduler::CreateWorker::__l2::void <lambda>(const TArray<FString,TSizedDefaultAllocator<32>> &, UWorld *, FOutputDevice &) &) Line 47
UnrealEditor-Core.dll!UE::Core::Private::Function::TFunctionRefCaller<`LowLevelTasks::FScheduler::CreateWorker'::`2'::void <lambda>(const TArray<FString,TSizedDefaultAllocator<32>> &, UWorld *, FOutputDevice &),void __cdecl(void)>::Call(void * Obj) Line 480
[Inline Frame] UnrealEditor-Core.dll!UE::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::TFunctionStorage<1>,void __cdecl(void)>::operator()() Line 629
UnrealEditor-Core.dll!FThreadImpl::Run() Line 69
UnrealEditor-Core.dll!FRunnableThreadWin::Run() Line 149
UnrealEditor-Core.dll!FRunnableThreadWin::GuardedRun() Line 71
RHIThread: Command Queue (CommandList) cycle to ID3D12GraphicsCommandList::Dispatch()
UnrealEditor-D3D12RHI.dll!FD3D12CommandContext::RHIDispatchComputeShader(unsigned int ThreadGroupCountX, unsigned int ThreadGroupCountY, unsigned int ThreadGroupCountZ) Line 114
UnrealEditor-Renderer.dll!FRHICommand<FRHICommandDispatchComputeShader,FRHICommandDispatchComputeShaderString1652>::ExecuteAndDestruct(FRHICommandListBase & CmdList, FRHICommandListDebugContext & Context) Line 1245
UnrealEditor-RHI.dll!FRHICommandListBase::Execute(TRHIPipelineArray<IRHIComputeContext *> & InOutContexts, FRHICommandListBase::FPersistentState::FGPUStats * ParentStats) Line 461
UnrealEditor-RHI.dll!FRHICommandListImmediate::QueueAsyncCommandListSubmit::__l50::<lambda>(FRHICommandListBase & ParentCmdList) Line 668
UnrealEditor-RHI.dll!TRHILambdaCommand<FRHICommandListImmediate,`FRHICommandListImmediate::QueueAsyncCommandListSubmit'::`50'::void <lambda>(const TArray<FString,TSizedDefaultAllocator<32>> &, UWorld *, FOutputDevice &)>::ExecuteAndDestruct(FRHICommandListBase & CmdList, FRHICommandListDebugContext & __formal) Line 471
UnrealEditor-RHI.dll!FRHICommandListBase::Execute(TRHIPipelineArray<IRHIComputeContext *> & InOutContexts, FRHICommandListBase::FPersistentState::FGPUStats * ParentStats) Line 461
UnrealEditor-RHI.dll!FRHICommandListImmediate::ExecuteAndReset::__l35::<lambda>() Line 772
[Inline Frame] UnrealEditor-RHI.dll!UE::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::TFunctionStorage<1>,void __cdecl(void)>::operator()() Line 629
[Inline Frame] UnrealEditor-RHI.dll!TFunctionGraphTaskImpl<void __cdecl(void),0>::DoTaskImpl(TUniqueFunction<void __cdecl(void)> & Function, ENamedThreads::Type) Line 1754
[Inline Frame] UnrealEditor-RHI.dll!TFunctionGraphTaskImpl<void __cdecl(void),0>::DoTask(ENamedThreads::Type) Line 1747
UnrealEditor-RHI.dll!TGraphTask<TFunctionGraphTaskImpl<void __cdecl(void),0>>::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & NewTasks, ENamedThreads::Type CurrentThread, bool bDeleteOnCompletion) Line 1265
[Inline Frame] UnrealEditor-Core.dll!FBaseGraphTask::Execute(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & CurrentThread, ENamedThreads::Type) Line 866
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksNamedThread(int QueueIndex, bool bAllowStall) Line 758
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksUntilQuit(int QueueIndex) Line 649
References

Render Dependency Graph, An immediate-mode API which records render commands into a graph data structure to be compiled and executed.
https://dev.epicgames.com/documentation/en-us/unreal-engine/render-dependency-graph-in-unreal-engine
RDG Insights Plugin
https://dev.epicgames.com/documentation/en-us/unreal-engine/render-dependency-graph-in-unreal-engine#rdginsightsplugin

Rendering Dependency Graph
https://mcro.de/c/rdg

RDG Insights, Quick Start Guide - unrealengine.com PDF

RDG 101: A Crash Course (PPT)
https://epicgames.ent.box.com/s/ul1h44ozs0t2850ug0hrohlzm53kxwrz

UE5 Render Dependency Graph-实用指南
https://zhuanlan.zhihu.com/p/637889120

Implement a single Static Mesh renderer with your own mesh path
https://strv.dev/blog/unrealengine--lets-implement-a-single-mesh-renderer/
https://strv.dev/blog/unrealengine--lets-implement-a-single-mesh-renderer-2/


Tell me and I’ll forget; show me and I may remember; involve me and I’ll understand. -Chinese Proverbs