// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #pragma once #include "Engine/Core/NonCopyable.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Scripting/ScriptingType.h" #include "RenderStats.h" class GPUTimerQuery; #if COMPILE_WITH_PROFILER // Profiler events buffers capacity (tweaked manually) #define PROFILER_GPU_EVENTS_FRAMES 6 /// /// Provides GPU performance measuring methods. /// API_CLASS(Static) class FLAXENGINE_API ProfilerGPU { DECLARE_SCRIPTING_TYPE_NO_SPAWN(ProfilerGPU); public: /// /// Represents single CPU profiling event data. /// API_STRUCT(NoDefault) struct Event { DECLARE_SCRIPTING_TYPE_MINIMAL(Event); /// /// The name of the event. /// API_FIELD() const Char* Name; /// /// The timer query used to get the exact event time on a GPU. Assigned and managed by the internal profiler layer. /// API_FIELD() GPUTimerQuery* Timer; /// /// The rendering stats for this event. When event is active it holds the stats on event begin. /// API_FIELD() RenderStatsData Stats; /// /// The event execution time on a GPU (in milliseconds). /// API_FIELD() float Time; /// /// The event depth. Value 0 is used for the root events. /// API_FIELD() int32 Depth; }; /// /// Implements simple profiling events buffer that holds single frame data. /// class EventBuffer : public NonCopyable { private: bool _isResolved = true; Array _data; public: /// /// The index of the frame buffer was used for recording events (for the last time). /// uint64 FrameIndex; /// /// Determines whether this buffer has ready data (resolved and not empty). /// bool HasData() const; /// /// Ends all used timer queries. /// void EndAll(); /// /// Tries the resolve this frame. Skips if already resolved or has no collected events. /// void TryResolve(); /// /// Gets the event at the specified index. /// /// The index. /// The event Event* Get(const int32 index) { return &_data[index]; } /// /// Adds new event to the buffer. /// /// The initial event data. /// The event index. int32 Add(const Event& e); /// /// Extracts the buffer data. /// /// The output data. void Extract(Array& data) const; /// /// Clears this buffer. /// void Clear(); }; private: static int32 _depth; static Array _timerQueriesPool; static Array _timerQueriesFree; static GPUTimerQuery* GetTimerQuery(); public: /// /// True if GPU profiling is enabled, otherwise false to disable events collecting and GPU timer queries usage. Can be changed during rendering. /// static bool Enabled; /// /// The current frame buffer to collect events. /// static int32 CurrentBuffer; /// /// The events buffers (one per frame). /// static EventBuffer Buffers[PROFILER_GPU_EVENTS_FRAMES]; public: /// /// Begins the event. Call EndEvent with index parameter equal to the returned value by BeginEvent function. /// /// The event name. /// The event token index static int32 BeginEvent(const Char* name); /// /// Ends the active event. /// /// The event token index returned by the BeginEvent method. static void EndEvent(int32 index); /// /// Begins the new frame rendering. Called by the engine to sync profiling data. /// static void BeginFrame(); /// /// Called when just before flushing current frame GPU commands (via Present or Flush). Call active timer queries should be ended now. /// static void OnPresent(); /// /// Ends the frame rendering. Called by the engine to sync profiling data. /// static void EndFrame(); /// /// Tries to get the rendering stats from the last frame drawing (that has been resolved and has valid data). /// /// The draw execution time on a GPU (in milliseconds). /// The rendering stats data. /// True if got the data, otherwise false. static bool GetLastFrameData(float& drawTimeMs, RenderStatsData& statsData); /// /// Releases resources. Calls to the profiling API after Dispose are not valid /// static void Dispose(); }; /// /// Helper structure used to call BeginEvent/EndEvent within single code block. /// struct ScopeProfileBlockGPU { int32 Index; FORCE_INLINE ScopeProfileBlockGPU(const Char* name) { Index = ProfilerGPU::BeginEvent(name); } FORCE_INLINE ~ScopeProfileBlockGPU() { ProfilerGPU::EndEvent(Index); } }; template<> struct TIsPODType { enum { Value = true }; }; // Shortcut macro for profiling rendering on GPU #define PROFILE_GPU(name) ScopeProfileBlockGPU ProfileBlockGPU(TEXT(name)) #else // Empty macros for disabled profiler #define PROFILE_GPU(name) #endif