// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once #include "RenderGraphDefinitions.h" #include "ProfilingDebugging/RealtimeGPUProfiler.h" /** A helper profiler class for tracking and evaluating hierarchical scopes in the context of render graph. */ template class FRDGScopeStack final { static constexpr uint32 kScopeStackDepthMax = 8; public: using FPushFunction = void(*)(FRHICommandListImmediate&, const TScopeType*); using FPopFunction = void(*)(FRHICommandListImmediate&); FRDGScopeStack(FRHICommandListImmediate& InRHICmdList, FPushFunction InPushFunction, FPopFunction InPopFunction); ~FRDGScopeStack(); ////////////////////////////////////////////////////////////////////////// //! Called during graph setup phase. /** Call to begin recording a scope. */ template void BeginScope(FScopeConstructArgs... ScopeConstructArgs); /** Call to end recording a scope. */ void EndScope(); ////////////////////////////////////////////////////////////////////////// //! Called during graph execute phase. /** Call prior to executing the graph. */ void BeginExecute(); /** Call prior to executing a pass in the graph. */ void BeginExecutePass(const TScopeType* ParentScope); /** Call after executing the graph. */ void EndExecute(); ////////////////////////////////////////////////////////////////////////// const TScopeType* GetCurrentScope() const { return CurrentScope; } FRHICommandListImmediate& RHICmdList; FMemStackBase& MemStack; private: void ClearScopes(); FPushFunction PushFunction; FPopFunction PopFunction; /** The top of the scope stack during setup. */ const TScopeType* CurrentScope = nullptr; /** Tracks scopes allocated through MemStack for destruction. */ TArray Scopes; /** Stacks of scopes pushed to the RHI command list during execution. */ TStaticArray ScopeStack; }; class FRDGPass; class FRDGBuilder; ////////////////////////////////////////////////////////////////////////// // // GPU Events - Named hierarchical events emitted to external profiling tools. // ////////////////////////////////////////////////////////////////////////// /** Stores a GPU event name for the render graph. Draw events can be compiled out entirely from * a release build for performance. */ class RENDERCORE_API FRDGEventName final { public: FRDGEventName() = default; explicit FRDGEventName(const TCHAR* EventFormat, ...); ~FRDGEventName(); FRDGEventName(const FRDGEventName& Other); FRDGEventName(FRDGEventName&& Other); FRDGEventName& operator=(const FRDGEventName& Other); FRDGEventName& operator=(FRDGEventName&& Other); const TCHAR* GetTCHAR() const; private: #if RDG_EVENTS == RDG_EVENTS_STRING_REF || RDG_EVENTS == RDG_EVENTS_STRING_COPY const TCHAR* EventName; #if RDG_EVENTS == RDG_EVENTS_STRING_COPY FString EventNameStorage; #endif #endif }; class FRDGEventScope final { public: FRDGEventScope(const FRDGEventScope* InParentScope, FRDGEventName&& InName) : ParentScope(InParentScope) , Name(Forward(InName)) {} const FRDGEventScope* const ParentScope; const FRDGEventName Name; }; /** Manages a stack of event scopes. Scopes are recorded ahead of time in a hierarchical fashion * and later executed topologically during pass execution. */ class RENDERCORE_API FRDGEventScopeStack final { public: FRDGEventScopeStack(FRHICommandListImmediate& RHICmdList); ////////////////////////////////////////////////////////////////////////// //! Called during graph setup phase. /** Call to begin recording an event scope. */ void BeginScope(FRDGEventName&& EventName); /** Call to end recording an event scope. */ void EndScope(); ////////////////////////////////////////////////////////////////////////// //! Called during graph execute phase. /** Call prior to executing the graph. */ void BeginExecute(); /** Call prior to executing a pass in the graph. */ void BeginExecutePass(const FRDGPass* Pass); /** Call after executing a pass in the graph. */ void EndExecutePass(); /** Call after executing the graph. */ void EndExecute(); ////////////////////////////////////////////////////////////////////////// const FRDGEventScope* GetCurrentScope() const { return ScopeStack.GetCurrentScope(); } private: static bool IsEnabled(); FRDGScopeStack ScopeStack; }; /** RAII class for begin / end of an event scope through the graph builder. */ class RENDERCORE_API FRDGEventScopeGuard final { public: FRDGEventScopeGuard(FRDGBuilder& InGraphBuilder, FRDGEventName&& ScopeName); FRDGEventScopeGuard(const FRDGEventScopeGuard&) = delete; ~FRDGEventScopeGuard(); private: FRDGBuilder& GraphBuilder; }; /** Macros for create render graph event names and scopes. * * FRDGEventName Name = RDG_EVENT_NAME("MyPass %sx%s", ViewRect.Width(), ViewRect.Height()); * * RDG_EVENT_SCOPE(GraphBuilder, "MyProcessing %sx%s", ViewRect.Width(), ViewRect.Height()); */ #if RDG_EVENTS == RDG_EVENTS_STRING_REF || RDG_EVENTS == RDG_EVENTS_STRING_COPY #define RDG_EVENT_NAME(Format, ...) FRDGEventName(TEXT(Format), ##__VA_ARGS__) #define RDG_EVENT_SCOPE(GraphBuilder, Format, ...) FRDGEventScopeGuard PREPROCESSOR_JOIN(__RDG_ScopeRef_,__LINE__) ((GraphBuilder), RDG_EVENT_NAME(Format, ##__VA_ARGS__)) #elif RDG_EVENTS == RDG_EVENTS_NONE #define RDG_EVENT_NAME(Format, ...) FRDGEventName() #define RDG_EVENT_SCOPE(GraphBuilder, Format, ...) #else #error "RDG_EVENTS is not a valid value." #endif ////////////////////////////////////////////////////////////////////////// // // GPU Stats - Aggregated counters emitted to the runtime 'stat GPU' profiler. // ////////////////////////////////////////////////////////////////////////// class FRDGStatScope final { public: FRDGStatScope(const FRDGStatScope* InParentScope, const FName& InName, const FName& InStatName) : ParentScope(InParentScope) , Name(InName) , StatName(InStatName) {} const FRDGStatScope* const ParentScope; const FName Name; const FName StatName; }; class RENDERCORE_API FRDGStatScopeStack final { public: FRDGStatScopeStack(FRHICommandListImmediate& RHICmdList); ////////////////////////////////////////////////////////////////////////// //! Called during graph setup phase. /** Call to begin recording a stat scope. */ void BeginScope(const FName& Name, const FName& StatName); /** Call to end recording a stat scope. */ void EndScope(); ////////////////////////////////////////////////////////////////////////// //! Called during graph execute phase. /** Call prior to executing the graph. */ void BeginExecute(); /** Call prior to executing a pass in the graph. */ void BeginExecutePass(const FRDGPass* Pass); /** Call after executing the graph. */ void EndExecute(); ////////////////////////////////////////////////////////////////////////// const FRDGStatScope* GetCurrentScope() const { return ScopeStack.GetCurrentScope(); } private: static bool IsEnabled(); FRDGScopeStack ScopeStack; }; /** RAII class for begin / end of a stat scope through the graph builder. */ class RENDERCORE_API FRDGStatScopeGuard final { public: FRDGStatScopeGuard(FRDGBuilder& InGraphBuilder, const FName& Name, const FName& StatName); FRDGStatScopeGuard(const FRDGStatScopeGuard&) = delete; ~FRDGStatScopeGuard(); private: FRDGBuilder& GraphBuilder; }; #if HAS_GPU_STATS #if STATS #define RDG_GPU_STAT_SCOPE(GraphBuilder, StatName) FRDGStatScopeGuard PREPROCESSOR_JOIN(GPUStatEvent_##StatName,__LINE__) ((GraphBuilder), CSV_STAT_FNAME(StatName), GET_STATID(Stat_GPU_##StatName).GetName()); #else #define RDG_GPU_STAT_SCOPE(GraphBuilder, StatName) FRDGStatScopeGuard PREPROCESSOR_JOIN(GPUStatEvent_##StatName,__LINE__) ((GraphBuilder), CSV_STAT_FNAME(StatName), FName()); #endif #else #define RDG_GPU_STAT_SCOPE(GraphBuilder, StatName) #endif #include "RenderGraphEvent.inl"