diff --git a/CMakeLists.txt b/CMakeLists.txt index f874bc4d..68307462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,10 +10,15 @@ SET(LIBRARY_OUTPUT_PATH lib/) # Options OPTION(COMPILE_EXAMPLES "Select this if you want to build the examples" OFF) OPTION(COMPILE_TESTS "Select this if you want to build the tests" OFF) +OPTION(PROFILING_ENABLED "Select this if you want to compile with enabled profiling" OFF) # Headers INCLUDE_DIRECTORIES(src) +IF (PROFILING_ENABLED) + ADD_DEFINITIONS(-DIS_PROFILING_ACTIVE) +ENDIF (PROFILING_ENABLED) + # Library configuration file ( GLOB_RECURSE diff --git a/src/engine/Profiler.cpp b/src/engine/Profiler.cpp index 8b137891..0c915469 100644 --- a/src/engine/Profiler.cpp +++ b/src/engine/Profiler.cpp @@ -1 +1,141 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://code.google.com/p/reactphysics3d/ * +* Copyright (c) 2010-2013 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ +#ifdef IS_PROFILING_ACTIVE + +// Libraries +#include "Profiler.h" + +using namespace reactphysics3d; + +// Initialization of static variables +ProfileNode Profiler::mRootNode("Root", NULL); +ProfileNode* Profiler::mCurrentNode = &Profiler::mRootNode; +uint Profiler::mFrameCounter = 0; + +// Constructor +ProfileNode::ProfileNode(const char* name, ProfileNode* parentNode) + :mName(name), mNbTotalCalls(0), mStartingTime(0), mTotalTime(0), + mRecursionCounter(0), mParentNode(parentNode), mChildNode(NULL), + mSiblingNode(NULL) { + +} + +// Destructor +ProfileNode::~ProfileNode() { + + delete mChildNode; + delete mSiblingNode; +} + +// Return a pointer to a sub node with a given name +ProfileNode* ProfileNode::findSubNode(const char* name) { + + // Try to find the node among the child nodes + ProfileNode* child = mChildNode; + while (child != NULL) { + if (child->mName == name) { + return child; + } + child = child->mSiblingNode; + } + + // The nose has not been found. Therefore, we create it + // and add it to the profiler tree + ProfileNode* newNode = new ProfileNode(name, this); + newNode->mSiblingNode = child; + child = newNode; + + return newNode; +} + +// Called when we enter the block of code corresponding to this profile node +void ProfileNode::enterBlockOfCode() { + mNbTotalCalls++; + + // If the current code is not called recursively + if (mRecursionCounter == 0) { + + // Get the current system time to initialize the starting time of + // the profiling of the current block of code + mStartingTime = Timer::getCurrentSystemTime(); + } + + mRecursionCounter++; +} + +// Called when we exit the block of code corresponding to this profile node +bool ProfileNode::exitBlockOfCode() { + mRecursionCounter--; + + if (mRecursionCounter == 0 && mNbTotalCalls != 0) { + + // Get the current system time + long double currentTime = Timer::getCurrentSystemTime(); + + // Increase the total elasped time in the current block of code + mTotalTime += currentTime - mStartingTime; + } + + // Return true if the current code is not recursing + return (mRecursionCounter == 0); +} + +// Method called when we want to start profiling a block of code. +void Profiler::startProfilingBlock(const char* name) { + + // Look for the node in the tree that corresponds to the block of + // code to profile + if (name != mCurrentNode->getName()) { + mCurrentNode = mCurrentNode->findSubNode(name); + } + + // Start profile the node + mCurrentNode->enterBlockOfCode(); +} + +// Method called at the end of the scope where the +// startProfilingBlock() method has been called. +void Profiler::stopProfilingBlock() { + + // Go to the parent node unless if the current block + // of code is recursing + if (mCurrentNode->exitBlockOfCode()) { + mCurrentNode = mCurrentNode->getParentNode(); + } + +} + +// Return an iterator over the profiler tree starting at the root +ProfileNodeIterator* Profiler::getIterator() { + return new ProfileNodeIterator(&mRootNode); +} + +// Print the report of the profiler in a given output stream +void printReport(std::ostream& outputStream) { + +} + +#endif diff --git a/src/engine/Profiler.h b/src/engine/Profiler.h index 51bac2c1..1768b01e 100644 --- a/src/engine/Profiler.h +++ b/src/engine/Profiler.h @@ -26,7 +26,146 @@ #ifndef PROFILER_H #define PROFILER_H +#ifdef IS_PROFILING_ACTIVE + // Libraries +#include "../configuration.h" +#include "Timer.h" + +/// ReactPhysics3D namespace +namespace reactphysics3d { + +// Class ProfileNode +/** + * It represents a profile sample in the profiler tree. + */ +class ProfileNode { + + private : + + /// Name of the node + const char* mName; + + /// Total number of calls of this node + uint mNbTotalCalls; + + /// Starting time of the sampling of corresponding block of code + long double mStartingTime; + + /// Total time spent in the block of code + long double mTotalTime; + + /// Recursion counter + int mRecursionCounter; + + /// Pointer to the parent node + ProfileNode* mParentNode; + + /// Pointer to a child node + ProfileNode* mChildNode; + + /// Pointer to a sibling node + ProfileNode* mSiblingNode; + + public : + + /// Constructor + ProfileNode(const char* name, ProfileNode* parentNode); + + /// Destructor + ~ProfileNode(); + + /// Return a pointer to a sub node + ProfileNode* findSubNode(const char* name); + + /// Return a pointer to the parent node + ProfileNode* getParentNode(); + + /// Return a pointer to a sibling node + ProfileNode* getSiblingNode(); + + /// Return a pointer to a child node + ProfileNode* getChildNode(); + + /// Return the name of the node + const char* getName(); + + /// Return the total number of call of the corresponding block of code + uint getNbTotalCalls() const; + + /// Return the total time spent in the block of code + long double getTotalTime() const; + + /// Called when we enter the block of code corresponding to this profile node + void enterBlockOfCode(); + + /// Called when we exit the block of code corresponding to this profile node + bool exitBlockOfCode(); +}; + +// Class ProfileNodeIterator +/** + * This class allow us to iterator over the profiler tree. + */ +class ProfileNodeIterator { + + private : + + /// Current parent node + ProfileNode* mCurrentParent; + + /// Current child node + ProfileNode* mCurrentChild; + + public : + + /// Constructor + ProfileNodeIterator(ProfileNode* startingNode); + + /// Go to the first node + void first(); + + /// Go to the next node + void next(); + + /// Enter a given child node + void enterChild(); + +}; + +// Class Profiler +/** + * This is the main class of the profiler. This profiler is based on "Real-Time Hierarchical + * Profiling" article from "Game Programming Gems 3" by Greg Hjelstrom and Byon Garrabrant. + */ +class Profiler { + + private : + + /// Root node of the profiler tree + static ProfileNode mRootNode; + + /// Current node in the current execution + static ProfileNode* mCurrentNode; + + /// Frame counter + static uint mFrameCounter; + + public : + + /// Method called when we want to start profiling a block of code. + static void startProfilingBlock(const char *name); + + /// Method called at the end of the scope where the + /// startProfilingBlock() method has been called. + static void stopProfilingBlock(); + + /// Return an iterator over the profiler tree starting at the root + static ProfileNodeIterator* getIterator(); + + /// Print the report of the profiler in a given output stream + static void printReport(std::ostream& outputStream); +}; // Class ProfileSample /** @@ -42,7 +181,7 @@ class ProfileSample { ProfileSample(const char* name) { // Ask the profiler to start profiling a block of code - Profiler::startProfilingBlock(); + Profiler::startProfilingBlock(name); } /// Destructor @@ -56,32 +195,43 @@ class ProfileSample { /// Use this macro to start profile a block of code #define PROFILE(name) ProfileSample profileSample(name) -// Class ProfileNode -/** - * Node of the profiler tree - */ -class ProfileNode { +/// Return a pointer to the parent node +ProfileNode* ProfileNode::getParentNode() { + return mParentNode; +} -}; +/// Return a pointer to a sibling node +ProfileNode* ProfileNode::getSiblingNode() { + return mSiblingNode; +} -// Class Profiler -/** - * This is the main class of the profiler - */ -class Profiler { +/// Return a pointer to a child node +ProfileNode* ProfileNode::getChildNode() { + return mChildNode; +} - public : +/// Return the name of the node +const char* ProfileNode::getName() { + return mName; +} - /// Method called when we want to start profiling a block of code. - static void startProfilingBlock() { +/// Return the total number of call of the corresponding block of code +uint ProfileNode::getNbTotalCalls() const { + return mNbTotalCalls; +} - } +/// Return the total time spent in the block of code +long double ProfileNode::getTotalTime() const { + return mTotalTime; +} - /// Method called at the end of the scope where the startProfilingBlock() method - /// has been called. - static void stopProfilingBlock() { +} - } -}; +#else // In profile is not active + +// Empty macro in case profiling is not active +#define PROFILE(name) + +#endif #endif diff --git a/src/engine/Timer.cpp b/src/engine/Timer.cpp index 007a4240..74c08086 100644 --- a/src/engine/Timer.cpp +++ b/src/engine/Timer.cpp @@ -39,6 +39,23 @@ Timer::~Timer() { } +// Return the current time of the system +long double Timer::getCurrentSystemTime() { + + #if defined(WINDOWS_OS) + LARGE_INTEGER ticksPerSecond; + LARGE_INTEGER ticks; + QueryPerformanceFrequency(&ticksPerSecond); + QueryPerformanceCounter(&ticks); + return (long double(ticks.QuadPart) / long double(ticksPerSecond.QuadPart)); + #else + // Initialize the lastUpdateTime with the current time in seconds + timeval timeValue; + gettimeofday(&timeValue, NULL); + return (timeValue.tv_sec + (timeValue.tv_usec / 1000000.0)); + #endif +} + diff --git a/src/engine/Timer.h b/src/engine/Timer.h index d165dbc6..7c70b52c 100644 --- a/src/engine/Timer.h +++ b/src/engine/Timer.h @@ -74,6 +74,7 @@ class Timer { /// True if the timer is running bool mIsRunning; + // -------------------- Methods -------------------- // /// Private copy-constructor @@ -98,8 +99,8 @@ class Timer { /// Set the timestep of the physics engine void setTimeStep(double timeStep); - /// Return the current time - long double getTime() const; + /// Return the current time of the physics engine + long double getPhysicsTime() const; /// Start the timer void start(); @@ -121,6 +122,9 @@ class Timer { /// Compute the interpolation factor decimal computeInterpolationFactor(); + + /// Return the current time of the system + static long double getCurrentSystemTime(); }; // Return the timestep of the physics engine @@ -135,7 +139,7 @@ inline void Timer::setTimeStep(double timeStep) { } // Return the current time -inline long double Timer::getTime() const { +inline long double Timer::getPhysicsTime() const { return mTime; } @@ -147,19 +151,9 @@ inline bool Timer::getIsRunning() const { // Start the timer inline void Timer::start() { if (!mIsRunning) { - -#if defined(WINDOWS_OS) - LARGE_INTEGER ticksPerSecond; - LARGE_INTEGER ticks; - QueryPerformanceFrequency(&ticksPerSecond); - QueryPerformanceCounter(&ticks); - mLastUpdateTime = double(ticks.QuadPart) / double(ticksPerSecond.QuadPart); -#else - // Initialize the lastUpdateTime with the current time in seconds - timeval timeValue; - gettimeofday(&timeValue, NULL); - mLastUpdateTime = timeValue.tv_sec + (timeValue.tv_usec / 1000000.0); -#endif + + // Get the current system time + mLastUpdateTime = getCurrentSystemTime(); mAccumulator = 0.0; mIsRunning = true; @@ -168,7 +162,6 @@ inline void Timer::start() { // Stop the timer inline void Timer::stop() { - std::cout << "Timer stop" << std::endl; mIsRunning = false; } @@ -195,20 +188,9 @@ inline decimal Timer::computeInterpolationFactor() { // Compute the time since the last update() call and add it to the accumulator inline void Timer::update() { - long double currentTime; - -#if defined(WINDOWS_OS) - LARGE_INTEGER ticksPerSecond; - LARGE_INTEGER ticks; - QueryPerformanceFrequency(&ticksPerSecond); - QueryPerformanceCounter(&ticks); - currentTime = double(ticks.QuadPart) / double(ticksPerSecond.QuadPart); -#else - // Compute the current time is seconds - timeval timeValue; - gettimeofday(&timeValue, NULL); - currentTime = timeValue.tv_sec + (timeValue.tv_usec / 1000000.0); -#endif + + // Get the current system time + long double currentTime = getCurrentSystemTime(); // Compute the delta display time between two display frames mDeltaTime = currentTime - mLastUpdateTime;