[657] | 1 | /*
|
---|
| 2 | -----------------------------------------------------------------------------
|
---|
| 3 | This source file is part of OGRE
|
---|
| 4 | (Object-oriented Graphics Rendering Engine)
|
---|
| 5 | For the latest info, see http://www.ogre3d.org/
|
---|
| 6 |
|
---|
| 7 | Copyright (c) 2000-2005 The OGRE Team
|
---|
| 8 | Also see acknowledgements in Readme.html
|
---|
| 9 |
|
---|
| 10 | This program is free software; you can redistribute it and/or modify it under
|
---|
| 11 | the terms of the GNU Lesser General Public License as published by the Free Software
|
---|
| 12 | Foundation; either version 2 of the License, or (at your option) any later
|
---|
| 13 | version.
|
---|
| 14 |
|
---|
| 15 | This program is distributed in the hope that it will be useful, but WITHOUT
|
---|
| 16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
---|
| 17 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
---|
| 18 |
|
---|
| 19 | You should have received a copy of the GNU Lesser General Public License along with
|
---|
| 20 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
---|
| 21 | Place - Suite 330, Boston, MA 02111-1307, USA, or go to
|
---|
| 22 | http://www.gnu.org/copyleft/lesser.txt.
|
---|
| 23 | -----------------------------------------------------------------------------
|
---|
| 24 | */
|
---|
| 25 | /*
|
---|
| 26 |
|
---|
| 27 | Although the code is original, many of the ideas for the profiler were borrowed from
|
---|
| 28 | "Real-Time In-Game Profiling" by Steve Rabin which can be found in Game Programming
|
---|
| 29 | Gems 1.
|
---|
| 30 |
|
---|
| 31 | This code can easily be adapted to your own non-Ogre project. The only code that is
|
---|
| 32 | Ogre-dependent is in the visualization/logging routines and the use of the Timer class.
|
---|
| 33 |
|
---|
| 34 | Enjoy!
|
---|
| 35 |
|
---|
| 36 | */
|
---|
| 37 |
|
---|
| 38 | #ifndef __Profiler_H__
|
---|
| 39 | #define __Profiler_H__
|
---|
| 40 |
|
---|
| 41 | #include "OgrePrerequisites.h"
|
---|
| 42 | #include "OgreSingleton.h"
|
---|
| 43 | #include "OgreString.h"
|
---|
| 44 | #include "OgreOverlay.h"
|
---|
| 45 |
|
---|
| 46 | #if OGRE_PROFILING == 1
|
---|
| 47 | # if OGRE_COMPILER != OGRE_COMPILER_BORL
|
---|
| 48 | # define OgreProfile( a ) Ogre::Profile _OgreProfileInstance( (a) )
|
---|
| 49 | # define OgreProfileBegin( a ) Ogre::Profiler::getSingleton().beginProfile( (a) )
|
---|
| 50 | # define OgreProfileEnd( a ) Ogre::Profiler::getSingleton().endProfile( (a) )
|
---|
| 51 | # else
|
---|
| 52 | # define OgreProfile( a ) Ogre::Profile _OgreProfileInstance( __FUNC__ )
|
---|
| 53 | # define OgreProfileBegin( a ) Ogre::Profiler::getSingleton().beginProfile( __FUNC__ )
|
---|
| 54 | # define OgreProfileEnd( a ) Ogre::Profiler::getSingleton().endProfile( __FUNC__ )
|
---|
| 55 | # endif
|
---|
| 56 | #else
|
---|
| 57 | # define OgreProfile( a )
|
---|
| 58 | # define OgreProfileBegin( a )
|
---|
| 59 | # define OgreProfileEnd( a )
|
---|
| 60 | #endif
|
---|
| 61 |
|
---|
| 62 | namespace Ogre {
|
---|
| 63 |
|
---|
| 64 | /** An individual profile that will be processed by the Profiler
|
---|
| 65 | @remarks
|
---|
| 66 | Use the macro OgreProfile(name) instead of instantiating this profile directly
|
---|
| 67 | @remarks
|
---|
| 68 | We use this Profile to allow scoping rules to signify the beginning and end of
|
---|
| 69 | the profile. Use the Profiler singleton (through the macro OgreProfileBegin(name)
|
---|
| 70 | and OgreProfileEnd(name)) directly if you want a profile to last
|
---|
| 71 | outside of a scope (ie the main game loop).
|
---|
| 72 | @author Amit Mathew (amitmathew (at) yahoo (dot) com)
|
---|
| 73 | */
|
---|
| 74 | class _OgreExport Profile {
|
---|
| 75 |
|
---|
| 76 | public:
|
---|
| 77 | Profile(const String& profileName);
|
---|
| 78 | ~Profile();
|
---|
| 79 |
|
---|
| 80 | protected:
|
---|
| 81 |
|
---|
| 82 | /// The name of this profile
|
---|
| 83 | String mName;
|
---|
| 84 |
|
---|
| 85 |
|
---|
| 86 | };
|
---|
| 87 |
|
---|
| 88 | /** The profiler allows you to measure the performance of your code
|
---|
| 89 | @remarks
|
---|
| 90 | Do not create profiles directly from this unless you want a profile to last
|
---|
| 91 | outside of its scope (ie the main game loop). For most cases, use the macro
|
---|
| 92 | OgreProfile(name) and braces to limit the scope. You must enable the Profile
|
---|
| 93 | before you can used it with setEnabled(true). If you want to disable profiling
|
---|
| 94 | in Ogre, simply set the macro OGRE_PROFILING to 0.
|
---|
| 95 | @author Amit Mathew (amitmathew (at) yahoo (dot) com)
|
---|
| 96 | @todo resolve artificial cap on number of profiles displayed
|
---|
| 97 | @todo fix display ordering of profiles not called every frame
|
---|
| 98 | */
|
---|
| 99 | class _OgreExport Profiler : public Singleton<Profiler> {
|
---|
| 100 |
|
---|
| 101 | public:
|
---|
| 102 | Profiler();
|
---|
| 103 | ~Profiler();
|
---|
| 104 |
|
---|
| 105 | /** Sets the timer for the profiler */
|
---|
| 106 | void setTimer(Timer* t);
|
---|
| 107 |
|
---|
| 108 | /** Retrieves the timer for the profiler */
|
---|
| 109 | Timer* getTimer();
|
---|
| 110 |
|
---|
| 111 | /** Begins a profile
|
---|
| 112 | @remarks
|
---|
| 113 | Use the macro OgreProfileBegin(name) instead of calling this directly
|
---|
| 114 | so that profiling can be ignored in the release version of your app.
|
---|
| 115 | @remarks
|
---|
| 116 | You only use the macro (or this) if you want a profile to last outside
|
---|
| 117 | of its scope (ie the main game loop). If you use this function, make sure you
|
---|
| 118 | use a corresponding OgreProfileEnd(name). Usually you would use the macro
|
---|
| 119 | OgreProfile(name). This function will be ignored for a profile that has been
|
---|
| 120 | disabled or if the profiler is disabled.
|
---|
| 121 | @param profileName Must be unique and must not be an empty string
|
---|
| 122 | */
|
---|
| 123 | void beginProfile(const String& profileName);
|
---|
| 124 |
|
---|
| 125 | /** Ends a profile
|
---|
| 126 | @remarks
|
---|
| 127 | Use the macro OgreProfileEnd(name) instead of calling this directly so that
|
---|
| 128 | profiling can be ignored in the release version of your app.
|
---|
| 129 | @remarks
|
---|
| 130 | This function is usually not called directly unless you want a profile to
|
---|
| 131 | last outside of its scope. In most cases, using the macro OgreProfile(name)
|
---|
| 132 | which will call this function automatically when it goes out of scope. Make
|
---|
| 133 | sure the name of this profile matches its corresponding beginProfile name.
|
---|
| 134 | This function will be ignored for a profile that has been disabled or if the
|
---|
| 135 | profiler is disabled.
|
---|
| 136 | */
|
---|
| 137 | void endProfile(const String& profileName);
|
---|
| 138 |
|
---|
| 139 | /** Sets whether this profiler is enabled. Only takes effect after the
|
---|
| 140 | the frame has ended.
|
---|
| 141 | @remarks When this is called the first time with the parameter true,
|
---|
| 142 | it initializes the GUI for the Profiler
|
---|
| 143 | */
|
---|
| 144 | void setEnabled(bool enabled);
|
---|
| 145 |
|
---|
| 146 | /** Gets whether this profiler is enabled */
|
---|
| 147 | bool getEnabled() const;
|
---|
| 148 |
|
---|
| 149 | /** Enables a previously disabled profile
|
---|
| 150 | @remarks Only enables the profile if this function is not
|
---|
| 151 | called during the profile it is trying to enable.
|
---|
| 152 | */
|
---|
| 153 | void enableProfile(const String& profileName);
|
---|
| 154 |
|
---|
| 155 | /** Disables a profile
|
---|
| 156 | @remarks Only disables the profile if this function is not called during
|
---|
| 157 | the profile it is trying to disable.
|
---|
| 158 | */
|
---|
| 159 | void disableProfile(const String& profileName);
|
---|
| 160 |
|
---|
| 161 | /** Returns true if the specified profile reaches a new frame time maximum
|
---|
| 162 | @remarks If this is called during a frame, it will be reading the results
|
---|
| 163 | from the previous frame. Therefore, it is best to use this after the frame
|
---|
| 164 | has ended.
|
---|
| 165 | */
|
---|
| 166 | bool watchForMax(const String& profileName);
|
---|
| 167 |
|
---|
| 168 | /** Returns true if the specified profile reaches a new frame time minimum
|
---|
| 169 | @remarks If this is called during a frame, it will be reading the results
|
---|
| 170 | from the previous frame. Therefore, it is best to use this after the frame
|
---|
| 171 | has ended.
|
---|
| 172 | */
|
---|
| 173 | bool watchForMin(const String& profileName);
|
---|
| 174 |
|
---|
| 175 | /** Returns true if the specified profile goes over or under the given limit
|
---|
| 176 | frame time
|
---|
| 177 | @remarks If this is called during a frame, it will be reading the results
|
---|
| 178 | from the previous frame. Therefore, it is best to use this after the frame
|
---|
| 179 | has ended.
|
---|
| 180 | @param limit A number between 0 and 1 representing the percentage of frame time
|
---|
| 181 | @param greaterThan If true, this will return whether the limit is exceeded. Otherwise,
|
---|
| 182 | it will return if the frame time has gone under this limit.
|
---|
| 183 | */
|
---|
| 184 | bool watchForLimit(const String& profileName, Real limit, bool greaterThan = true);
|
---|
| 185 |
|
---|
| 186 | /** Outputs current profile statistics to the log */
|
---|
| 187 | void logResults();
|
---|
| 188 |
|
---|
| 189 | /** Clears the profiler statistics */
|
---|
| 190 | void reset();
|
---|
| 191 |
|
---|
| 192 | /** Sets the Profiler so the display of results are updated ever n frames*/
|
---|
| 193 | void setUpdateDisplayFrequency(uint freq);
|
---|
| 194 |
|
---|
| 195 | /** Gets the frequency that the Profiler display is updated */
|
---|
| 196 | uint getUpdateDisplayFrequency() const;
|
---|
| 197 |
|
---|
| 198 | /** Override standard Singleton retrieval.
|
---|
| 199 | @remarks
|
---|
| 200 | Why do we do this? Well, it's because the Singleton
|
---|
| 201 | implementation is in a .h file, which means it gets compiled
|
---|
| 202 | into anybody who includes it. This is needed for the
|
---|
| 203 | Singleton template to work, but we actually only want it
|
---|
| 204 | compiled into the implementation of the class based on the
|
---|
| 205 | Singleton, not all of them. If we don't change this, we get
|
---|
| 206 | link errors when trying to use the Singleton-based class from
|
---|
| 207 | an outside dll.
|
---|
| 208 | @par
|
---|
| 209 | This method just delegates to the template version anyway,
|
---|
| 210 | but the implementation stays in this single compilation unit,
|
---|
| 211 | preventing link errors.
|
---|
| 212 | */
|
---|
| 213 | static Profiler& getSingleton(void);
|
---|
| 214 | /** Override standard Singleton retrieval.
|
---|
| 215 | @remarks
|
---|
| 216 | Why do we do this? Well, it's because the Singleton
|
---|
| 217 | implementation is in a .h file, which means it gets compiled
|
---|
| 218 | into anybody who includes it. This is needed for the
|
---|
| 219 | Singleton template to work, but we actually only want it
|
---|
| 220 | compiled into the implementation of the class based on the
|
---|
| 221 | Singleton, not all of them. If we don't change this, we get
|
---|
| 222 | link errors when trying to use the Singleton-based class from
|
---|
| 223 | an outside dll.
|
---|
| 224 | @par
|
---|
| 225 | This method just delegates to the template version anyway,
|
---|
| 226 | but the implementation stays in this single compilation unit,
|
---|
| 227 | preventing link errors.
|
---|
| 228 | */
|
---|
| 229 | static Profiler* getSingletonPtr(void);
|
---|
| 230 |
|
---|
| 231 | protected:
|
---|
| 232 |
|
---|
| 233 | /** Initializes the profiler's gui elements */
|
---|
| 234 | void initialize();
|
---|
| 235 |
|
---|
| 236 | /** Prints the profiling results of each frame */
|
---|
| 237 | void displayResults();
|
---|
| 238 |
|
---|
| 239 | /** Processes the profiler data after each frame */
|
---|
| 240 | void processFrameStats();
|
---|
| 241 |
|
---|
| 242 | /** Handles a change of the profiler's enabled state*/
|
---|
| 243 | void changeEnableState();
|
---|
| 244 |
|
---|
| 245 | /** An internal function to create the container which will hold our display elements*/
|
---|
| 246 | OverlayContainer* createContainer();
|
---|
| 247 |
|
---|
| 248 | /** An internal function to create a text area */
|
---|
| 249 | OverlayElement* createTextArea(const String& name, Real width, Real height, Real top, Real left,
|
---|
| 250 | uint fontSize, const String& caption, bool show = true);
|
---|
| 251 |
|
---|
| 252 | /** An internal function to create a panel */
|
---|
| 253 | OverlayElement* createPanel(const String& name, Real width, Real height, Real top, Real left,
|
---|
| 254 | const String& materialName, bool show = true);
|
---|
| 255 |
|
---|
| 256 | /// Represents an individual profile call
|
---|
| 257 | struct ProfileInstance {
|
---|
| 258 |
|
---|
| 259 | /// The name of the profile
|
---|
| 260 | String name;
|
---|
| 261 |
|
---|
| 262 | /// The name of the parent, empty string if root
|
---|
| 263 | String parent;
|
---|
| 264 |
|
---|
| 265 | /// The time this profile was started
|
---|
| 266 | ulong currTime;
|
---|
| 267 |
|
---|
| 268 | /// Represents the total time of all child profiles to subtract
|
---|
| 269 | /// from this profile
|
---|
| 270 | ulong accum;
|
---|
| 271 |
|
---|
| 272 | /// The hierarchical level of this profile, 0 being the root profile
|
---|
| 273 | uint hierarchicalLvl;
|
---|
| 274 | };
|
---|
| 275 |
|
---|
| 276 | /// Represents the total timing information of a profile
|
---|
| 277 | /// since profiles can be called more than once each frame
|
---|
| 278 | struct ProfileFrame {
|
---|
| 279 |
|
---|
| 280 | /// The name of the profile
|
---|
| 281 | String name;
|
---|
| 282 |
|
---|
| 283 | /// The total time this profile has taken this frame
|
---|
| 284 | ulong frameTime;
|
---|
| 285 |
|
---|
| 286 | /// The number of times this profile was called this frame
|
---|
| 287 | uint calls;
|
---|
| 288 |
|
---|
| 289 | /// The hierarchical level of this profile, 0 being the main loop
|
---|
| 290 | uint hierarchicalLvl;
|
---|
| 291 |
|
---|
| 292 | };
|
---|
| 293 |
|
---|
| 294 | /// Represents a history of each profile during the duration of the app
|
---|
| 295 | struct ProfileHistory {
|
---|
| 296 |
|
---|
| 297 | /// The name of the profile
|
---|
| 298 | String name;
|
---|
| 299 |
|
---|
| 300 | /// The current percentage of frame time this profile has taken
|
---|
| 301 | Real currentTime; // %
|
---|
| 302 |
|
---|
| 303 | /// The maximum percentage of frame time this profile has taken
|
---|
| 304 | Real maxTime; // %
|
---|
| 305 |
|
---|
| 306 | /// The minimum percentage of frame time this profile has taken
|
---|
| 307 | Real minTime; // %
|
---|
| 308 |
|
---|
| 309 | /// The number of times this profile has been called each frame
|
---|
| 310 | uint numCallsThisFrame;
|
---|
| 311 |
|
---|
| 312 | /// The total percentage of frame time this profile has taken
|
---|
| 313 | /// (used to calculate average)
|
---|
| 314 | Real totalTime; // %
|
---|
| 315 |
|
---|
| 316 | /// The total number of times this profile was called
|
---|
| 317 | /// (used to calculate average)
|
---|
| 318 | ulong totalCalls; // %
|
---|
| 319 |
|
---|
| 320 | /// The hierarchical level of this profile, 0 being the root profile
|
---|
| 321 | uint hierarchicalLvl;
|
---|
| 322 |
|
---|
| 323 | };
|
---|
| 324 |
|
---|
| 325 |
|
---|
| 326 | typedef std::list<ProfileInstance> ProfileStack;
|
---|
| 327 | typedef std::list<ProfileFrame> ProfileFrameList;
|
---|
| 328 | typedef std::list<ProfileHistory> ProfileHistoryList;
|
---|
| 329 | typedef std::map<String, ProfileHistoryList::iterator> ProfileHistoryMap;
|
---|
| 330 | typedef std::map<String, bool> DisabledProfileMap;
|
---|
| 331 |
|
---|
| 332 | typedef std::list<OverlayElement*> ProfileBarList;
|
---|
| 333 |
|
---|
| 334 | /// A stack for each individual profile per frame
|
---|
| 335 | ProfileStack mProfiles;
|
---|
| 336 |
|
---|
| 337 | /// Accumulates the results of each profile per frame (since a profile can be called
|
---|
| 338 | /// more than once a frame)
|
---|
| 339 | ProfileFrameList mProfileFrame;
|
---|
| 340 |
|
---|
| 341 | /// Keeps track of the statistics of each profile
|
---|
| 342 | ProfileHistoryList mProfileHistory;
|
---|
| 343 |
|
---|
| 344 | /// We use this for quick look-ups of profiles in the history list
|
---|
| 345 | ProfileHistoryMap mProfileHistoryMap;
|
---|
| 346 |
|
---|
| 347 | /// Holds the names of disabled profiles
|
---|
| 348 | DisabledProfileMap mDisabledProfiles;
|
---|
| 349 |
|
---|
| 350 | /// Holds the display bars for each profile results
|
---|
| 351 | ProfileBarList mProfileBars;
|
---|
| 352 |
|
---|
| 353 | /// Whether the GUI elements have been initialized
|
---|
| 354 | bool mInitialized;
|
---|
| 355 |
|
---|
| 356 | /// The max number of profiles we can display
|
---|
| 357 | uint maxProfiles;
|
---|
| 358 |
|
---|
| 359 | /// The overlay which contains our profiler results display
|
---|
| 360 | Overlay* mOverlay;
|
---|
| 361 |
|
---|
| 362 | /// The window that displays the profiler results
|
---|
| 363 | OverlayContainer* mProfileGui;
|
---|
| 364 |
|
---|
| 365 | /// The height of each bar
|
---|
| 366 | Real mBarHeight;
|
---|
| 367 |
|
---|
| 368 | /// The height of the stats window
|
---|
| 369 | Real mGuiHeight;
|
---|
| 370 |
|
---|
| 371 | /// The width of the stats window
|
---|
| 372 | Real mGuiWidth;
|
---|
| 373 |
|
---|
| 374 | /// The size of the indent for each profile display bar
|
---|
| 375 | Real mBarIndent;
|
---|
| 376 |
|
---|
| 377 | /// The width of the border between the profile window and each bar
|
---|
| 378 | Real mGuiBorderWidth;
|
---|
| 379 |
|
---|
| 380 | /// The width of the min, avg, and max lines in a profile display
|
---|
| 381 | Real mBarLineWidth;
|
---|
| 382 |
|
---|
| 383 | /// The number of frames that must elapse before the current
|
---|
| 384 | /// frame display is updated
|
---|
| 385 | uint mUpdateDisplayFrequency;
|
---|
| 386 |
|
---|
| 387 | /// The number of elasped frame, used with mUpdateDisplayFrequency
|
---|
| 388 | uint mCurrentFrame;
|
---|
| 389 |
|
---|
| 390 | /// The timer used for profiling
|
---|
| 391 | Timer* mTimer;
|
---|
| 392 |
|
---|
| 393 | /// The total time each frame takes
|
---|
| 394 | ulong mTotalFrameTime;
|
---|
| 395 |
|
---|
| 396 | /// Whether this profiler is enabled
|
---|
| 397 | bool mEnabled;
|
---|
| 398 |
|
---|
| 399 | /// Keeps track of whether this profiler has
|
---|
| 400 | /// received a request to be enabled/disabled
|
---|
| 401 | bool mEnableStateChangePending;
|
---|
| 402 |
|
---|
| 403 | /// Keeps track of the new enabled/disabled state that the user has requested
|
---|
| 404 | /// which will be applied after the frame ends
|
---|
| 405 | bool mNewEnableState;
|
---|
| 406 |
|
---|
| 407 | }; // end class
|
---|
| 408 |
|
---|
| 409 | } // end namespace
|
---|
| 410 |
|
---|
| 411 | #endif
|
---|