/src/ogre/OgreMain/include/OgreProfiler.h
Line | Count | Source (jump to first uncovered line) |
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-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | /* |
29 | | |
30 | | Although the code is original, many of the ideas for the profiler were borrowed from |
31 | | "Real-Time In-Game Profiling" by Steve Rabin which can be found in Game Programming |
32 | | Gems 1. |
33 | | |
34 | | This code can easily be adapted to your own non-Ogre project. The only code that is |
35 | | Ogre-dependent is in the visualization/logging routines and the use of the Timer class. |
36 | | |
37 | | Enjoy! |
38 | | |
39 | | */ |
40 | | |
41 | | #ifndef __Profiler_H__ |
42 | | #define __Profiler_H__ |
43 | | |
44 | | #include "OgrePrerequisites.h" |
45 | | #include "OgreSingleton.h" |
46 | | #include "OgreHeaderPrefix.h" |
47 | | |
48 | | #if OGRE_PROFILING == 1 |
49 | | # define OgreProfile( a ) Ogre::Profile _OgreProfileInstance( (a) ) |
50 | | # define OgreProfileBegin( a ) Ogre::Profiler::getSingleton().beginProfile( (a) ) |
51 | | # define OgreProfileEnd( a ) Ogre::Profiler::getSingleton().endProfile( (a) ) |
52 | | # define OgreProfileGroup( a, g ) Ogre::Profile OGRE_TOKEN_PASTE(_OgreProfileInstance, __LINE__) ( (a), (g) ) |
53 | | # define OgreProfileBeginGroup( a, g ) Ogre::Profiler::getSingleton().beginProfile( (a), (g) ) |
54 | | # define OgreProfileEndGroup( a, g ) Ogre::Profiler::getSingleton().endProfile( (a), (g) ) |
55 | | # define OgreProfileBeginGPUEvent( g ) Ogre::Root::getSingleton().getRenderSystem()->beginProfileEvent(g) |
56 | | # define OgreProfileEndGPUEvent( g ) Ogre::Root::getSingleton().getRenderSystem()->endProfileEvent() |
57 | | # define OgreProfileMarkGPUEvent( e ) Ogre::Root::getSingleton().getRenderSystem()->markProfileEvent(e) |
58 | | #else |
59 | | # define OgreProfile( a ) |
60 | | # define OgreProfileBegin( a ) |
61 | | # define OgreProfileEnd( a ) |
62 | | # define OgreProfileGroup( a, g ) |
63 | | # define OgreProfileBeginGroup( a, g ) |
64 | | # define OgreProfileEndGroup( a, g ) |
65 | | # define OgreProfileBeginGPUEvent( e ) |
66 | | # define OgreProfileEndGPUEvent( e ) |
67 | | # define OgreProfileMarkGPUEvent( e ) |
68 | | #endif |
69 | | |
70 | | namespace Ogre { |
71 | | /** \addtogroup Core |
72 | | * @{ |
73 | | */ |
74 | | /** \addtogroup General |
75 | | * @{ |
76 | | */ |
77 | | /** List of reserved profiling masks |
78 | | */ |
79 | | enum ProfileGroupMask |
80 | | { |
81 | | /// User default profile |
82 | | OGREPROF_USER_DEFAULT = 0x00000001, |
83 | | /// All in-built Ogre profiling will match this mask |
84 | | OGREPROF_ALL = 0xFF000000, |
85 | | /// General processing |
86 | | OGREPROF_GENERAL = 0x80000000, |
87 | | /// Culling |
88 | | OGREPROF_CULLING = 0x40000000, |
89 | | /// Rendering |
90 | | OGREPROF_RENDERING = 0x20000000 |
91 | | }; |
92 | | |
93 | | /** Represents the total timing information of a profile |
94 | | since profiles can be called more than once each frame |
95 | | */ |
96 | | struct ProfileFrame |
97 | | { |
98 | | |
99 | | /// The total time this profile has taken this frame |
100 | | ulong frameTime; |
101 | | |
102 | | /// The number of times this profile was called this frame |
103 | | uint calls; |
104 | | |
105 | | /// The hierarchical level of this profile, 0 being the main loop |
106 | | uint hierarchicalLvl; |
107 | | |
108 | | }; |
109 | | |
110 | | /// Represents a history of each profile during the duration of the app |
111 | | struct ProfileHistory |
112 | | { |
113 | | /// The current percentage of frame time this profile has taken |
114 | | Real currentTimePercent; |
115 | | /// The current frame time this profile has taken in milliseconds |
116 | | Real currentTimeMillisecs; |
117 | | |
118 | | /// The maximum percentage of frame time this profile has taken |
119 | | Real maxTimePercent; |
120 | | /// The maximum frame time this profile has taken in milliseconds |
121 | | Real maxTimeMillisecs; |
122 | | |
123 | | /// The minimum percentage of frame time this profile has taken |
124 | | Real minTimePercent; |
125 | | /// The minimum frame time this profile has taken in milliseconds |
126 | | Real minTimeMillisecs; |
127 | | |
128 | | /// The number of times this profile has been called each frame |
129 | | uint numCallsThisFrame; |
130 | | |
131 | | /// The total percentage of frame time this profile has taken |
132 | | Real totalTimePercent; |
133 | | /// The total frame time this profile has taken in milliseconds |
134 | | Real totalTimeMillisecs; |
135 | | |
136 | | /// The total number of times this profile was called |
137 | | /// (used to calculate average) |
138 | | ulong totalCalls; |
139 | | |
140 | | /// The hierarchical level of this profile, 0 being the root profile |
141 | | uint hierarchicalLvl; |
142 | | |
143 | | }; |
144 | | |
145 | | /// Represents an individual profile call |
146 | | class _OgreExport ProfileInstance : public ProfilerAlloc |
147 | | { |
148 | | friend class Profiler; |
149 | | public: |
150 | | ProfileInstance(void); |
151 | | virtual ~ProfileInstance(void); |
152 | | |
153 | | typedef std::map<String,ProfileInstance*> ProfileChildren; |
154 | | |
155 | | void logResults(); |
156 | | void reset(); |
157 | | |
158 | 0 | inline bool watchForMax(void) { return history.currentTimePercent == history.maxTimePercent; } |
159 | 0 | inline bool watchForMin(void) { return history.currentTimePercent == history.minTimePercent; } |
160 | | inline bool watchForLimit(Real limit, bool greaterThan = true) |
161 | 0 | { |
162 | 0 | if (greaterThan) |
163 | 0 | return history.currentTimePercent > limit; |
164 | 0 | else |
165 | 0 | return history.currentTimePercent < limit; |
166 | 0 | } |
167 | | |
168 | | bool watchForMax(const String& profileName); |
169 | | bool watchForMin(const String& profileName); |
170 | | bool watchForLimit(const String& profileName, Real limit, bool greaterThan = true); |
171 | | |
172 | | /// The name of the profile |
173 | | String name; |
174 | | |
175 | | /// The name of the parent, null if root |
176 | | ProfileInstance* parent; |
177 | | |
178 | | ProfileChildren children; |
179 | | |
180 | | ProfileFrame frame; |
181 | | ulong frameNumber; |
182 | | |
183 | | ProfileHistory history; |
184 | | |
185 | | /// The time this profile was started |
186 | | ulong currTime; |
187 | | |
188 | | /// Represents the total time of all child profiles to subtract |
189 | | /// from this profile |
190 | | ulong accum; |
191 | | |
192 | | /// The hierarchical level of this profile, 0 being the root profile |
193 | | uint hierarchicalLvl; |
194 | | }; |
195 | | |
196 | | /** ProfileSessionListener should be used to visualize profile results. |
197 | | Concrete impl. could be done using Overlay's but its not limited to |
198 | | them you can also create a custom listener which sends the profile |
199 | | informtaion over a network. |
200 | | */ |
201 | | class _OgreExport ProfileSessionListener |
202 | | { |
203 | | public: |
204 | 0 | virtual ~ProfileSessionListener() {} |
205 | | |
206 | | /// Create the internal resources |
207 | | virtual void initializeSession() = 0; |
208 | | |
209 | | /// All internal resources should be deleted here |
210 | | virtual void finializeSession() = 0; |
211 | | |
212 | | /** If the profiler disables this listener then it |
213 | | should hide its panels (if any exists) or stop |
214 | | sending data over the network |
215 | | */ |
216 | 0 | virtual void changeEnableState(bool enabled) {}; |
217 | | |
218 | | /// Here we get the real profiling information which we can use |
219 | 0 | virtual void displayResults(const ProfileInstance& instance, ulong maxTotalFrameTime) {}; |
220 | | }; |
221 | | |
222 | | /** The profiler allows you to measure the performance of your code |
223 | | |
224 | | Do not create profiles directly from this unless you want a profile to last |
225 | | outside of its scope (i.e. the main game loop). For most cases, use the macro |
226 | | OgreProfile(name) and braces to limit the scope. You must enable the Profile |
227 | | before you can used it with setEnabled(true). If you want to disable profiling |
228 | | in Ogre, simply set the macro OGRE_PROFILING to 0. |
229 | | @author Amit Mathew (amitmathew (at) yahoo (dot) com) |
230 | | @todo resolve artificial cap on number of profiles displayed |
231 | | @todo fix display ordering of profiles not called every frame |
232 | | */ |
233 | | class _OgreExport Profiler : |
234 | | public Singleton<Profiler>, |
235 | | public ProfilerAlloc |
236 | | { |
237 | | public: |
238 | | Profiler(); |
239 | | ~Profiler(); |
240 | | |
241 | | /** Sets the timer for the profiler */ |
242 | | void setTimer(Timer* t); |
243 | | |
244 | | /** Retrieves the timer for the profiler */ |
245 | | Timer* getTimer(); |
246 | | |
247 | | /** Begins a profile |
248 | | |
249 | | Use the macro OgreProfileBegin(name) instead of calling this directly |
250 | | so that profiling can be ignored in the release version of your app. |
251 | | |
252 | | You only use the macro (or this) if you want a profile to last outside |
253 | | of its scope (i.e. the main game loop). If you use this function, make sure you |
254 | | use a corresponding OgreProfileEnd(name). Usually you would use the macro |
255 | | OgreProfile(name). This function will be ignored for a profile that has been |
256 | | disabled or if the profiler is disabled. |
257 | | @param profileName Must be unique and must not be an empty string |
258 | | @param groupID A profile group identifier, which can allow you to mask profiles |
259 | | */ |
260 | | void beginProfile(const String& profileName, uint32 groupID = (uint32)OGREPROF_USER_DEFAULT); |
261 | | |
262 | | /** Ends a profile |
263 | | |
264 | | Use the macro OgreProfileEnd(name) instead of calling this directly so that |
265 | | profiling can be ignored in the release version of your app. |
266 | | |
267 | | This function is usually not called directly unless you want a profile to |
268 | | last outside of its scope. In most cases, using the macro OgreProfile(name) |
269 | | which will call this function automatically when it goes out of scope. Make |
270 | | sure the name of this profile matches its corresponding beginProfile name. |
271 | | This function will be ignored for a profile that has been disabled or if the |
272 | | profiler is disabled. |
273 | | @param profileName Must be unique and must not be an empty string |
274 | | @param groupID A profile group identifier, which can allow you to mask profiles |
275 | | */ |
276 | | void endProfile(const String& profileName, uint32 groupID = (uint32)OGREPROF_USER_DEFAULT); |
277 | | |
278 | | /** Sets whether this profiler is enabled. Only takes effect after the |
279 | | the frame has ended. |
280 | | @remarks When this is called the first time with the parameter true, |
281 | | it initializes the GUI for the Profiler |
282 | | */ |
283 | | void setEnabled(bool enabled); |
284 | | |
285 | | /** Gets whether this profiler is enabled */ |
286 | | bool getEnabled() const; |
287 | | |
288 | | /** Enables a previously disabled profile |
289 | | @remarks Can be safely called in the middle of the profile. |
290 | | */ |
291 | | void enableProfile(const String& profileName); |
292 | | |
293 | | /** Disables a profile |
294 | | @remarks Can be safely called in the middle of the profile. |
295 | | */ |
296 | | void disableProfile(const String& profileName); |
297 | | |
298 | | /** Set the mask which all profiles must pass to be enabled. |
299 | | */ |
300 | 0 | void setProfileGroupMask(uint32 mask) { mProfileMask = mask; } |
301 | | /** Get the mask which all profiles must pass to be enabled. |
302 | | */ |
303 | 0 | uint32 getProfileGroupMask() const { return mProfileMask; } |
304 | | |
305 | | /** Returns true if the specified profile reaches a new frame time maximum |
306 | | @remarks If this is called during a frame, it will be reading the results |
307 | | from the previous frame. Therefore, it is best to use this after the frame |
308 | | has ended. |
309 | | */ |
310 | | bool watchForMax(const String& profileName); |
311 | | |
312 | | /** Returns true if the specified profile reaches a new frame time minimum |
313 | | @remarks If this is called during a frame, it will be reading the results |
314 | | from the previous frame. Therefore, it is best to use this after the frame |
315 | | has ended. |
316 | | */ |
317 | | bool watchForMin(const String& profileName); |
318 | | |
319 | | /** Returns true if the specified profile goes over or under the given limit |
320 | | frame time |
321 | | @remarks If this is called during a frame, it will be reading the results |
322 | | from the previous frame. Therefore, it is best to use this after the frame |
323 | | has ended. |
324 | | @param profileName Must be unique and must not be an empty string |
325 | | @param limit A number between 0 and 1 representing the percentage of frame time |
326 | | @param greaterThan If true, this will return whether the limit is exceeded. Otherwise, |
327 | | it will return if the frame time has gone under this limit. |
328 | | */ |
329 | | bool watchForLimit(const String& profileName, Real limit, bool greaterThan = true); |
330 | | |
331 | | /** Outputs current profile statistics to the log */ |
332 | | void logResults(); |
333 | | |
334 | | /** Clears the profiler statistics */ |
335 | | void reset(); |
336 | | |
337 | | /** Sets the Profiler so the display of results are updated every n frames*/ |
338 | | void setUpdateDisplayFrequency(uint freq); |
339 | | |
340 | | /** Gets the frequency that the Profiler display is updated */ |
341 | | uint getUpdateDisplayFrequency() const; |
342 | | |
343 | | /** |
344 | | |
345 | | Register a ProfileSessionListener from the Profiler |
346 | | @param listener |
347 | | A valid listener derived class |
348 | | */ |
349 | | void addListener(ProfileSessionListener* listener); |
350 | | |
351 | | /** |
352 | | |
353 | | Unregister a ProfileSessionListener from the Profiler |
354 | | @param listener |
355 | | A valid listener derived class |
356 | | */ |
357 | | void removeListener(ProfileSessionListener* listener); |
358 | | |
359 | | /// @copydoc Singleton::getSingleton() |
360 | | static Profiler& getSingleton(void); |
361 | | /// @copydoc Singleton::getSingleton() |
362 | | static Profiler* getSingletonPtr(void); |
363 | | |
364 | | private: |
365 | | friend class ProfileInstance; |
366 | | |
367 | | typedef std::vector<ProfileSessionListener*> TProfileSessionListener; |
368 | | TProfileSessionListener mListeners; |
369 | | |
370 | | /** Initializes the profiler's GUI elements */ |
371 | | void initialize(); |
372 | | |
373 | | void displayResults(); |
374 | | |
375 | | /** Processes frame stats for all of the mRoot's children */ |
376 | | void processFrameStats(void); |
377 | | /** Processes specific ProfileInstance and it's children recursively.*/ |
378 | | void processFrameStats(ProfileInstance* instance, Real& maxFrameTime); |
379 | | |
380 | | /** Handles a change of the profiler's enabled state*/ |
381 | | void changeEnableState(); |
382 | | |
383 | | // lol. Uses typedef; put's original container type in name. |
384 | | typedef std::set<String> DisabledProfileMap; |
385 | | typedef ProfileInstance::ProfileChildren ProfileChildren; |
386 | | |
387 | | ProfileInstance* mCurrent; |
388 | | ProfileInstance* mLast; |
389 | | ProfileInstance mRoot; |
390 | | |
391 | | /// Holds the names of disabled profiles |
392 | | DisabledProfileMap mDisabledProfiles; |
393 | | |
394 | | /// Whether the GUI elements have been initialized |
395 | | bool mInitialized; |
396 | | |
397 | | /// The number of frames that must elapse before the current |
398 | | /// frame display is updated |
399 | | uint mUpdateDisplayFrequency; |
400 | | |
401 | | /// The number of elapsed frame, used with mUpdateDisplayFrequency |
402 | | uint mCurrentFrame; |
403 | | |
404 | | /// The timer used for profiling |
405 | | Timer* mTimer; |
406 | | |
407 | | /// The total time each frame takes |
408 | | ulong mTotalFrameTime; |
409 | | |
410 | | /// Whether this profiler is enabled |
411 | | bool mEnabled; |
412 | | |
413 | | /// Keeps track of the new enabled/disabled state that the user has requested |
414 | | /// which will be applied after the frame ends |
415 | | bool mNewEnableState; |
416 | | |
417 | | /// Mask to decide whether a type of profile is enabled or not |
418 | | uint32 mProfileMask; |
419 | | |
420 | | /// The max frame time recorded |
421 | | ulong mMaxTotalFrameTime; |
422 | | |
423 | | /// Rolling average of millisecs |
424 | | Real mAverageFrameTime; |
425 | | bool mResetExtents; |
426 | | |
427 | | |
428 | | }; // end class |
429 | | |
430 | | /** An individual profile that will be processed by the Profiler |
431 | | |
432 | | Use the macro OgreProfile(name) instead of instantiating this profile directly |
433 | | |
434 | | We use this Profile to allow scoping rules to signify the beginning and end of |
435 | | the profile. Use the Profiler singleton (through the macro OgreProfileBegin(name) |
436 | | and OgreProfileEnd(name)) directly if you want a profile to last |
437 | | outside of a scope (i.e. the main game loop). |
438 | | @author Amit Mathew (amitmathew (at) yahoo (dot) com) |
439 | | */ |
440 | | class Profile : public ProfilerAlloc |
441 | | { |
442 | | |
443 | | public: |
444 | | Profile(const String& profileName, uint32 groupID = (uint32)OGREPROF_USER_DEFAULT) |
445 | | : mName(profileName), mGroupID(groupID) |
446 | 0 | { |
447 | 0 | Profiler::getSingleton().beginProfile(profileName, groupID); |
448 | 0 | } |
449 | 0 | ~Profile() { Profiler::getSingleton().endProfile(mName, mGroupID); } |
450 | | |
451 | | private: |
452 | | /// The name of this profile |
453 | | String mName; |
454 | | /// The group ID |
455 | | uint32 mGroupID; |
456 | | }; |
457 | | /** @} */ |
458 | | /** @} */ |
459 | | |
460 | | } // end namespace |
461 | | |
462 | | #include "OgreHeaderSuffix.h" |
463 | | |
464 | | #endif |