790 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			790 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 
 | |
| ***************************************************************************************************
 | |
| **
 | |
| ** 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
 | |
| 
 | |
| #if defined (SUNOS) || defined (__SUNOS__)
 | |
| #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
 | |
| 	#include <Xtl.h>
 | |
| #else //_XBOX
 | |
| 	#include <windows.h>
 | |
| 
 | |
| #if WINVER <0x0602
 | |
| #define GetTickCount64 GetTickCount
 | |
| #endif
 | |
| 
 | |
| #endif //_XBOX
 | |
| 
 | |
| #include <time.h>
 | |
| 
 | |
| 
 | |
| #else //_WIN32
 | |
| #include <sys/time.h>
 | |
| 
 | |
| #ifdef BT_LINUX_REALTIME
 | |
| //required linking against rt (librt)
 | |
| #include <time.h>
 | |
| #endif //BT_LINUX_REALTIME
 | |
| 
 | |
| #endif //_WIN32
 | |
| 
 | |
| #define mymin(a,b) (a > b ? a : b)
 | |
| 
 | |
| struct btClockData
 | |
| {
 | |
| 
 | |
| #ifdef BT_USE_WINDOWS_TIMERS
 | |
| 	LARGE_INTEGER mClockFrequency;
 | |
| 	LONGLONG mStartTick;
 | |
| 	LARGE_INTEGER mStartTime;
 | |
| #else
 | |
| #ifdef __CELLOS_LV2__
 | |
| 	uint64_t	mStartTime;
 | |
| #else
 | |
| #ifdef __APPLE__
 | |
|     uint64_t mStartTimeNano;
 | |
| #endif
 | |
| 	struct timeval mStartTime;
 | |
| #endif
 | |
| #endif //__CELLOS_LV2__
 | |
| 
 | |
| };
 | |
| 
 | |
| ///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;
 | |
| }
 | |
| 
 | |
| 
 | |
| 	/// Resets the initial reference time.
 | |
| void btClock::reset()
 | |
| {
 | |
| #ifdef BT_USE_WINDOWS_TIMERS
 | |
| 	QueryPerformanceCounter(&m_data->mStartTime);
 | |
| 	m_data->mStartTick = GetTickCount64();
 | |
| #else
 | |
| #ifdef __CELLOS_LV2__
 | |
| 
 | |
| 	typedef uint64_t  ClockSize;
 | |
| 	ClockSize newTime;
 | |
| 	//__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");
 | |
| 	SYS_TIMEBASE_GET( newTime );
 | |
| 	m_data->mStartTime = newTime;
 | |
| #else
 | |
| #ifdef __APPLE__
 | |
|     m_data->mStartTimeNano = mach_absolute_time();
 | |
| #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 -
 | |
| 		m_data->mStartTime.QuadPart;
 | |
| 		// Compute the number of millisecond ticks elapsed.
 | |
| 	unsigned long msecTicks = (unsigned long)(1000 * elapsedTime /
 | |
| 		m_data->mClockFrequency.QuadPart);
 | |
| 
 | |
| 		return msecTicks;
 | |
| #else
 | |
| 
 | |
| #ifdef __CELLOS_LV2__
 | |
| 		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);
 | |
| #else
 | |
| 
 | |
| 		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__
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 	/// Returns the time in us since the last call to reset or since
 | |
| 	/// the Clock was created.
 | |
| unsigned long long int btClock::getTimeMicroseconds()
 | |
| {
 | |
| #ifdef BT_USE_WINDOWS_TIMERS
 | |
| 	//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;
 | |
| #else
 | |
| 
 | |
| #ifdef __CELLOS_LV2__
 | |
| 		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);
 | |
| #else
 | |
| 
 | |
| 		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__
 | |
| #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
 | |
| 		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;
 | |
| #else
 | |
| 
 | |
| #ifdef __CELLOS_LV2__
 | |
| 		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);
 | |
| #else
 | |
| #ifdef __APPLE__
 | |
|     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__
 | |
