@ -5,310 +5,44 @@
* SPDX - License - Identifier : Apache - 2.0 OR MIT
*
*/
# ifndef AZCORE_PROFILER_H
# define AZCORE_PROFILER_H 1
# pragma once
# include <AzCore/std/chrono/chrono.h>
# include <AzCore/std/function/function_fwd.h>
namespace AZ
{
namespace Debug
{
using ProfileCategoryPrimitiveType = AZ : : u64 ;
/**
* Profiling categories consumed by AZ_PROFILE_FUNCTION and AZ_PROFILE_SCOPE variants for profile filtering
*/
enum class ProfileCategory : ProfileCategoryPrimitiveType
{
// These initial categories match up with the legacy EProfiledSubsystem categories
Any = 0 ,
Renderer ,
ThreeDEngine ,
Particle ,
AI ,
Animation ,
Movie ,
Entity ,
Font ,
Network ,
Physics ,
Script ,
ScriptCFunc ,
Audio ,
Editor ,
System ,
Action ,
Game ,
Input ,
Sync ,
// Legacy network traffic categories
LegacyNetworkTrafficReserved ,
LegacyDeviceReserved ,
// must match EProfiledSubsystem::PROFILE_LAST_SUBSYSTEM
LegacyLast ,
// Bulk category via AZ_TRACE_METHOD
AzTrace ,
AzCore ,
AzRender ,
AzFramework ,
AzToolsFramework ,
ScriptCanvas ,
LegacyTerrain ,
Terrain ,
Cloth ,
// Add new major categories here (and add names to the parallel position in ProfileCategoryNames) - these categories are enabled by default
FirstDetailedCategory ,
RendererDetailed = FirstDetailedCategory ,
ThreeDEngineDetailed ,
JobManagerDetailed ,
AzRenderDetailed ,
ClothDetailed ,
// Add new detailed categories here (and add names to the parallel position in ProfileCategoryNames) -- these categories are disabled by default
// Internal reserved categories, not for use with performance events
FirstReservedCategory ,
MemoryReserved = FirstReservedCategory ,
Global ,
// Must be last
Count
} ;
static_assert ( static_cast < size_t > ( ProfileCategory : : Count ) < ( sizeof ( ProfileCategoryPrimitiveType ) * 8 ) , " The number of profile categories must not exceed the number of bits in ProfileCategoryPrimitiveType " ) ;
/**
* Parallel array to ProfileCategory as string category names to be used as Driller category names or for debug purposes
*/
static const char * ProfileCategoryNames [ ] =
{
" Any " ,
" Renderer " ,
" 3DEngine " ,
" Particle " ,
" AI " ,
" Animation " ,
" Movie " ,
" Entity " ,
" Font " ,
" Network " ,
" Physics " ,
" Script " ,
" ScriptCFunc " ,
" Audio " ,
" Editor " ,
" System " ,
" Action " ,
" Game " ,
" Input " ,
" Sync " ,
" LegacyNetworkTrafficReserved " ,
" LegacyDeviceReserved " ,
" LegacyLast " ,
" AzTrace " ,
" AzCore " ,
" AzRender " ,
" AzFramework " ,
" AzToolsFramework " ,
" ScriptCanvas " ,
" LegacyTerrain " ,
" Terrain " ,
" Cloth " ,
" RendererDetailed " ,
" 3DEngineDetailed " ,
" JobManagerDetailed " ,
" AzRenderDetailed " ,
" ClothDetailed " ,
" MemoryReserved " ,
" Global "
} ;
static_assert ( AZ_ARRAY_SIZE ( ProfileCategoryNames ) = = static_cast < size_t > ( ProfileCategory : : Count ) , " ProfileCategory and ProfileCategoryNames size mismatch " ) ;
}
}
// Must be included below ProfileCategory
# ifdef AZ_PROFILE_TELEMETRY
# include <RADTelemetry / ProfileTelemetry.h>
# ifdef USE_PIX
# include <AzCore/PlatformIncl.h>
# include <WinPixEventRuntime/pix3.h>
# endif
# if defined(AZ_PROFILER_MACRO_DISABLE) // by default we never disable the profiler registers as their overhead should be minimal, you can still do that for your code though.
# define AZ_PROFILE_TIMER(...)
# define AZ_PROFILE_TIMER_END(_SectionVariableName)
# define AZ_PROFILE_VALUE_SET(...)
# define AZ_PROFILE_VALUE_ADD(...)
# define AZ_PROFILE_VALUE_SET_NAMED(...)
# define AZ_PROFILE_VALUE_ADD_NAMED(...)
# define AZ_PROFILE_SCOPE(...)
# define AZ_PROFILE_FUNCTION(...)
# define AZ_PROFILE_BEGIN(...)
# define AZ_PROFILE_END(...)
# else
/// Implementation when we have only 1 param system name
# define AZ_PROFILE_TIMER_1(_1) AZ_PROFILE_TIMER_2(_1, nullptr)
/// Implementation when we have 2 params (_1 system name and _2 is name of the "section"/register/profiled section - used for debug)
# define AZ_PROFILE_TIMER_2(_1, _2) AZ_PROFILE_TIMER_3(_1, _2, AZ_JOIN(azProfileSection, __LINE__))
/// Implementation when we have all 3 params (system name, section/register name, section variable name)
# define AZ_PROFILE_TIMER_3(_1, _2, _3) \
AZ : : Debug : : ProfilerSection _3 ; \
if ( AZ : : u64 profilerId = AZ : : Debug : : Profiler : : GetId ( ) ) { \
static AZ_THREAD_LOCAL AZ : : Internal : : RegisterData AZ_JOIN ( azProfileRegister , __LINE__ ) = { 0 , 0 } ; \
if ( AZ_JOIN ( azProfileRegister , __LINE__ ) . m_profilerId ! = profilerId ) { \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register = AZ : : Debug : : ProfilerRegister : : TimerCreateAndStart ( _1 , _2 , & _3 , AZ_FUNCTION_SIGNATURE , __LINE__ ) ; \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_profilerId = profilerId ; \
} else { \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register - > TimerStart ( & _3 ) ; \
} \
}
/**
* Macro to declare a profile section for the current scope { } .
* format is : AZ_PROFILE_TIMER ( const char * systemName , const char * sectionDescription = nullptr , optional sectionName )
* \ param _1 is required and it ' s ' const char * ' of the system name of which system this scope / register belongs to .
* \ param _2 is optional and it ' s ' const char * ' with a name for the " section " / register / profiled section - used as description . If not provided a " Anonymous " will be set .
* \ param _3 is optional unique name for a section C + + variable ( so you can stop the SCOPE as you wish ) . If not provided a default unique name is created .
* format is : AZ_PROFILE_SCOPE ( categoryName , const char * formatStr , . . . )
*/
# define AZ_PROFILE_TIMER(...) AZ_MACRO_SPECIALIZE(AZ_PROFILE_TIMER_, AZ_VA_NUM_ARGS(__VA_ARGS__), (__VA_ARGS__))
// Optional (USE ONLY IN EXTREME CASES!!!) scope end command for named sections, so you stop the profiler register timing before it goes out of scope.
# define AZ_PROFILE_TIMER_END(_SectionVariableName) { _SectionVariableName.Stop(); }
/**
* Macro to operate on custom values . All values are AZ : : s64 . You can provide up to 5 values .
* format is AZ_PROFILE_VALUE_SET / ADD ( const char * systemName , const char * valueName ,
* value1 , optional value2 , optional value3 , optional value 4 , optional value5 , optional registerName ( for direct register manipulation for EXPERTS ONLY ) ) .
* \ param _SystemName is required and it ' s ' const char * ' of the system name of which system this scope / register belongs to .
* \ param _RegisterName is required and it ' s ' const char * ' with a name for the register - used as description .
* \ param 3 is required and it ' s AZ : : s64 , operates on m_value1 .
* \ param 4 is optional and it ' s AZ : : s64 , operates on m_value2 .
* \ param 5 is optional and it ' s AZ : : s64 , operates on m_value3 .
* \ param 6 is optional and it ' s AZ : : s64 , operates on m_value4 .
* \ param 7 is optional and it ' s AZ : : s64 , operates on m_value5 .
*/
# define AZ_PROFILE_VALUE_SET(_SystemName, _RegisterName, ...) \
if ( AZ : : u64 profilerId = AZ : : Debug : : Profiler : : GetId ( ) ) { \
static AZ_THREAD_LOCAL AZ : : Internal : : RegisterData AZ_JOIN ( azProfileRegister , __LINE__ ) = { 0 , 0 } ; \
if ( AZ_JOIN ( azProfileRegister , __LINE__ ) . m_profilerId ! = profilerId ) { \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register = AZ : : Debug : : ProfilerRegister : : ValueCreate ( _SystemName , _RegisterName , AZ_FUNCTION_SIGNATURE , __LINE__ ) ; \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_profilerId = profilerId ; \
} \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register - > ValueSet ( __VA_ARGS__ ) ; \
}
/// Same as AZ_PROFILE_VALUE_SET except is add the values passed in the macro (you can use -(value), to subtract values)
# define AZ_PROFILE_VALUE_ADD(_SystemName, _RegisterName, ...) \
if ( AZ : : u64 profilerId = AZ : : Debug : : Profiler : : GetId ( ) ) { \
static AZ_THREAD_LOCAL AZ : : Internal : : RegisterData AZ_JOIN ( azProfileRegister , __LINE__ ) = { 0 , 0 } ; \
if ( AZ_JOIN ( azProfileRegister , __LINE__ ) . m_profilerId ! = profilerId ) { \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register = AZ : : Debug : : ProfilerRegister : : ValueCreate ( _SystemName , _RegisterName , AZ_FUNCTION_SIGNATURE , __LINE__ ) ; \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_profilerId = profilerId ; \
} \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register - > ValueAdd ( __VA_ARGS__ ) ; \
}
/**
* Same as AZ_PROFILER_VALUE_SET but with option to access the register by name . ( USE ONLY IN EXTREME CASES ! ! ! )
* \ param _RegisterVaribaleName is optional unique name for a register C + + variable so you can manipulate the register .
*/
# define AZ_PROFILE_VALUE_SET_NAMED(_SystemName, _RegisterName, _RegisterVaribaleName, ...) \
AZ : : Debug : : ProfilerRegister * _RegisterVaribaleName = nullptr ; \
if ( AZ : : u64 profilerId = AZ : : Debug : : Profiler : : GetId ( ) ) { \
static AZ_THREAD_LOCAL AZ : : Internal : : RegisterData AZ_JOIN ( azProfileRegister , __LINE__ ) = { 0 , 0 } ; \
if ( AZ_JOIN ( azProfileRegister , __LINE__ ) . m_profilerId ! = profilerId ) { \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register = AZ : : Debug : : ProfilerRegister : : ValueCreate ( _SystemName , _RegisterName , AZ_FUNCTION_SIGNATURE , __LINE__ ) ; \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_profilerId = profilerId ; \
} \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register - > ValueSet ( __VA_ARGS__ ) ; \
_RegisterVaribaleName = AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register ; \
}
/// Same as AZ_PROFILE_VALUE_SET_NAMED but add the values to the current. (USE ONLY IN EXTREME CASES!!!)
# define AZ_PROFILE_VALUE_ADD_NAMED(_SystemName, _RegisterName, _RegisterVaribaleName, ...) \
AZ : : Debug : : ProfilerRegister * _RegisterVaribaleName = nullptr ; \
if ( AZ : : u64 profilerId = AZ : : Debug : : Profiler : : GetId ( ) ) { \
static AZ_THREAD_LOCAL AZ : : Internal : : RegisterData AZ_JOIN ( azProfileRegister , __LINE__ ) = { 0 , 0 } ; \
if ( AZ_JOIN ( azProfileRegister , __LINE__ ) . m_profilerId ! = profilerId ) { \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register = AZ : : Debug : : ProfilerRegister : : ValueCreate ( _SystemName , _RegisterName , AZ_FUNCTION_SIGNATURE , __LINE__ ) ; \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_profilerId = profilerId ; \
} \
AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register - > ValueAdd ( __VA_ARGS__ ) ; \
_RegisterVaribaleName = AZ_JOIN ( azProfileRegister , __LINE__ ) . m_register ; \
}
# define AZ_PROFILE_SCOPE(category, ...) ::AZ::ProfileScope AZ_JOIN(azProfileScope, __LINE__){ #category, __VA_ARGS__ }
# define AZ_PROFILE_FUNCTION(category) AZ_PROFILE_SCOPE(category, AZ_FUNCTION_SIGNATURE)
// Prefer using the scoped macros which automatically end the event (AZ_PROFILE_SCOPE/AZ_PROFILE_FUNCTION)
# define AZ_PROFILE_BEGIN(category, ...) ::AZ::ProfileScope::BeginRegion(#category, __VA_ARGS__)
# define AZ_PROFILE_END() ::AZ::ProfileScope::EndRegion()
# endif // AZ_PROFILER_MACRO_DISABLE
# ifndef AZ_PROFILE_FUNCTION
// No other profiler has defined the performance markers AZ_PROFILE_SCOPE (and friends), fallback to a Driller implementation
# define AZ_INTERNAL_PROF_VERIFY_CAT(category) static_assert(category < AZ::Debug::ProfileCategory::Count, "Invalid profile category")
# define AZ_INTERNAL_PROF_CAT_NAME(category) AZ::Debug::ProfileCategoryNames[static_cast<AZ::u32>(category)]
# define AZ_PROFILE_FUNCTION(category) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_PROFILE_TIMER ( AZ_INTERNAL_PROF_CAT_NAME ( category ) )
# define AZ_PROFILE_FUNCTION_STALL(category) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_PROFILE_TIMER ( AZ_INTERNAL_PROF_CAT_NAME ( category ) )
# define AZ_PROFILE_FUNCTION_IDLE(category) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_PROFILE_TIMER ( AZ_INTERNAL_PROF_CAT_NAME ( category ) )
# define AZ_PROFILE_SCOPE(category, name) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_PROFILE_TIMER ( AZ_INTERNAL_PROF_CAT_NAME ( category ) ) ; ( void ) ( name )
# define AZ_PROFILE_SCOPE_STALL(category, name) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_PROFILE_TIMER ( AZ_INTERNAL_PROF_CAT_NAME ( category ) ) ; ( void ) ( name )
# define AZ_PROFILE_SCOPE_IDLE(category, name) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_PROFILE_TIMER ( AZ_INTERNAL_PROF_CAT_NAME ( category ) ) ; ( void ) ( name )
# define AZ_PROFILE_SCOPE_DYNAMIC(category, ...) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_PROFILE_TIMER ( AZ_INTERNAL_PROF_CAT_NAME ( category ) )
# define AZ_PROFILE_SCOPE_STALL_DYNAMIC(category, ...) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_PROFILE_TIMER ( AZ_INTERNAL_PROF_CAT_NAME ( category ) )
# define AZ_PROFILE_SCOPE_IDLE_DYNAMIC(category, ...) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_PROFILE_TIMER ( AZ_INTERNAL_PROF_CAT_NAME ( category ) )
# endif
# ifndef AZ_PROFILE_EVENT_BEGIN
// No other profiler has defined the performance markers AZ_PROFILE_EVENT_START/END, fallback to a Driller implementation (currently empty)
# define AZ_PROFILE_EVENT_BEGIN(category, name) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; ( void ) ( name )
# define AZ_PROFILE_EVENT_END(category) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category )
# endif
# ifndef AZ_PROFILE_INTERVAL_START
// No other profiler has defined the performance markers AZ_PROFILE_INTERVAL_START/END, fallback to a Driller implementation (currently empty)
# define AZ_INTERNAL_PROF_VERIFY_INTERVAL_ID(id) static_assert(sizeof(id) <= sizeof(AZ::u64), "Interval id must be a unique value no larger than 64-bits")
# define AZ_PROFILE_INTERVAL_START(category, id, ...) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_INTERNAL_PROF_VERIFY_INTERVAL_ID ( id )
# define AZ_PROFILE_INTERVAL_START_COLORED(category, id, color, ...) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; ( void ) ( color ) ; AZ_INTERNAL_PROF_VERIFY_INTERVAL_ID ( id )
# define AZ_PROFILE_INTERVAL_END(category, id) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_INTERNAL_PROF_VERIFY_INTERVAL_ID ( id )
# define AZ_PROFILE_INTERVAL_SCOPED(category, id, ...) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; AZ_INTERNAL_PROF_VERIFY_INTERVAL_ID ( id )
# define AZ_PROFILE_INTERVAL_START(...)
# define AZ_PROFILE_INTERVAL_START_COLORED(...)
# define AZ_PROFILE_INTERVAL_END(...)
# define AZ_PROFILE_INTERVAL_SCOPED(...)
# endif
# ifndef AZ_PROFILE_DATAPOINT
// No other profiler has defined the performance markers AZ_PROFILE_DATAPOINT, fallback to a Driller implementation (currently empty)
# define AZ_PROFILE_DATAPOINT(category, value, ...) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; static_cast < double > ( value )
# define AZ_PROFILE_DATAPOINT_PERCENT(category, value, ...) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; static_cast < double > ( value )
# endif
# ifndef AZ_PROFILE_MEMORY_ALLOC
// No other profiler has defined the performance markers AZ_PROFILE_MEMORY_ALLOC (and friends), fall back to a Driller implementation (currently empty)
# define AZ_PROFILE_MEMORY_ALLOC(category, address, size, context) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; ( void ) ( context )
# define AZ_PROFILE_MEMORY_ALLOC_EX(category, filename, lineNumber, address, size, context) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category ) ; ( void ) ( context )
# define AZ_PROFILE_MEMORY_FREE(category, address) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category )
# define AZ_PROFILE_MEMORY_FREE_EX(category, filename, lineNumber, address) \
AZ_INTERNAL_PROF_VERIFY_CAT ( category )
# define AZ_PROFILE_DATAPOINT(...)
# define AZ_PROFILE_DATAPOINT_PERCENT(...)
# endif
namespace AZStd
@ -318,6 +52,42 @@ namespace AZStd
namespace AZ
{
class ProfileScope
{
public :
static uint32_t GetSystemID ( const char * system ) ;
template < typename . . . T >
static void BeginRegion ( [ [ maybe_unused ] ] const char * system , [ [ maybe_unused ] ] const char * eventName , [ [ maybe_unused ] ] T const & . . . args )
{
// TODO: Verification that the supplied system name corresponds to a known budget
# if defined(USE_PIX)
PIXBeginEvent ( PIX_COLOR_INDEX ( GetSystemID ( system ) & 0xff ) , eventName , args . . . ) ;
# endif
// TODO: injecting instrumentation for other profilers
// NOTE: external profiler registration won't occur inline in a header necessarily in this manner, but the exact mechanism
// will be introduced in a future PR
}
static void EndRegion ( )
{
# if defined(USE_PIX)
PIXEndEvent ( ) ;
# endif
}
template < typename . . . T >
ProfileScope ( const char * system , char const * eventName , T const & . . . args )
{
BeginRegion ( system , eventName , args . . . ) ;
}
~ ProfileScope ( )
{
EndRegion ( ) ;
}
} ;
namespace Debug
{
class ProfilerSection ;
@ -615,5 +385,9 @@ namespace AZ
}
} // namespace AZ
# endif // AZCORE_PROFILER_H
# pragma once
# ifdef USE_PIX
// The pix3 header unfortunately brings in other Windows macros we need to undef
# undef DeleteFile
# undef LoadImage
# undef GetCurrentTime
# endif