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
|
---|