// Copyright (c) 2012-2020 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() 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; Array _data; public: EventBuffer() : _data(256) { _isResolved = true; } ~EventBuffer() { } 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). /// /// true if this buffer has data; otherwise, false. FORCE_INLINE bool HasData() const { return _isResolved && _data.HasItems(); } /// /// 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 { // Don't use unresolved data ASSERT(_isResolved); data = _data; } /// /// Clears this buffer. /// void Clear() { _data.Clear(); _isResolved = false; FrameIndex = 0; } }; private: static int32 _depth; static Array _timerQueriesPool; static Array _timerQueriesFree; static GPUTimerQuery* GetTimerQuery(); FORCE_INLINE static void FreeTimerQuery(GPUTimerQuery* q) { _timerQueriesFree.Add(q); } 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 { /// /// The event token index. /// int32 Index; /// /// Initializes a new instance of the struct. /// /// The event name. ScopeProfileBlockGPU(const Char* name) { Index = ProfilerGPU::BeginEvent(name); } /// /// Finalizes an instance of the class. /// ~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