|     
 | |
| #ifdef BT_LINUX_REALTIME
 | |
|     timespec ts;
 | |
|     clock_gettime(CLOCK_REALTIME,&ts);
 | |
|     return 1000000000*ts.tv_sec + ts.tv_nsec;
 | |
| #else
 | |
|     	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 
 | |
| }
 | |
| 
 | |
| 
 | |
| /// Returns the time in s since the last call to reset or since 
 | |
| /// 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;
 | |
| 
 | |
| 
 | |
| inline void Profile_Get_Ticks(unsigned long int * ticks)
 | |
| {
 | |
| 	*ticks = (unsigned long int)gProfileClock.getTimeMicroseconds();
 | |
| }
 | |
| 
 | |
| inline float Profile_Get_Tick_Rate(void)
 | |
| {
 | |
| //	return 1000000.f;
 | |
| 	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.                                                                         *
 | |
|  *=============================================================================================*/
 | |
| 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)
 | |
| {
 | |
| 	Reset();
 | |
| }
 | |
| 
 | |
| 
 | |
| void	CProfileNode::CleanupMemory()
 | |
| {
 | |
| 	delete ( Child);
 | |
| 	Child = NULL;
 | |
| 	delete ( Sibling);
 | |
| 	Sibling = NULL;
 | |
| }
 | |
| 
 | |
| CProfileNode::~CProfileNode( void )
 | |
