| |
| |
| /*************************************************************************************************** |
| ** |
| ** 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 "LinearMath/btQuickprof.h" |
| |
| |
| #ifdef USE_BT_CLOCK |
| |
| static btClock gProfileClock; |
| |
| inline void Profile_Get_Ticks(unsigned long int * ticks) |
| { |
| *ticks = 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 ) |
| { |
| Reset(); |
| } |
| |
| |
| void CProfileNode::CleanupMemory() |
| { |
| delete ( Child); |
| Child = NULL; |
| delete ( Sibling); |
| Sibling = NULL; |
| } |
| |
| CProfileNode::~CProfileNode( void ) |
| { |
| delete ( Child); |
| delete ( Sibling); |
| } |
| |
| |
| /*********************************************************************************************** |
| * 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 CProfileManager::Root( "Root", NULL ); |
| CProfileNode * CProfileManager::CurrentNode = &CProfileManager::Root; |
| int CProfileManager::FrameCounter = 0; |
| unsigned long int CProfileManager::ResetTime = 0; |
| |
| |
| /*********************************************************************************************** |
| * 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 ) |
| { |
| if (name != CurrentNode->Get_Name()) { |
| CurrentNode = CurrentNode->Get_Sub_Node( name ); |
| } |
| |
| CurrentNode->Call(); |
| } |
| |
| |
| /*********************************************************************************************** |
| * CProfileManager::Stop_Profile -- Stop timing and record the results. * |
| *=============================================================================================*/ |
| void CProfileManager::Stop_Profile( void ) |
| { |
| // Return will indicate whether we should back up to our parent (we may |
| // be profiling a recursive function) |
| if (CurrentNode->Return()) { |
| CurrentNode = CurrentNode->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(); |
| Root.Reset(); |
| Root.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); |
| } |
| |
| |
| |
| #endif //USE_BT_CLOCK |
| |