diff --git a/client/TracyProfiler.cpp b/client/TracyProfiler.cpp index 0aebe8e3..71032484 100644 --- a/client/TracyProfiler.cpp +++ b/client/TracyProfiler.cpp @@ -325,24 +325,44 @@ static void InitFailure( const char* msg ) exit( 1 ); } +static bool CheckHardwareSupportsInvariantTSC() +{ + const char* noCheck = GetEnvVar( "TRACY_NO_INVARIANT_CHECK" ); + if( noCheck && noCheck[0] == '1' ) return true; + + uint32_t regs[4]; + CpuId( regs, 1 ); + if( !( regs[3] & ( 1 << 4 ) ) ) + { +#if !defined TRACY_TIMER_QPC && !defined TRACY_TIMER_FALLBACK + InitFailure( "CPU doesn't support RDTSC instruction." ); +#endif + return false; + } + CpuId( regs, 0x80000007 ); + if( regs[3] & ( 1 << 8 ) ) return true; + + return false; +} + +#if defined TRACY_TIMER_FALLBACK && defined TRACY_HW_TIMER +bool HardwareSupportsInvariantTSC() +{ + static bool cachedResult = CheckHardwareSupportsInvariantTSC(); + return cachedResult; +} +#endif + static int64_t SetupHwTimer() { #if !defined TRACY_TIMER_QPC && !defined TRACY_TIMER_FALLBACK - uint32_t regs[4]; - CpuId( regs, 1 ); - if( !( regs[3] & ( 1 << 4 ) ) ) InitFailure( "CPU doesn't support RDTSC instruction." ); - CpuId( regs, 0x80000007 ); - if( !( regs[3] & ( 1 << 8 ) ) ) + if( !CheckHardwareSupportsInvariantTSC() ) { - const char* noCheck = GetEnvVar( "TRACY_NO_INVARIANT_CHECK" ); - if( !noCheck || noCheck[0] != '1' ) - { #if defined _WIN32 - InitFailure( "CPU doesn't support invariant TSC.\nDefine TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\nAlternatively you may rebuild the application with the TRACY_TIMER_QPC or TRACY_TIMER_FALLBACK define to use lower resolution timer." ); + InitFailure( "CPU doesn't support invariant TSC.\nDefine TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\nAlternatively you may rebuild the application with the TRACY_TIMER_QPC or TRACY_TIMER_FALLBACK define to use lower resolution timer." ); #else - InitFailure( "CPU doesn't support invariant TSC.\nDefine TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\nAlternatively you may rebuild the application with the TRACY_TIMER_FALLBACK define to use lower resolution timer." ); + InitFailure( "CPU doesn't support invariant TSC.\nDefine TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\nAlternatively you may rebuild the application with the TRACY_TIMER_FALLBACK define to use lower resolution timer." ); #endif - } } #endif @@ -3465,23 +3485,32 @@ void Profiler::HandleDisconnect() void Profiler::CalibrateTimer() { -#ifdef TRACY_HW_TIMER - std::atomic_signal_fence( std::memory_order_acq_rel ); - const auto t0 = std::chrono::high_resolution_clock::now(); - const auto r0 = GetTime(); - std::atomic_signal_fence( std::memory_order_acq_rel ); - std::this_thread::sleep_for( std::chrono::milliseconds( 200 ) ); - std::atomic_signal_fence( std::memory_order_acq_rel ); - const auto t1 = std::chrono::high_resolution_clock::now(); - const auto r1 = GetTime(); - std::atomic_signal_fence( std::memory_order_acq_rel ); - - const auto dt = std::chrono::duration_cast( t1 - t0 ).count(); - const auto dr = r1 - r0; - - m_timerMul = double( dt ) / double( dr ); -#else m_timerMul = 1.; + +#ifdef TRACY_HW_TIMER + +# if !defined TRACY_TIMER_QPC && defined TRACY_TIMER_FALLBACK + const bool needCalibration = HardwareSupportsInvariantTSC(); +# else + const bool needCalibration = true; +# endif + if( needCalibration ) + { + std::atomic_signal_fence( std::memory_order_acq_rel ); + const auto t0 = std::chrono::high_resolution_clock::now(); + const auto r0 = GetTime(); + std::atomic_signal_fence( std::memory_order_acq_rel ); + std::this_thread::sleep_for( std::chrono::milliseconds( 200 ) ); + std::atomic_signal_fence( std::memory_order_acq_rel ); + const auto t1 = std::chrono::high_resolution_clock::now(); + const auto r1 = GetTime(); + std::atomic_signal_fence( std::memory_order_acq_rel ); + + const auto dt = std::chrono::duration_cast( t1 - t0 ).count(); + const auto dr = r1 - r0; + + m_timerMul = double( dt ) / double( dr ); + } #endif } diff --git a/client/TracyProfiler.hpp b/client/TracyProfiler.hpp index 41121a69..fab7770b 100644 --- a/client/TracyProfiler.hpp +++ b/client/TracyProfiler.hpp @@ -26,11 +26,11 @@ # include #endif -#if !defined TRACY_TIMER_FALLBACK && ( defined _WIN32 || ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) || ( defined TARGET_OS_IOS && TARGET_OS_IOS == 1 ) ) +#if ( defined _WIN32 || ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) || ( defined TARGET_OS_IOS && TARGET_OS_IOS == 1 ) ) # define TRACY_HW_TIMER #endif -#if !defined TRACY_HW_TIMER +#if defined TRACY_TIMER_FALLBACK || !defined TRACY_HW_TIMER # include #endif @@ -68,6 +68,23 @@ TRACY_API bool ProfilerAvailable(); TRACY_API bool ProfilerAllocatorAvailable(); TRACY_API int64_t GetFrequencyQpc(); +#if defined TRACY_TIMER_FALLBACK && defined TRACY_HW_TIMER && ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) +TRACY_API bool HardwareSupportsInvariantTSC(); // check, if we need fallback scenario +#else +# if defined TRACY_HW_TIMER +tracy_force_inline bool HardwareSupportsInvariantTSC() +{ + return true; // this is checked at startup +} +# else +tracy_force_inline bool HardwareSupportsInvariantTSC() +{ + return false; +} +# endif +#endif + + struct SourceLocationData { const char* name; @@ -167,25 +184,33 @@ public: { #ifdef TRACY_HW_TIMER # if defined TARGET_OS_IOS && TARGET_OS_IOS == 1 - return mach_absolute_time(); + if( HardwareSupportsInvariantTSC() ) return mach_absolute_time(); # elif defined _WIN32 # ifdef TRACY_TIMER_QPC return GetTimeQpc(); # else - return int64_t( __rdtsc() ); + if( HardwareSupportsInvariantTSC() ) return int64_t( __rdtsc() ); # endif # elif defined __i386 || defined _M_IX86 - uint32_t eax, edx; - asm volatile ( "rdtsc" : "=a" (eax), "=d" (edx) ); - return ( uint64_t( edx ) << 32 ) + uint64_t( eax ); + if( HardwareSupportsInvariantTSC() ) + { + uint32_t eax, edx; + asm volatile ( "rdtsc" : "=a" (eax), "=d" (edx) ); + return ( uint64_t( edx ) << 32 ) + uint64_t( eax ); + } # elif defined __x86_64__ || defined _M_X64 - uint64_t rax, rdx; - asm volatile ( "rdtsc" : "=a" (rax), "=d" (rdx) ); - return (int64_t)(( rdx << 32 ) + rax); + if( HardwareSupportsInvariantTSC() ) + { + uint64_t rax, rdx; + asm volatile ( "rdtsc" : "=a" (rax), "=d" (rdx) ); + return (int64_t)(( rdx << 32 ) + rax); + } # else # error "TRACY_HW_TIMER detection logic needs fixing" # endif -#else +#endif + +#if !defined TRACY_HW_TIMER || defined TRACY_TIMER_FALLBACK # if defined __linux__ && defined CLOCK_MONOTONIC_RAW struct timespec ts; clock_gettime( CLOCK_MONOTONIC_RAW, &ts ); @@ -194,6 +219,8 @@ public: return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); # endif #endif + + return 0; // unreacheble branch } tracy_force_inline uint32_t GetNextZoneId()