| {
 | |
| 	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.                                                                     *
 | |
|  *=============================================================================================*/
 | |
| CProfileNode * CProfileNode::Get_Sub_Node( const char * name )
 | |
| {
 | |
| 	// Try to find this sub node
 | |
| 	CProfileNode * child = Child;
 | |
| 	while ( child ) {
 | |
| 		if ( child->Name == name ) {
 | |
| 			return child;
 | |
| 		}
 | |
| 		child = child->Sibling;
 | |
| 	}
 | |
| 
 | |
| 	// We didn't find it, so add it
 | |
| 
 | |
| 	CProfileNode * node = new CProfileNode( name, this );
 | |
| 	node->Sibling = Child;
 | |
| 	Child = node;
 | |
| 	return node;
 | |
| }
 | |
| 
 | |
| 
 | |
| void	CProfileNode::Reset( void )
 | |
| {
 | |
| 	TotalCalls = 0;
 | |
| 	TotalTime = 0.0f;
 | |
| 
 | |
| 
 | |
| 	if ( Child ) {
 | |
| 		Child->Reset();
 | |
| 	}
 | |
| 	if ( Sibling ) {
 | |
| 		Sibling->Reset();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| void	CProfileNode::Call( void )
 | |
| {
 | |
| 	TotalCalls++;
 | |
| 	if (RecursionCounter++ == 0) {
 | |
| 		Profile_Get_Ticks(&StartTime);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| bool	CProfileNode::Return( void )
 | |
| {
 | |
| 	if ( --RecursionCounter == 0 && TotalCalls != 0 ) {
 | |
| 		unsigned long int time;
 | |
| 		Profile_Get_Ticks(&time);
 | |
| 
 | |
| 		time-=StartTime;
 | |
| 		TotalTime += (float)time / Profile_Get_Tick_Rate();
 | |
| 	}
 | |
| 	return ( RecursionCounter == 0 );
 | |
| }
 | |
| 
 | |
| 
 | |
| /***************************************************************************************************
 | |
| **
 | |
| ** CProfileIterator
 | |
| **
 | |
| ***************************************************************************************************/
 | |
| CProfileIterator::CProfileIterator( CProfileNode * start )
 | |
| {
 | |
| 	CurrentParent = start;
 | |
| 	CurrentChild = CurrentParent->Get_Child();
 | |
| }
 | |
| 
 | |
| 
 | |
| void	CProfileIterator::First(void)
 | |
| {
 | |
| 	CurrentChild = CurrentParent->Get_Child();
 | |
| }
 | |
| 
 | |
| 
 | |
| void	CProfileIterator::Next(void)
 | |
| {
 | |
| 	CurrentChild = CurrentChild->Get_Sibling();
 | |
| }
 | |
| 
 | |
| 
 | |
| bool	CProfileIterator::Is_Done(void)
 | |
| {
 | |
| 	return CurrentChild == NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| void	CProfileIterator::Enter_Child( int index )
 | |
| {
 | |
| 	CurrentChild = CurrentParent->Get_Child();
 | |
| 	while ( (CurrentChild != NULL) && (index != 0) ) {
 | |
| 		index--;
 | |
| 		CurrentChild = CurrentChild->Get_Sibling();
 | |
| 	}
 | |
| 
 | |
| 	if ( CurrentChild != NULL ) {
 | |
| 		CurrentParent = CurrentChild;
 | |
| 		CurrentChild = CurrentParent->Get_Child();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| void	CProfileIterator::Enter_Parent( void )
 | |
| {
 | |
| 	if ( CurrentParent->Get_Parent() != NULL ) {
 | |
| 		CurrentParent = CurrentParent->Get_Parent();
 | |
| 	}
 | |
| 	CurrentChild = CurrentParent->Get_Child();
 | |
| }
 | |
| 
 | |
| 
 | |
| /***************************************************************************************************
 | |
| **
 | |
| ** CProfileManager
 | |
| **
 | |
| ***************************************************************************************************/
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 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],
 | |
| };
 | |
| 
 | |
| 
 | |
| int				CProfileManager::FrameCounter = 0;
 | |
| unsigned long int			CProfileManager::ResetTime = 0;
 | |
| 
 | |
| CProfileIterator *	CProfileManager::Get_Iterator( void )
 | |
| { 
 | |
| 
 | |
| 		int threadIndex = btQuickprofGetCurrentThreadIndex2();
 | |
| 		if ((threadIndex<0) || threadIndex >= BT_QUICKPROF_MAX_THREAD_COUNT)
 | |
| 			return 0;
 | |
| 
 | |
| 		return new CProfileIterator( &gRoots[threadIndex]); 
 | |
| }
 | |
| 
 | |
| void						CProfileManager::CleanupMemory(void)
 | |
| {
 | |
| 	for (int i=0;i<BT_QUICKPROF_MAX_THREAD_COUNT;i++)
 | |
| 	{
 | |
| 		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.                                                          *
 | |
|  *=============================================================================================*/
 | |
| void	CProfileManager::Start_Profile( const char * name )
 | |
| {
 | |
| 	int threadIndex = btQuickprofGetCurrentThreadIndex2();
 | |
| 	if ((threadIndex<0) || threadIndex >= BT_QUICKPROF_MAX_THREAD_COUNT)
 | |
| 		return;
 | |
| 
 | |
| 	if (name != gCurrentNodes[threadIndex]->Get_Name()) {
 | |
| 		gCurrentNodes[threadIndex] = gCurrentNodes[threadIndex]->Get_Sub_Node( name );
 | |
| 	}
 | |
| 
 | |
| 	gCurrentNodes[threadIndex]->Call();
 | |
| }
 | |
| 
 | |
| 
 | |
| /***********************************************************************************************
 | |
|  * CProfileManager::Stop_Profile -- Stop timing and record the results.                       *
 | |
|  *=============================================================================================*/
 | |
| void	CProfileManager::Stop_Profile( void )
 | |
| {
 | |
| 	int threadIndex = btQuickprofGetCurrentThreadIndex2();
 | |
| 	if ((threadIndex<0) || threadIndex >= BT_QUICKPROF_MAX_THREAD_COUNT)
 | |
| 		return;
 | |
| 
 | |
| 	// Return will indicate whether we should back up to our parent (we may
 | |
| 	// be profiling a recursive function)
 | |
| 	if (gCurrentNodes[threadIndex]->Return()) {
 | |
| 		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.  *
 | |
|  *=============================================================================================*/
 | |
| void	CProfileManager::Reset( void )
 | |
| {
 | |
| 	gProfileClock.reset();
 | |
| 	int threadIndex = btQuickprofGetCurrentThreadIndex2();
 | |
| 	if ((threadIndex<0) || threadIndex >= BT_QUICKPROF_MAX_THREAD_COUNT)
 | |
| 		return;
 | |
| 	gRoots[threadIndex].Reset();
 | |
| 	gRoots[threadIndex].Call();
 | |
| 	FrameCounter = 0;
 | |
| 	Profile_Get_Ticks(&ResetTime);
 | |
| }
 | |
| 
 | |
| 
 | |
| /***********************************************************************************************
 | |
|  * CProfileManager::Increment_Frame_Counter -- Increment the frame counter                    *
 | |
|  *=============================================================================================*/
 | |
| void CProfileManager::Increment_Frame_Counter( void )
 | |
| {
 | |
| 	FrameCounter++;
 | |
| }
 | |
| 
 | |
| 
 | |
| /***********************************************************************************************
 | |
|  * CProfileManager::Get_Time_Since_Reset -- returns the elapsed time since last reset         *
 | |
|  *=============================================================================================*/
 | |
| float CProfileManager::Get_Time_Since_Reset( void )
 | |
| {
 | |
| 	unsigned long int time;
 | |
| 	Profile_Get_Ticks(&time);
 | |
| 	time -= ResetTime;
 | |
| 	return (float)time / Profile_Get_Tick_Rate();
 | |
| }
 | |
| 
 | |
| #include <stdio.h>
 | |
| 
 | |
| void	CProfileManager::dumpRecursive(CProfileIterator* profileIterator, int spacing)
 | |
| {
 | |
| 	profileIterator->First();
 | |
| 	if (profileIterator->Is_Done())
 | |
| 		return;
 | |
| 
 | |
| 	float accumulated_time=0,parent_time = profileIterator->Is_Root() ? CProfileManager::Get_Time_Since_Reset() : profileIterator->Get_Current_Parent_Total_Time();
 | |
| 	int i;
 | |
| 	int frames_since_reset = CProfileManager::Get_Frame_Count_Since_Reset();
 | |
| 	for (i=0;i<spacing;i++)	printf(".");
 | |
| 	printf("----------------------------------\n");
 | |
| 	for (i=0;i<spacing;i++)	printf(".");
 | |
| 	printf("Profiling: %s (total running time: %.3f ms) ---\n",	profileIterator->Get_Current_Parent_Name(), parent_time );
 | |
| 	float totalTime = 0.f;
 | |
| 
 | |
| 
 | |
| 	int numChildren = 0;
 | |
| 
 | |
| 	for (i = 0; !profileIterator->Is_Done(); i++,profileIterator->Next())
 | |
| 	{
 | |
| 		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;
 | |
| 		{
 | |
| 			int i;	for (i=0;i<spacing;i++)	printf(".");
 | |
| 		}
 | |
| 		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());
 | |
| 		totalTime += current_total_time;
 | |
| 		//recurse into children
 | |
| 	}
 | |
| 
 | |
| 	if (parent_time < accumulated_time)
 | |
| 	{
 | |
| 		//printf("what's wrong\n");
 | |
| 	}
 | |
| 	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);
 | |
| 
 | |
| 	for (i=0;i<numChildren;i++)
 | |
| 	{
 | |
| 		profileIterator->Enter_Child(i);
 | |
| 		dumpRecursive(profileIterator,spacing+3);
 | |
| 		profileIterator->Enter_Parent();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void	CProfileManager::dumpAll()
 | |
| {
 | |
| 	CProfileIterator* profileIterator = 0;
 | |
| 	profileIterator = CProfileManager::Get_Iterator();
 | |
| 
 | |
| 	dumpRecursive(profileIterator,0);
 | |
| 
 | |
| 	CProfileManager::Release_Iterator(profileIterator);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| unsigned int btQuickprofGetCurrentThreadIndex2()
 | |
| {
 | |
| #if BT_THREADSAFE
 | |
|     return btGetCurrentThreadIndex();
 | |
| #else // #if BT_THREADSAFE
 | |
| 	const unsigned int kNullIndex = ~0U;
 | |
| #ifdef _WIN32
 | |
|     #if defined(__MINGW32__) || defined(__MINGW64__)
 | |
|         static __thread unsigned int sThreadIndex = kNullIndex;
 | |
|     #else
 | |
|         __declspec( thread ) static unsigned int sThreadIndex = kNullIndex;
 | |
|     #endif
 | |
| #else
 | |
| #ifdef __APPLE__
 | |
| 	#if TARGET_OS_IPHONE
 | |
| 		unsigned int sThreadIndex = 0;
 | |
| 		return -1;
 | |
| 	#else
 | |
| 		static __thread unsigned int sThreadIndex = kNullIndex;
 | |
| 	#endif
 | |
| #else//__APPLE__
 | |
| #if __linux__
 | |
| 	static __thread unsigned int sThreadIndex = kNullIndex;
 | |
| #else
 | |
| 	unsigned int sThreadIndex = 0;
 | |
| 	return -1;
 | |
| #endif
 | |
| #endif//__APPLE__
 | |
| 	
 | |
| #endif
 | |
| 	static int gThreadCounter=0;
 | |
| 
 | |
| 	if ( sThreadIndex == kNullIndex )
 | |
| 	{
 | |
| 		sThreadIndex = gThreadCounter++;
 | |
| 	}
 | |
| 	return sThreadIndex;
 | |
| #endif // #else // #if BT_THREADSAFE
 | |
| }
 | |
| 
 | |
| void	btEnterProfileZoneDefault(const char* name)
 | |
| {
 | |
| 	CProfileManager::Start_Profile( name ); 
 | |
| }
 | |
| void	btLeaveProfileZoneDefault()
 | |
| {
 | |
| 	CProfileManager::Stop_Profile(); 
 | |
| }
 | |
| 
 | |
| 
 | |
| #else
 | |
| void	btEnterProfileZoneDefault(const char* name)
 | |
| {
 | |
| }
 | |
| void	btLeaveProfileZoneDefault()
 | |
| {
 | |
| }
 | |
| #endif //BT_NO_PROFILE
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| static btEnterProfileZoneFunc* bts_enterFunc = btEnterProfileZoneDefault;
 | |
| static btLeaveProfileZoneFunc* bts_leaveFunc = btLeaveProfileZoneDefault;
 | |
| 
 | |
| void btEnterProfileZone(const char* name)
 | |
| {
 | |
| 	(bts_enterFunc)(name);
 | |
| }
 | |
| void btLeaveProfileZone()
 | |
| {
 | |
| 	(bts_leaveFunc)();
 | |
| }
 | |
| 
 | |
| btEnterProfileZoneFunc* btGetCurrentEnterProfileZoneFunc()
 | |
| {
 | |
| 	return bts_enterFunc ;
 | |
| }
 | |
| btLeaveProfileZoneFunc* btGetCurrentLeaveProfileZoneFunc()
 | |
| {
 | |
| 	return bts_leaveFunc;
 | |
| }
 | |
| 
 | |
| 
 | |
| void btSetCustomEnterProfileZoneFunc(btEnterProfileZoneFunc* enterFunc)
 | |
| {
 | |
| 	bts_enterFunc = enterFunc;
 | |
| }
 | |
| void btSetCustomLeaveProfileZoneFunc(btLeaveProfileZoneFunc* leaveFunc)
 | |
| {
 | |
| 	bts_leaveFunc = leaveFunc;
 | |
| }
 | |
| 
 | |
| CProfileSample::CProfileSample( const char * name )
 | |
| { 
 | |
| 	btEnterProfileZone(name);
 | |
| }
 | |
| 
 | |
| CProfileSample::~CProfileSample( void )					
 | |
| { 
 | |
| 	btLeaveProfileZone();
 | |
| }
 | |
| 
 |