2017-08-01 12:30:58 +00:00
|
|
|
/*
|
|
|
|
|
|
|
|
***************************************************************************************************
|
|
|
|
**
|
|
|
|
** profile.cpp
|
|
|
|
**
|
|
|
|
** Real-Time Hierarchical Profiling for Game Programming Gems 3
|
|
|
|
**
|
|
|
|
** by Greg Hjelstrom & Byon Garrabrant
|
|
|
|
**
|
|
|
|
***************************************************************************************************/
|
|
|
|
|
|
|
|
// Credits: The Clock class was inspired by the Timer classes in
|
|
|
|
// Ogre (www.ogre3d.org).
|
|
|
|
|
|
|
|
#include "btQuickprof.h"
|
|
|
|
#include "btThreads.h"
|
|
|
|
|
|
|
|
#ifdef __CELLOS_LV2__
|
|
|
|
#include <sys/sys_time.h>
|
|
|
|
#include <sys/time_util.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#endif
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
#if defined(SUNOS) || defined(__SUNOS__)
|
2017-08-01 12:30:58 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <mach/mach_time.h>
|
|
|
|
#include <TargetConditionals.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
|
|
|
|
|
|
#define BT_USE_WINDOWS_TIMERS
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#define NOWINRES
|
|
|
|
#define NOMCX
|
|
|
|
#define NOIME
|
|
|
|
|
|
|
|
#ifdef _XBOX
|
2019-01-03 13:26:51 +00:00
|
|
|
#include <Xtl.h>
|
|
|
|
#else //_XBOX
|
|
|
|
#include <windows.h>
|
2017-08-01 12:30:58 +00:00
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
#if WINVER < 0x0602
|
2017-08-01 12:30:58 +00:00
|
|
|
#define GetTickCount64 GetTickCount
|
|
|
|
#endif
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
#endif //_XBOX
|
2017-08-01 12:30:58 +00:00
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
#else //_WIN32
|
2017-08-01 12:30:58 +00:00
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
#ifdef BT_LINUX_REALTIME
|
|
|
|
//required linking against rt (librt)
|
|
|
|
#include <time.h>
|
2019-01-03 13:26:51 +00:00
|
|
|
#endif //BT_LINUX_REALTIME
|
2017-08-01 12:30:58 +00:00
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
#endif //_WIN32
|
2017-08-01 12:30:58 +00:00
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
#define mymin(a, b) (a > b ? a : b)
|
2017-08-01 12:30:58 +00:00
|
|
|
|
|
|
|
struct btClockData
|
|
|
|
{
|
|
|
|
#ifdef BT_USE_WINDOWS_TIMERS
|
|
|
|
LARGE_INTEGER mClockFrequency;
|
|
|
|
LONGLONG mStartTick;
|
|
|
|
LARGE_INTEGER mStartTime;
|
|
|
|
#else
|
|
|
|
#ifdef __CELLOS_LV2__
|
2019-01-03 13:26:51 +00:00
|
|
|
uint64_t mStartTime;
|
2017-08-01 12:30:58 +00:00
|
|
|
#else
|
|
|
|
#ifdef __APPLE__
|
2019-01-03 13:26:51 +00:00
|
|
|
uint64_t mStartTimeNano;
|
2017-08-01 12:30:58 +00:00
|
|
|
#endif
|
|
|
|
struct timeval mStartTime;
|
|
|
|
#endif
|
2019-01-03 13:26:51 +00:00
|
|
|
#endif //__CELLOS_LV2__
|
2017-08-01 12:30:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
///The btClock is a portable basic clock that measures accurate time in seconds, use for profiling.
|
|
|
|
btClock::btClock()
|
|
|
|
{
|
|
|
|
m_data = new btClockData;
|
|
|
|
#ifdef BT_USE_WINDOWS_TIMERS
|
|
|
|
QueryPerformanceFrequency(&m_data->mClockFrequency);
|
|
|
|
#endif
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
btClock::~btClock()
|
|
|
|
{
|
|
|
|
delete m_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
btClock::btClock(const btClock& other)
|
|
|
|
{
|
|
|
|
m_data = new btClockData;
|
|
|
|
*m_data = *other.m_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
btClock& btClock::operator=(const btClock& other)
|
|
|
|
{
|
|
|
|
*m_data = *other.m_data;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
/// Resets the initial reference time.
|
2017-08-01 12:30:58 +00:00
|
|
|
void btClock::reset()
|
|
|
|
{
|
|
|
|
#ifdef BT_USE_WINDOWS_TIMERS
|
|
|
|
QueryPerformanceCounter(&m_data->mStartTime);
|
|
|
|
m_data->mStartTick = GetTickCount64();
|
|
|
|
#else
|
|
|
|
#ifdef __CELLOS_LV2__
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
typedef uint64_t ClockSize;
|
2017-08-01 12:30:58 +00:00
|
|
|
ClockSize newTime;
|
|
|
|
//__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");
|
2019-01-03 13:26:51 +00:00
|
|
|
SYS_TIMEBASE_GET(newTime);
|
2017-08-01 12:30:58 +00:00
|
|
|
m_data->mStartTime = newTime;
|
|
|
|
#else
|
|
|
|
#ifdef __APPLE__
|
2019-01-03 13:26:51 +00:00
|
|
|
m_data->mStartTimeNano = mach_absolute_time();
|
2017-08-01 12:30:58 +00:00
|
|
|
#endif
|
|
|
|
gettimeofday(&m_data->mStartTime, 0);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the time in ms since the last call to reset or since
|
|
|
|
/// the btClock was created.
|
|
|
|
unsigned long long int btClock::getTimeMilliseconds()
|
|
|
|
{
|
|
|
|
#ifdef BT_USE_WINDOWS_TIMERS
|
|
|
|
LARGE_INTEGER currentTime;
|
|
|
|
QueryPerformanceCounter(¤tTime);
|
|
|
|
LONGLONG elapsedTime = currentTime.QuadPart -
|
2019-01-03 13:26:51 +00:00
|
|
|
m_data->mStartTime.QuadPart;
|
|
|
|
// Compute the number of millisecond ticks elapsed.
|
2017-08-01 12:30:58 +00:00
|
|
|
unsigned long msecTicks = (unsigned long)(1000 * elapsedTime /
|
2019-01-03 13:26:51 +00:00
|
|
|
m_data->mClockFrequency.QuadPart);
|
2017-08-01 12:30:58 +00:00
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
return msecTicks;
|
2017-08-01 12:30:58 +00:00
|
|
|
#else
|
|
|
|
|
|
|
|
#ifdef __CELLOS_LV2__
|
2019-01-03 13:26:51 +00:00
|
|
|
uint64_t freq = sys_time_get_timebase_frequency();
|
|
|
|
double dFreq = ((double)freq) / 1000.0;
|
|
|
|
typedef uint64_t ClockSize;
|
|
|
|
ClockSize newTime;
|
|
|
|
SYS_TIMEBASE_GET(newTime);
|
|
|
|
//__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");
|
|
|
|
|
|
|
|
return (unsigned long int)((double(newTime - m_data->mStartTime)) / dFreq);
|
2017-08-01 12:30:58 +00:00
|
|
|
#else
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
struct timeval currentTime;
|
|
|
|
gettimeofday(¤tTime, 0);
|
|
|
|
return (currentTime.tv_sec - m_data->mStartTime.tv_sec) * 1000 +
|
|
|
|
(currentTime.tv_usec - m_data->mStartTime.tv_usec) / 1000;
|
|
|
|
#endif //__CELLOS_LV2__
|
2017-08-01 12:30:58 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
/// Returns the time in us since the last call to reset or since
|
|
|
|
/// the Clock was created.
|
2017-08-01 12:30:58 +00:00
|
|
|
unsigned long long int btClock::getTimeMicroseconds()
|
|
|
|
{
|
|
|
|
#ifdef BT_USE_WINDOWS_TIMERS
|
2019-01-03 13:26:51 +00:00
|
|
|
//see https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx
|
|
|
|
LARGE_INTEGER currentTime, elapsedTime;
|
|
|
|
|
|
|
|
QueryPerformanceCounter(¤tTime);
|
|
|
|
elapsedTime.QuadPart = currentTime.QuadPart -
|
|
|
|
m_data->mStartTime.QuadPart;
|
|
|
|
elapsedTime.QuadPart *= 1000000;
|
|
|
|
elapsedTime.QuadPart /= m_data->mClockFrequency.QuadPart;
|
|
|
|
|
|
|
|
return (unsigned long long)elapsedTime.QuadPart;
|
2017-08-01 12:30:58 +00:00
|
|
|
#else
|
|
|
|
|
|
|
|
#ifdef __CELLOS_LV2__
|
2019-01-03 13:26:51 +00:00
|
|
|
uint64_t freq = sys_time_get_timebase_frequency();
|
|
|
|
double dFreq = ((double)freq) / 1000000.0;
|
|
|
|
typedef uint64_t ClockSize;
|
|
|
|
ClockSize newTime;
|
|
|
|
//__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");
|
|
|
|
SYS_TIMEBASE_GET(newTime);
|
|
|
|
|
|
|
|
return (unsigned long int)((double(newTime - m_data->mStartTime)) / dFreq);
|
2017-08-01 12:30:58 +00:00
|
|
|
#else
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
struct timeval currentTime;
|
|
|
|
gettimeofday(¤tTime, 0);
|
|
|
|
return (currentTime.tv_sec - m_data->mStartTime.tv_sec) * 1000000 +
|
|
|
|
(currentTime.tv_usec - m_data->mStartTime.tv_usec);
|
|
|
|
#endif //__CELLOS_LV2__
|
2017-08-01 12:30:58 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long long int btClock::getTimeNanoseconds()
|
|
|
|
{
|
|
|
|
#ifdef BT_USE_WINDOWS_TIMERS
|
|
|
|
//see https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx
|
2019-01-03 13:26:51 +00:00
|
|
|
LARGE_INTEGER currentTime, elapsedTime;
|
|
|
|
|
|
|
|
QueryPerformanceCounter(¤tTime);
|
|
|
|
elapsedTime.QuadPart = currentTime.QuadPart -
|
|
|
|
m_data->mStartTime.QuadPart;
|
|
|
|
elapsedTime.QuadPart *= 1000000000;
|
|
|
|
elapsedTime.QuadPart /= m_data->mClockFrequency.QuadPart;
|
|
|
|
|
|
|
|
return (unsigned long long)elapsedTime.QuadPart;
|
2017-08-01 12:30:58 +00:00
|
|
|
#else
|
|
|
|
|
|
|
|
#ifdef __CELLOS_LV2__
|
2019-01-03 13:26:51 +00:00
|
|
|
uint64_t freq = sys_time_get_timebase_frequency();
|
|
|
|
double dFreq = ((double)freq) / 1e9;
|
|
|
|
typedef uint64_t ClockSize;
|
|
|
|
ClockSize newTime;
|
|
|
|
//__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");
|
|
|
|
SYS_TIMEBASE_GET(newTime);
|
|
|
|
|
|
|
|
return (unsigned long int)((double(newTime - m_data->mStartTime)) / dFreq);
|
2017-08-01 12:30:58 +00:00
|
|
|
#else
|
|
|
|
#ifdef __APPLE__
|
2019-01-03 13:26:51 +00:00
|
|
|
uint64_t ticks = mach_absolute_time() - m_data->mStartTimeNano;
|
|
|
|
static long double conversion = 0.0L;
|
|
|
|
if (0.0L == conversion)
|
|
|
|
{
|
|
|
|
// attempt to get conversion to nanoseconds
|
|
|
|
mach_timebase_info_data_t info;
|
|
|
|
int err = mach_timebase_info(&info);
|
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
btAssert(0);
|
|
|
|
conversion = 1.;
|
|
|
|
}
|
|
|
|
conversion = info.numer / info.denom;
|
|
|
|
}
|
|
|
|
return (ticks * conversion);
|
|
|
|
|
|
|
|
#else //__APPLE__
|
|
|
|
|
2017-08-01 12:30:58 +00:00
|
|
|
#ifdef BT_LINUX_REALTIME
|
2019-01-03 13:26:51 +00:00
|
|
|
timespec ts;
|
|
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
|
|
return 1000000000 * ts.tv_sec + ts.tv_nsec;
|
2017-08-01 12:30:58 +00:00
|
|
|
#else
|
2019-01-03 13:26:51 +00:00
|
|
|
struct timeval currentTime;
|
|
|
|
gettimeofday(¤tTime, 0);
|
|
|
|
return (currentTime.tv_sec - m_data->mStartTime.tv_sec) * 1e9 +
|
|
|
|
(currentTime.tv_usec - m_data->mStartTime.tv_usec) * 1000;
|
|
|
|
#endif //BT_LINUX_REALTIME
|
|
|
|
|
|
|
|
#endif //__APPLE__
|
|
|
|
#endif //__CELLOS_LV2__
|
|
|
|
#endif
|
2017-08-01 12:30:58 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
/// Returns the time in s since the last call to reset or since
|
2017-08-01 12:30:58 +00:00
|
|
|
/// the Clock was created.
|
|
|
|
btScalar btClock::getTimeSeconds()
|
|
|
|
{
|
|
|
|
static const btScalar microseconds_to_seconds = btScalar(0.000001);
|
|
|
|
return btScalar(getTimeMicroseconds()) * microseconds_to_seconds;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef BT_NO_PROFILE
|
|
|
|
|
|
|
|
static btClock gProfileClock;
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
inline void Profile_Get_Ticks(unsigned long int* ticks)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
*ticks = (unsigned long int)gProfileClock.getTimeMicroseconds();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline float Profile_Get_Tick_Rate(void)
|
|
|
|
{
|
2019-01-03 13:26:51 +00:00
|
|
|
// return 1000000.f;
|
2017-08-01 12:30:58 +00:00
|
|
|
return 1000.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************************************
|
|
|
|
**
|
|
|
|
** CProfileNode
|
|
|
|
**
|
|
|
|
***************************************************************************************************/
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* INPUT: *
|
|
|
|
* name - pointer to a static string which is the name of this profile node *
|
|
|
|
* parent - parent pointer *
|
|
|
|
* *
|
|
|
|
* WARNINGS: *
|
|
|
|
* The name is assumed to be a static pointer, only the pointer is stored and compared for *
|
|
|
|
* efficiency reasons. *
|
|
|
|
*=============================================================================================*/
|
2019-01-03 13:26:51 +00:00
|
|
|
CProfileNode::CProfileNode(const char* name, CProfileNode* parent) : Name(name),
|
|
|
|
TotalCalls(0),
|
|
|
|
TotalTime(0),
|
|
|
|
StartTime(0),
|
|
|
|
RecursionCounter(0),
|
|
|
|
Parent(parent),
|
|
|
|
Child(NULL),
|
|
|
|
Sibling(NULL),
|
|
|
|
m_userPtr(0)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileNode::CleanupMemory()
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
2019-01-03 13:26:51 +00:00
|
|
|
delete (Child);
|
2017-08-01 12:30:58 +00:00
|
|
|
Child = NULL;
|
2019-01-03 13:26:51 +00:00
|
|
|
delete (Sibling);
|
2017-08-01 12:30:58 +00:00
|
|
|
Sibling = NULL;
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
CProfileNode::~CProfileNode(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
CleanupMemory();
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* INPUT: *
|
|
|
|
* name - static string pointer to the name of the node we are searching for *
|
|
|
|
* *
|
|
|
|
* WARNINGS: *
|
|
|
|
* All profile names are assumed to be static strings so this function uses pointer compares *
|
|
|
|
* to find the named node. *
|
|
|
|
*=============================================================================================*/
|
2019-01-03 13:26:51 +00:00
|
|
|
CProfileNode* CProfileNode::Get_Sub_Node(const char* name)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
// Try to find this sub node
|
2019-01-03 13:26:51 +00:00
|
|
|
CProfileNode* child = Child;
|
|
|
|
while (child)
|
|
|
|
{
|
|
|
|
if (child->Name == name)
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
return child;
|
|
|
|
}
|
|
|
|
child = child->Sibling;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We didn't find it, so add it
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
CProfileNode* node = new CProfileNode(name, this);
|
2017-08-01 12:30:58 +00:00
|
|
|
node->Sibling = Child;
|
|
|
|
Child = node;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileNode::Reset(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
TotalCalls = 0;
|
|
|
|
TotalTime = 0.0f;
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
if (Child)
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
Child->Reset();
|
|
|
|
}
|
2019-01-03 13:26:51 +00:00
|
|
|
if (Sibling)
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
Sibling->Reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileNode::Call(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
TotalCalls++;
|
2019-01-03 13:26:51 +00:00
|
|
|
if (RecursionCounter++ == 0)
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
Profile_Get_Ticks(&StartTime);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
bool CProfileNode::Return(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
2019-01-03 13:26:51 +00:00
|
|
|
if (--RecursionCounter == 0 && TotalCalls != 0)
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
unsigned long int time;
|
|
|
|
Profile_Get_Ticks(&time);
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
time -= StartTime;
|
2017-08-01 12:30:58 +00:00
|
|
|
TotalTime += (float)time / Profile_Get_Tick_Rate();
|
|
|
|
}
|
2019-01-03 13:26:51 +00:00
|
|
|
return (RecursionCounter == 0);
|
2017-08-01 12:30:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************************************
|
|
|
|
**
|
|
|
|
** CProfileIterator
|
|
|
|
**
|
|
|
|
***************************************************************************************************/
|
2019-01-03 13:26:51 +00:00
|
|
|
CProfileIterator::CProfileIterator(CProfileNode* start)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
CurrentParent = start;
|
|
|
|
CurrentChild = CurrentParent->Get_Child();
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileIterator::First(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
CurrentChild = CurrentParent->Get_Child();
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileIterator::Next(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
CurrentChild = CurrentChild->Get_Sibling();
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
bool CProfileIterator::Is_Done(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
return CurrentChild == NULL;
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileIterator::Enter_Child(int index)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
CurrentChild = CurrentParent->Get_Child();
|
2019-01-03 13:26:51 +00:00
|
|
|
while ((CurrentChild != NULL) && (index != 0))
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
index--;
|
|
|
|
CurrentChild = CurrentChild->Get_Sibling();
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
if (CurrentChild != NULL)
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
CurrentParent = CurrentChild;
|
|
|
|
CurrentChild = CurrentParent->Get_Child();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileIterator::Enter_Parent(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
2019-01-03 13:26:51 +00:00
|
|
|
if (CurrentParent->Get_Parent() != NULL)
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
CurrentParent = CurrentParent->Get_Parent();
|
|
|
|
}
|
|
|
|
CurrentChild = CurrentParent->Get_Child();
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************************************
|
|
|
|
**
|
|
|
|
** CProfileManager
|
|
|
|
**
|
|
|
|
***************************************************************************************************/
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
CProfileNode gRoots[BT_QUICKPROF_MAX_THREAD_COUNT] = {
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL),
|
|
|
|
CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL), CProfileNode("Root", NULL)};
|
|
|
|
|
|
|
|
CProfileNode* gCurrentNodes[BT_QUICKPROF_MAX_THREAD_COUNT] =
|
|
|
|
{
|
|
|
|
&gRoots[0],
|
|
|
|
&gRoots[1],
|
|
|
|
&gRoots[2],
|
|
|
|
&gRoots[3],
|
|
|
|
&gRoots[4],
|
|
|
|
&gRoots[5],
|
|
|
|
&gRoots[6],
|
|
|
|
&gRoots[7],
|
|
|
|
&gRoots[8],
|
|
|
|
&gRoots[9],
|
|
|
|
&gRoots[10],
|
|
|
|
&gRoots[11],
|
|
|
|
&gRoots[12],
|
|
|
|
&gRoots[13],
|
|
|
|
&gRoots[14],
|
|
|
|
&gRoots[15],
|
|
|
|
&gRoots[16],
|
|
|
|
&gRoots[17],
|
|
|
|
&gRoots[18],
|
|
|
|
&gRoots[19],
|
|
|
|
&gRoots[20],
|
|
|
|
&gRoots[21],
|
|
|
|
&gRoots[22],
|
|
|
|
&gRoots[23],
|
|
|
|
&gRoots[24],
|
|
|
|
&gRoots[25],
|
|
|
|
&gRoots[26],
|
|
|
|
&gRoots[27],
|
|
|
|
&gRoots[28],
|
|
|
|
&gRoots[29],
|
|
|
|
&gRoots[30],
|
|
|
|
&gRoots[31],
|
|
|
|
&gRoots[32],
|
|
|
|
&gRoots[33],
|
|
|
|
&gRoots[34],
|
|
|
|
&gRoots[35],
|
|
|
|
&gRoots[36],
|
|
|
|
&gRoots[37],
|
|
|
|
&gRoots[38],
|
|
|
|
&gRoots[39],
|
|
|
|
&gRoots[40],
|
|
|
|
&gRoots[41],
|
|
|
|
&gRoots[42],
|
|
|
|
&gRoots[43],
|
|
|
|
&gRoots[44],
|
|
|
|
&gRoots[45],
|
|
|
|
&gRoots[46],
|
|
|
|
&gRoots[47],
|
|
|
|
&gRoots[48],
|
|
|
|
&gRoots[49],
|
|
|
|
&gRoots[50],
|
|
|
|
&gRoots[51],
|
|
|
|
&gRoots[52],
|
|
|
|
&gRoots[53],
|
|
|
|
&gRoots[54],
|
|
|
|
&gRoots[55],
|
|
|
|
&gRoots[56],
|
|
|
|
&gRoots[57],
|
|
|
|
&gRoots[58],
|
|
|
|
&gRoots[59],
|
|
|
|
&gRoots[60],
|
|
|
|
&gRoots[61],
|
|
|
|
&gRoots[62],
|
|
|
|
&gRoots[63],
|
2017-08-01 12:30:58 +00:00
|
|
|
};
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
int CProfileManager::FrameCounter = 0;
|
|
|
|
unsigned long int CProfileManager::ResetTime = 0;
|
2017-08-01 12:30:58 +00:00
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
CProfileIterator* CProfileManager::Get_Iterator(void)
|
|
|
|
{
|
|
|
|
int threadIndex = btQuickprofGetCurrentThreadIndex2();
|
|
|
|
if ((threadIndex < 0) || threadIndex >= BT_QUICKPROF_MAX_THREAD_COUNT)
|
|
|
|
return 0;
|
2017-08-01 12:30:58 +00:00
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
return new CProfileIterator(&gRoots[threadIndex]);
|
2017-08-01 12:30:58 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileManager::CleanupMemory(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
2019-01-03 13:26:51 +00:00
|
|
|
for (int i = 0; i < BT_QUICKPROF_MAX_THREAD_COUNT; i++)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
gRoots[i].CleanupMemory();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* CProfileManager::Start_Profile -- Begin a named profile *
|
|
|
|
* *
|
|
|
|
* Steps one level deeper into the tree, if a child already exists with the specified name *
|
|
|
|
* then it accumulates the profiling; otherwise a new child node is added to the profile tree. *
|
|
|
|
* *
|
|
|
|
* INPUT: *
|
|
|
|
* name - name of this profiling record *
|
|
|
|
* *
|
|
|
|
* WARNINGS: *
|
|
|
|
* The string used is assumed to be a static string; pointer compares are used throughout *
|
|
|
|
* the profiling code for efficiency. *
|
|
|
|
*=============================================================================================*/
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileManager::Start_Profile(const char* name)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
int threadIndex = btQuickprofGetCurrentThreadIndex2();
|
2019-01-03 13:26:51 +00:00
|
|
|
if ((threadIndex < 0) || threadIndex >= BT_QUICKPROF_MAX_THREAD_COUNT)
|
2017-08-01 12:30:58 +00:00
|
|
|
return;
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
if (name != gCurrentNodes[threadIndex]->Get_Name())
|
|
|
|
{
|
|
|
|
gCurrentNodes[threadIndex] = gCurrentNodes[threadIndex]->Get_Sub_Node(name);
|
2017-08-01 12:30:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
gCurrentNodes[threadIndex]->Call();
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* CProfileManager::Stop_Profile -- Stop timing and record the results. *
|
|
|
|
*=============================================================================================*/
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileManager::Stop_Profile(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
int threadIndex = btQuickprofGetCurrentThreadIndex2();
|
2019-01-03 13:26:51 +00:00
|
|
|
if ((threadIndex < 0) || threadIndex >= BT_QUICKPROF_MAX_THREAD_COUNT)
|
2017-08-01 12:30:58 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Return will indicate whether we should back up to our parent (we may
|
|
|
|
// be profiling a recursive function)
|
2019-01-03 13:26:51 +00:00
|
|
|
if (gCurrentNodes[threadIndex]->Return())
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
gCurrentNodes[threadIndex] = gCurrentNodes[threadIndex]->Get_Parent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* CProfileManager::Reset -- Reset the contents of the profiling system *
|
|
|
|
* *
|
|
|
|
* This resets everything except for the tree structure. All of the timing data is reset. *
|
|
|
|
*=============================================================================================*/
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileManager::Reset(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
gProfileClock.reset();
|
|
|
|
int threadIndex = btQuickprofGetCurrentThreadIndex2();
|
2019-01-03 13:26:51 +00:00
|
|
|
if ((threadIndex < 0) || threadIndex >= BT_QUICKPROF_MAX_THREAD_COUNT)
|
2017-08-01 12:30:58 +00:00
|
|
|
return;
|
|
|
|
gRoots[threadIndex].Reset();
|
|
|
|
gRoots[threadIndex].Call();
|
|
|
|
FrameCounter = 0;
|
|
|
|
Profile_Get_Ticks(&ResetTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* CProfileManager::Increment_Frame_Counter -- Increment the frame counter *
|
|
|
|
*=============================================================================================*/
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileManager::Increment_Frame_Counter(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
FrameCounter++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* CProfileManager::Get_Time_Since_Reset -- returns the elapsed time since last reset *
|
|
|
|
*=============================================================================================*/
|
2019-01-03 13:26:51 +00:00
|
|
|
float CProfileManager::Get_Time_Since_Reset(void)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
unsigned long int time;
|
|
|
|
Profile_Get_Ticks(&time);
|
|
|
|
time -= ResetTime;
|
|
|
|
return (float)time / Profile_Get_Tick_Rate();
|
|
|
|
}
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileManager::dumpRecursive(CProfileIterator* profileIterator, int spacing)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
profileIterator->First();
|
|
|
|
if (profileIterator->Is_Done())
|
|
|
|
return;
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
float accumulated_time = 0, parent_time = profileIterator->Is_Root() ? CProfileManager::Get_Time_Since_Reset() : profileIterator->Get_Current_Parent_Total_Time();
|
2017-08-01 12:30:58 +00:00
|
|
|
int i;
|
|
|
|
int frames_since_reset = CProfileManager::Get_Frame_Count_Since_Reset();
|
2019-01-03 13:26:51 +00:00
|
|
|
for (i = 0; i < spacing; i++) printf(".");
|
2017-08-01 12:30:58 +00:00
|
|
|
printf("----------------------------------\n");
|
2019-01-03 13:26:51 +00:00
|
|
|
for (i = 0; i < spacing; i++) printf(".");
|
|
|
|
printf("Profiling: %s (total running time: %.3f ms) ---\n", profileIterator->Get_Current_Parent_Name(), parent_time);
|
2017-08-01 12:30:58 +00:00
|
|
|
float totalTime = 0.f;
|
|
|
|
|
|
|
|
int numChildren = 0;
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
for (i = 0; !profileIterator->Is_Done(); i++, profileIterator->Next())
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
numChildren++;
|
|
|
|
float current_total_time = profileIterator->Get_Current_Total_Time();
|
|
|
|
accumulated_time += current_total_time;
|
|
|
|
float fraction = parent_time > SIMD_EPSILON ? (current_total_time / parent_time) * 100 : 0.f;
|
|
|
|
{
|
2019-01-03 13:26:51 +00:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < spacing; i++) printf(".");
|
2017-08-01 12:30:58 +00:00
|
|
|
}
|
2019-01-03 13:26:51 +00:00
|
|
|
printf("%d -- %s (%.2f %%) :: %.3f ms / frame (%d calls)\n", i, profileIterator->Get_Current_Name(), fraction, (current_total_time / (double)frames_since_reset), profileIterator->Get_Current_Total_Calls());
|
2017-08-01 12:30:58 +00:00
|
|
|
totalTime += current_total_time;
|
|
|
|
//recurse into children
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent_time < accumulated_time)
|
|
|
|
{
|
|
|
|
//printf("what's wrong\n");
|
|
|
|
}
|
2019-01-03 13:26:51 +00:00
|
|
|
for (i = 0; i < spacing; i++) printf(".");
|
|
|
|
printf("%s (%.3f %%) :: %.3f ms\n", "Unaccounted:", parent_time > SIMD_EPSILON ? ((parent_time - accumulated_time) / parent_time) * 100 : 0.f, parent_time - accumulated_time);
|
2017-08-01 12:30:58 +00:00
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
for (i = 0; i < numChildren; i++)
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
profileIterator->Enter_Child(i);
|
2019-01-03 13:26:51 +00:00
|
|
|
dumpRecursive(profileIterator, spacing + 3);
|
2017-08-01 12:30:58 +00:00
|
|
|
profileIterator->Enter_Parent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
void CProfileManager::dumpAll()
|
2017-08-01 12:30:58 +00:00
|
|
|
{
|
|
|
|
CProfileIterator* profileIterator = 0;
|
|
|
|
profileIterator = CProfileManager::Get_Iterator();
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
dumpRecursive(profileIterator, 0);
|
2017-08-01 12:30:58 +00:00
|
|
|
|
|
|
|
CProfileManager::Release_Iterator(profileIterator);
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
|
|
|
|
void btEnterProfileZoneDefault(const char* name)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void btLeaveProfileZoneDefault()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
void btEnterProfileZoneDefault(const char* name)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void btLeaveProfileZoneDefault()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif //BT_NO_PROFILE
|
|
|
|
|
|
|
|
|
2018-09-07 14:11:04 +00:00
|
|
|
// clang-format off
|
|
|
|
#if defined(_WIN32) && (defined(__MINGW32__) || defined(__MINGW64__))
|
|
|
|
#define BT_HAVE_TLS 1
|
|
|
|
#elif __APPLE__ && !TARGET_OS_IPHONE
|
|
|
|
// TODO: Modern versions of iOS support TLS now with updated version checking.
|
|
|
|
#define BT_HAVE_TLS 1
|
|
|
|
#elif __linux__
|
|
|
|
#define BT_HAVE_TLS 1
|
|
|
|
#endif
|
2017-08-01 12:30:58 +00:00
|
|
|
|
2018-09-07 14:11:04 +00:00
|
|
|
// __thread is broken on Andorid clang until r12b. See
|
|
|
|
// https://github.com/android-ndk/ndk/issues/8
|
|
|
|
#if defined(__ANDROID__) && defined(__clang__)
|
|
|
|
#if __has_include(<android/ndk-version.h>)
|
|
|
|
#include <android/ndk-version.h>
|
|
|
|
#endif // __has_include(<android/ndk-version.h>)
|
|
|
|
#if defined(__NDK_MAJOR__) && \
|
|
|
|
((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))
|
|
|
|
#undef BT_HAVE_TLS
|
|
|
|
#endif
|
|
|
|
#endif // defined(__ANDROID__) && defined(__clang__)
|
|
|
|
// clang-format on
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
unsigned int btQuickprofGetCurrentThreadIndex2()
|
|
|
|
{
|
|
|
|
const unsigned int kNullIndex = ~0U;
|
2017-08-01 12:30:58 +00:00
|
|
|
|
|
|
|
#if BT_THREADSAFE
|
2019-01-03 13:26:51 +00:00
|
|
|
return btGetCurrentThreadIndex();
|
2017-08-01 12:30:58 +00:00
|
|
|
#else
|
2018-09-07 14:11:04 +00:00
|
|
|
#if defined(BT_HAVE_TLS)
|
2019-01-03 13:26:51 +00:00
|
|
|
static __thread unsigned int sThreadIndex = kNullIndex;
|
2018-09-07 14:11:04 +00:00
|
|
|
#elif defined(_WIN32)
|
2019-01-03 13:26:51 +00:00
|
|
|
__declspec(thread) static unsigned int sThreadIndex = kNullIndex;
|
2017-08-01 12:30:58 +00:00
|
|
|
#else
|
2019-01-03 13:26:51 +00:00
|
|
|
unsigned int sThreadIndex = 0;
|
|
|
|
return -1;
|
2017-08-01 12:30:58 +00:00
|
|
|
#endif
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
static int gThreadCounter = 0;
|
2017-08-01 12:30:58 +00:00
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
if (sThreadIndex == kNullIndex)
|
|
|
|
{
|
|
|
|
sThreadIndex = gThreadCounter++;
|
|
|
|
}
|
|
|
|
return sThreadIndex;
|
|
|
|
#endif //BT_THREADSAFE
|
2017-08-01 12:30:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static btEnterProfileZoneFunc* bts_enterFunc = btEnterProfileZoneDefault;
|
|
|
|
static btLeaveProfileZoneFunc* bts_leaveFunc = btLeaveProfileZoneDefault;
|
|
|
|
|
|
|
|
void btEnterProfileZone(const char* name)
|
|
|
|
{
|
|
|
|
(bts_enterFunc)(name);
|
|
|
|
}
|
|
|
|
void btLeaveProfileZone()
|
|
|
|
{
|
|
|
|
(bts_leaveFunc)();
|
|
|
|
}
|
|
|
|
|
|
|
|
btEnterProfileZoneFunc* btGetCurrentEnterProfileZoneFunc()
|
|
|
|
{
|
2019-01-03 13:26:51 +00:00
|
|
|
return bts_enterFunc;
|
2017-08-01 12:30:58 +00:00
|
|
|
}
|
|
|
|
btLeaveProfileZoneFunc* btGetCurrentLeaveProfileZoneFunc()
|
|
|
|
{
|
|
|
|
return bts_leaveFunc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void btSetCustomEnterProfileZoneFunc(btEnterProfileZoneFunc* enterFunc)
|
|
|
|
{
|
|
|
|
bts_enterFunc = enterFunc;
|
|
|
|
}
|
|
|
|
void btSetCustomLeaveProfileZoneFunc(btLeaveProfileZoneFunc* leaveFunc)
|
|
|
|
{
|
|
|
|
bts_leaveFunc = leaveFunc;
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
CProfileSample::CProfileSample(const char* name)
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
btEnterProfileZone(name);
|
|
|
|
}
|
|
|
|
|
2019-01-03 13:26:51 +00:00
|
|
|
CProfileSample::~CProfileSample(void)
|
|
|
|
{
|
2017-08-01 12:30:58 +00:00
|
|
|
btLeaveProfileZone();
|
|
|
|
}
|