/src/ogre/OgreMain/src/OgreProfiler.cpp
Line | Count | Source |
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 | | #include "OgreStableHeaders.h" |
29 | | /* |
30 | | |
31 | | Although the code is original, many of the ideas for the profiler were borrowed from |
32 | | "Real-Time In-Game Profiling" by Steve Rabin which can be found in Game Programming |
33 | | Gems 1. |
34 | | |
35 | | This code can easily be adapted to your own non-Ogre project. The only code that is |
36 | | Ogre-dependent is in the visualization/logging routines and the use of the Timer class. |
37 | | |
38 | | Enjoy! |
39 | | |
40 | | */ |
41 | | |
42 | | #include "OgreTimer.h" |
43 | | |
44 | | #ifdef USE_REMOTERY |
45 | | #include "Remotery.h" |
46 | | static Remotery* rmt; |
47 | | #endif |
48 | | |
49 | | namespace Ogre { |
50 | | //----------------------------------------------------------------------- |
51 | | // PROFILE DEFINITIONS |
52 | | //----------------------------------------------------------------------- |
53 | | template<> Profiler* Singleton<Profiler>::msSingleton = 0; |
54 | | Profiler* Profiler::getSingletonPtr(void) |
55 | 0 | { |
56 | 0 | return msSingleton; |
57 | 0 | } |
58 | | Profiler& Profiler::getSingleton(void) |
59 | 0 | { |
60 | 0 | assert( msSingleton ); return ( *msSingleton ); |
61 | 0 | } |
62 | | |
63 | | //----------------------------------------------------------------------- |
64 | | // PROFILER DEFINITIONS |
65 | | //----------------------------------------------------------------------- |
66 | | Profiler::Profiler() |
67 | 0 | : mCurrent(&mRoot) |
68 | 0 | , mLast(NULL) |
69 | 0 | , mRoot() |
70 | 0 | , mInitialized(false) |
71 | 0 | , mUpdateDisplayFrequency(10) |
72 | 0 | , mCurrentFrame(0) |
73 | 0 | , mTimer(0) |
74 | 0 | , mTotalFrameTime(0) |
75 | 0 | , mEnabled(false) |
76 | 0 | , mNewEnableState(false) |
77 | 0 | , mProfileMask(0xFFFFFFFF) |
78 | 0 | , mMaxTotalFrameTime(0) |
79 | 0 | , mAverageFrameTime(0) |
80 | 0 | , mResetExtents(false) |
81 | 0 | { |
82 | 0 | mRoot.hierarchicalLvl = 0 - 1; |
83 | |
|
84 | | #ifdef USE_REMOTERY |
85 | | rmt_Settings()->reuse_open_port = true; |
86 | | if(auto error = rmt_CreateGlobalInstance(&rmt)) |
87 | | { |
88 | | LogManager::getSingleton().logError("Could not launch Remotery - RMT_ERROR " + std::to_string(error)); |
89 | | return; |
90 | | } |
91 | | rmt_SetCurrentThreadName("Ogre Main"); |
92 | | #endif |
93 | 0 | } |
94 | | //----------------------------------------------------------------------- |
95 | | ProfileInstance::ProfileInstance(void) |
96 | 0 | : parent(NULL) |
97 | 0 | , frameNumber(0) |
98 | 0 | , accum(0) |
99 | 0 | , hierarchicalLvl(0) |
100 | 0 | { |
101 | 0 | history.numCallsThisFrame = 0; |
102 | 0 | history.totalTimePercent = 0; |
103 | 0 | history.totalTimeMillisecs = 0; |
104 | 0 | history.totalCalls = 0; |
105 | 0 | history.maxTimePercent = 0; |
106 | 0 | history.maxTimeMillisecs = 0; |
107 | 0 | history.minTimePercent = 1; |
108 | 0 | history.minTimeMillisecs = 100000; |
109 | 0 | history.currentTimePercent = 0; |
110 | 0 | history.currentTimeMillisecs = 0; |
111 | |
|
112 | 0 | frame.frameTime = 0; |
113 | 0 | frame.calls = 0; |
114 | 0 | } |
115 | | ProfileInstance::~ProfileInstance(void) |
116 | 0 | { |
117 | 0 | for(auto & it : children) |
118 | 0 | { |
119 | 0 | ProfileInstance* instance = it.second; |
120 | 0 | OGRE_DELETE instance; |
121 | 0 | } |
122 | 0 | children.clear(); |
123 | 0 | } |
124 | | //----------------------------------------------------------------------- |
125 | | Profiler::~Profiler() |
126 | 0 | { |
127 | | #ifdef USE_REMOTERY |
128 | | rmt_DestroyGlobalInstance(rmt); |
129 | | #else |
130 | 0 | if (!mRoot.children.empty()) |
131 | 0 | { |
132 | | // log the results of our profiling before we quit |
133 | 0 | logResults(); |
134 | 0 | } |
135 | | |
136 | | // clear all our lists |
137 | 0 | mDisabledProfiles.clear(); |
138 | 0 | #endif |
139 | 0 | } |
140 | | //----------------------------------------------------------------------- |
141 | | void Profiler::setTimer(Timer* t) |
142 | 0 | { |
143 | 0 | mTimer = t; |
144 | 0 | } |
145 | | //----------------------------------------------------------------------- |
146 | | Timer* Profiler::getTimer() |
147 | 0 | { |
148 | 0 | assert(mTimer && "Timer not set!"); |
149 | 0 | return mTimer; |
150 | 0 | } |
151 | | //----------------------------------------------------------------------- |
152 | | void Profiler::setEnabled(bool enabled) |
153 | 0 | { |
154 | 0 | if (!mInitialized && enabled) |
155 | 0 | { |
156 | 0 | for(auto & l : mListeners) |
157 | 0 | l->initializeSession(); |
158 | |
|
159 | 0 | mInitialized = true; |
160 | 0 | } |
161 | 0 | else if (mInitialized) |
162 | 0 | { |
163 | 0 | for(auto & l : mListeners) |
164 | 0 | l->finializeSession(); |
165 | |
|
166 | 0 | mInitialized = false; |
167 | 0 | mEnabled = false; |
168 | 0 | } |
169 | | // We store this enable/disable request until the frame ends |
170 | | // (don't want to screw up any open profiles!) |
171 | 0 | mNewEnableState = enabled; |
172 | 0 | } |
173 | | //----------------------------------------------------------------------- |
174 | | bool Profiler::getEnabled() const |
175 | 0 | { |
176 | 0 | return mEnabled; |
177 | 0 | } |
178 | | //----------------------------------------------------------------------- |
179 | | void Profiler::changeEnableState() |
180 | 0 | { |
181 | 0 | for(auto & l : mListeners) |
182 | 0 | l->changeEnableState(mNewEnableState); |
183 | |
|
184 | 0 | mEnabled = mNewEnableState; |
185 | 0 | } |
186 | | //----------------------------------------------------------------------- |
187 | | void Profiler::disableProfile(const String& profileName) |
188 | 0 | { |
189 | | // even if we are in the middle of this profile, endProfile() will still end it. |
190 | 0 | mDisabledProfiles.insert(profileName); |
191 | 0 | } |
192 | | //----------------------------------------------------------------------- |
193 | | void Profiler::enableProfile(const String& profileName) |
194 | 0 | { |
195 | 0 | mDisabledProfiles.erase(profileName); |
196 | 0 | } |
197 | | //----------------------------------------------------------------------- |
198 | | void Profiler::beginProfile(const String& profileName, uint32 groupID) |
199 | 0 | { |
200 | | #ifdef USE_REMOTERY |
201 | | // mask groups |
202 | | if ((groupID & mProfileMask) == 0) |
203 | | return; |
204 | | |
205 | | rmt_BeginCPUSampleDynamic(profileName.c_str(), RMTSF_Aggregate); |
206 | | #else |
207 | | // if the profiler is enabled |
208 | 0 | if (!mEnabled) |
209 | 0 | return; |
210 | | |
211 | | // mask groups |
212 | 0 | if ((groupID & mProfileMask) == 0) |
213 | 0 | return; |
214 | | |
215 | | // empty string is reserved for the root |
216 | | // not really fatal anymore, however one shouldn't name one's profile as an empty string anyway. |
217 | 0 | assert ((profileName != "") && ("Profile name can't be an empty string")); |
218 | | |
219 | | // we only process this profile if isn't disabled |
220 | 0 | if (mDisabledProfiles.find(profileName) != mDisabledProfiles.end()) |
221 | 0 | return; |
222 | | |
223 | | // regardless of whether or not we are enabled, we need the application's root profile (ie the first profile started each frame) |
224 | | // we need this so bogus profiles don't show up when users enable profiling mid frame |
225 | | // so we check |
226 | | |
227 | | // this would be an internal error. |
228 | 0 | assert (mCurrent); |
229 | | |
230 | | // need a timer to profile! |
231 | 0 | assert (mTimer && "Timer not set!"); |
232 | |
|
233 | 0 | ProfileInstance*& instance = mCurrent->children[profileName]; |
234 | 0 | if(instance) |
235 | 0 | { // found existing child. |
236 | | |
237 | | // Sanity check. |
238 | 0 | assert(instance->name == profileName); |
239 | |
|
240 | 0 | if(instance->frameNumber != mCurrentFrame) |
241 | 0 | { // new frame, reset stats |
242 | 0 | instance->frame.calls = 0; |
243 | 0 | instance->frame.frameTime = 0; |
244 | 0 | instance->frameNumber = mCurrentFrame; |
245 | 0 | } |
246 | 0 | } |
247 | 0 | else |
248 | 0 | { // new child! |
249 | 0 | instance = OGRE_NEW ProfileInstance(); |
250 | 0 | instance->name = profileName; |
251 | 0 | instance->parent = mCurrent; |
252 | 0 | instance->hierarchicalLvl = mCurrent->hierarchicalLvl + 1; |
253 | 0 | } |
254 | |
|
255 | 0 | instance->frameNumber = mCurrentFrame; |
256 | |
|
257 | 0 | mCurrent = instance; |
258 | | |
259 | | // we do this at the very end of the function to get the most |
260 | | // accurate timing results |
261 | 0 | mCurrent->currTime = mTimer->getMicroseconds(); |
262 | 0 | #endif |
263 | 0 | } |
264 | | //----------------------------------------------------------------------- |
265 | | void Profiler::endProfile(const String& profileName, uint32 groupID) |
266 | 0 | { |
267 | | #ifdef USE_REMOTERY |
268 | | // mask groups |
269 | | if ((groupID & mProfileMask) == 0) |
270 | | return; |
271 | | |
272 | | rmt_EndCPUSample(); |
273 | | #else |
274 | 0 | if(!mEnabled) |
275 | 0 | { |
276 | | // if the profiler received a request to be enabled or disabled |
277 | 0 | if(mNewEnableState != mEnabled) |
278 | 0 | { // note mNewEnableState == true to reach this. |
279 | 0 | changeEnableState(); |
280 | | |
281 | | // NOTE we will be in an 'error' state until the next begin. ie endProfile will likely get invoked using a profileName that was never started. |
282 | | // even then, we can't be sure that the next beginProfile will be the true start of a new frame |
283 | 0 | } |
284 | |
|
285 | 0 | return; |
286 | 0 | } |
287 | 0 | else |
288 | 0 | { |
289 | 0 | if(mNewEnableState != mEnabled) |
290 | 0 | { // note mNewEnableState == false to reach this. |
291 | 0 | changeEnableState(); |
292 | | |
293 | | // unwind the hierarchy, should be easy enough |
294 | 0 | mCurrent = &mRoot; |
295 | 0 | mLast = NULL; |
296 | 0 | } |
297 | |
|
298 | 0 | if(&mRoot == mCurrent && mLast) |
299 | 0 | { // profiler was enabled this frame, but the first subsequent beginProfile was NOT the beinging of a new frame as we had hoped. |
300 | | // we have bogus ProfileInstance in our hierarchy, we will need to remove it, then update the overlays so as not to confuse ze user |
301 | |
|
302 | 0 | for(auto& e : mRoot.children) |
303 | 0 | { |
304 | 0 | OGRE_DELETE e.second; |
305 | 0 | } |
306 | 0 | mRoot.children.clear(); |
307 | |
|
308 | 0 | mLast = NULL; |
309 | |
|
310 | 0 | processFrameStats(); |
311 | 0 | displayResults(); |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | 0 | if(&mRoot == mCurrent) |
316 | 0 | return; |
317 | | |
318 | | // mask groups |
319 | 0 | if ((groupID & mProfileMask) == 0) |
320 | 0 | return; |
321 | | |
322 | | // need a timer to profile! |
323 | 0 | assert (mTimer && "Timer not set!"); |
324 | | |
325 | | // get the end time of this profile |
326 | | // we do this as close the beginning of this function as possible |
327 | | // to get more accurate timing results |
328 | 0 | const ulong endTime = mTimer->getMicroseconds(); |
329 | | |
330 | | // empty string is reserved for designating an empty parent |
331 | 0 | assert ((profileName != "") && ("Profile name can't be an empty string")); |
332 | | |
333 | | // we only process this profile if isn't disabled |
334 | | // we check the current instance name against the provided profileName as a guard against disabling a profile name /after/ said profile began |
335 | 0 | if(mCurrent->name != profileName && mDisabledProfiles.find(profileName) != mDisabledProfiles.end()) |
336 | 0 | return; |
337 | | |
338 | | // calculate the elapsed time of this profile |
339 | 0 | const ulong timeElapsed = endTime - mCurrent->currTime; |
340 | | |
341 | | // update parent's accumulator if it isn't the root |
342 | 0 | if (&mRoot != mCurrent->parent) |
343 | 0 | { |
344 | | // add this profile's time to the parent's accumlator |
345 | 0 | mCurrent->parent->accum += timeElapsed; |
346 | 0 | } |
347 | |
|
348 | 0 | mCurrent->frame.frameTime += timeElapsed; |
349 | 0 | ++mCurrent->frame.calls; |
350 | |
|
351 | 0 | mLast = mCurrent; |
352 | 0 | mCurrent = mCurrent->parent; |
353 | |
|
354 | 0 | if (&mRoot == mCurrent) |
355 | 0 | { |
356 | | // the stack is empty and all the profiles have been completed |
357 | | // we have reached the end of the frame so process the frame statistics |
358 | | |
359 | | // we know that the time elapsed of the main loop is the total time the frame took |
360 | 0 | mTotalFrameTime = timeElapsed; |
361 | |
|
362 | 0 | if(timeElapsed > mMaxTotalFrameTime) |
363 | 0 | mMaxTotalFrameTime = timeElapsed; |
364 | | |
365 | | // we got all the information we need, so process the profiles |
366 | | // for this frame |
367 | 0 | processFrameStats(); |
368 | | |
369 | | // we display everything to the screen |
370 | 0 | displayResults(); |
371 | 0 | } |
372 | 0 | #endif |
373 | 0 | } |
374 | | //----------------------------------------------------------------------- |
375 | | void Profiler::processFrameStats(ProfileInstance* instance, Real& maxFrameTime) |
376 | 0 | { |
377 | | // calculate what percentage of frame time this profile took |
378 | 0 | const Real framePercentage = (Real) instance->frame.frameTime / (Real) mTotalFrameTime; |
379 | |
|
380 | 0 | const Real frameTimeMillisecs = (Real) instance->frame.frameTime / 1000.0f; |
381 | | |
382 | | // update the profile stats |
383 | 0 | instance->history.currentTimePercent = framePercentage; |
384 | 0 | instance->history.currentTimeMillisecs = frameTimeMillisecs; |
385 | 0 | if(mResetExtents) |
386 | 0 | { |
387 | 0 | instance->history.totalTimePercent = framePercentage; |
388 | 0 | instance->history.totalTimeMillisecs = frameTimeMillisecs; |
389 | 0 | instance->history.totalCalls = 1; |
390 | 0 | } |
391 | 0 | else |
392 | 0 | { |
393 | 0 | instance->history.totalTimePercent += framePercentage; |
394 | 0 | instance->history.totalTimeMillisecs += frameTimeMillisecs; |
395 | 0 | instance->history.totalCalls++; |
396 | 0 | } |
397 | 0 | instance->history.numCallsThisFrame = instance->frame.calls; |
398 | | |
399 | | // if we find a new minimum for this profile, update it |
400 | 0 | if (frameTimeMillisecs < instance->history.minTimeMillisecs || mResetExtents) |
401 | 0 | { |
402 | 0 | instance->history.minTimePercent = framePercentage; |
403 | 0 | instance->history.minTimeMillisecs = frameTimeMillisecs; |
404 | 0 | } |
405 | | |
406 | | // if we find a new maximum for this profile, update it |
407 | 0 | if (frameTimeMillisecs > instance->history.maxTimeMillisecs || mResetExtents) |
408 | 0 | { |
409 | 0 | instance->history.maxTimePercent = framePercentage; |
410 | 0 | instance->history.maxTimeMillisecs = frameTimeMillisecs; |
411 | 0 | } |
412 | |
|
413 | 0 | if (instance->frame.frameTime > maxFrameTime) |
414 | 0 | maxFrameTime = (Real)instance->frame.frameTime; |
415 | |
|
416 | 0 | for (auto& i : instance->children) |
417 | 0 | { |
418 | 0 | ProfileInstance* child = i.second; |
419 | | |
420 | | // we set the number of times each profile was called per frame to 0 |
421 | | // because not all profiles are called every frame |
422 | 0 | child->history.numCallsThisFrame = 0; |
423 | |
|
424 | 0 | if(child->frame.calls > 0) |
425 | 0 | { |
426 | 0 | processFrameStats(child, maxFrameTime); |
427 | 0 | } |
428 | 0 | } |
429 | 0 | } |
430 | | //----------------------------------------------------------------------- |
431 | | void Profiler::processFrameStats(void) |
432 | 0 | { |
433 | 0 | Real maxFrameTime = 0; |
434 | |
|
435 | 0 | for (auto& i : mRoot.children) |
436 | 0 | { |
437 | 0 | ProfileInstance* child = i.second; |
438 | | |
439 | | // we set the number of times each profile was called per frame to 0 |
440 | | // because not all profiles are called every frame |
441 | 0 | child->history.numCallsThisFrame = 0; |
442 | |
|
443 | 0 | if(child->frame.calls > 0) |
444 | 0 | { |
445 | 0 | processFrameStats(child, maxFrameTime); |
446 | 0 | } |
447 | 0 | } |
448 | | |
449 | | // Calculate whether the extents are now so out of date they need regenerating |
450 | 0 | if (mCurrentFrame == 0) |
451 | 0 | mAverageFrameTime = maxFrameTime; |
452 | 0 | else |
453 | 0 | mAverageFrameTime = (mAverageFrameTime + maxFrameTime) * 0.5f; |
454 | |
|
455 | 0 | if ((Real)mMaxTotalFrameTime > mAverageFrameTime * 4) |
456 | 0 | { |
457 | 0 | mResetExtents = true; |
458 | 0 | mMaxTotalFrameTime = (ulong)mAverageFrameTime; |
459 | 0 | } |
460 | 0 | else |
461 | 0 | mResetExtents = false; |
462 | 0 | } |
463 | | //----------------------------------------------------------------------- |
464 | | void Profiler::displayResults() |
465 | 0 | { |
466 | | // if its time to update the display |
467 | 0 | if (!(mCurrentFrame % mUpdateDisplayFrequency)) |
468 | 0 | { |
469 | | // ensure the root won't be culled |
470 | 0 | mRoot.frame.calls = 1; |
471 | |
|
472 | 0 | for(auto& l : mListeners) |
473 | 0 | l->displayResults(mRoot, mMaxTotalFrameTime); |
474 | 0 | } |
475 | 0 | ++mCurrentFrame; |
476 | 0 | } |
477 | | //----------------------------------------------------------------------- |
478 | | bool Profiler::watchForMax(const String& profileName) |
479 | 0 | { |
480 | 0 | assert ((profileName != "") && ("Profile name can't be an empty string")); |
481 | |
|
482 | 0 | return mRoot.watchForMax(profileName); |
483 | 0 | } |
484 | | //----------------------------------------------------------------------- |
485 | | bool ProfileInstance::watchForMax(const String& profileName) |
486 | 0 | { |
487 | 0 | for(auto& i : children) |
488 | 0 | { |
489 | 0 | ProfileInstance* child = i.second; |
490 | 0 | if( (child->name == profileName && child->watchForMax()) || child->watchForMax(profileName)) |
491 | 0 | return true; |
492 | 0 | } |
493 | 0 | return false; |
494 | 0 | } |
495 | | //----------------------------------------------------------------------- |
496 | | bool Profiler::watchForMin(const String& profileName) |
497 | 0 | { |
498 | 0 | assert ((profileName != "") && ("Profile name can't be an empty string")); |
499 | 0 | return mRoot.watchForMin(profileName); |
500 | 0 | } |
501 | | //----------------------------------------------------------------------- |
502 | | bool ProfileInstance::watchForMin(const String& profileName) |
503 | 0 | { |
504 | 0 | for(auto& i : children) |
505 | 0 | { |
506 | 0 | ProfileInstance* child = i.second; |
507 | 0 | if( (child->name == profileName && child->watchForMin()) || child->watchForMin(profileName)) |
508 | 0 | return true; |
509 | 0 | } |
510 | 0 | return false; |
511 | 0 | } |
512 | | //----------------------------------------------------------------------- |
513 | | bool Profiler::watchForLimit(const String& profileName, Real limit, bool greaterThan) |
514 | 0 | { |
515 | 0 | assert ((profileName != "") && ("Profile name can't be an empty string")); |
516 | 0 | return mRoot.watchForLimit(profileName, limit, greaterThan); |
517 | 0 | } |
518 | | //----------------------------------------------------------------------- |
519 | | bool ProfileInstance::watchForLimit(const String& profileName, Real limit, bool greaterThan) |
520 | 0 | { |
521 | 0 | for(auto& i : children) |
522 | 0 | { |
523 | 0 | ProfileInstance* child = i.second; |
524 | 0 | if( (child->name == profileName && child->watchForLimit(limit, greaterThan)) || child->watchForLimit(profileName, limit, greaterThan)) |
525 | 0 | return true; |
526 | 0 | } |
527 | 0 | return false; |
528 | 0 | } |
529 | | //----------------------------------------------------------------------- |
530 | | void Profiler::logResults() |
531 | 0 | { |
532 | 0 | LogManager::getSingleton().logMessage("----------------------Profiler Results----------------------"); |
533 | |
|
534 | 0 | for(auto& it : mRoot.children) |
535 | 0 | { |
536 | 0 | it.second->logResults(); |
537 | 0 | } |
538 | |
|
539 | 0 | LogManager::getSingleton().logMessage("------------------------------------------------------------"); |
540 | 0 | } |
541 | | //----------------------------------------------------------------------- |
542 | | void ProfileInstance::logResults() |
543 | 0 | { |
544 | 0 | LogManager::getSingleton().logMessage(StringUtil::format( |
545 | 0 | "%*s%s\t| Min %.2f | Max %.2f | Avg %.2f", hierarchicalLvl * 4, "", name.c_str(), history.minTimePercent, |
546 | 0 | history.maxTimePercent, history.totalTimePercent / history.totalCalls)); |
547 | |
|
548 | 0 | for(auto& it : children) |
549 | 0 | { |
550 | 0 | it.second->logResults(); |
551 | 0 | } |
552 | 0 | } |
553 | | //----------------------------------------------------------------------- |
554 | | void Profiler::reset() |
555 | 0 | { |
556 | 0 | mRoot.reset(); |
557 | 0 | mMaxTotalFrameTime = 0; |
558 | 0 | } |
559 | | //----------------------------------------------------------------------- |
560 | | void ProfileInstance::reset(void) |
561 | 0 | { |
562 | 0 | history.currentTimePercent = history.maxTimePercent = history.totalTimePercent = 0; |
563 | 0 | history.currentTimeMillisecs = history.maxTimeMillisecs = history.totalTimeMillisecs = 0; |
564 | 0 | history.numCallsThisFrame = history.totalCalls = 0; |
565 | |
|
566 | 0 | history.minTimePercent = 1; |
567 | 0 | history.minTimeMillisecs = 100000; |
568 | 0 | for(auto& it : children) |
569 | 0 | { |
570 | 0 | it.second->reset(); |
571 | 0 | } |
572 | 0 | } |
573 | | //----------------------------------------------------------------------- |
574 | | void Profiler::setUpdateDisplayFrequency(uint freq) |
575 | 0 | { |
576 | 0 | mUpdateDisplayFrequency = freq; |
577 | 0 | } |
578 | | //----------------------------------------------------------------------- |
579 | | uint Profiler::getUpdateDisplayFrequency() const |
580 | 0 | { |
581 | 0 | return mUpdateDisplayFrequency; |
582 | 0 | } |
583 | | //----------------------------------------------------------------------- |
584 | | void Profiler::addListener(ProfileSessionListener* listener) |
585 | 0 | { |
586 | 0 | if (std::find(mListeners.begin(), mListeners.end(), listener) == mListeners.end()) |
587 | 0 | mListeners.push_back(listener); |
588 | 0 | } |
589 | | //----------------------------------------------------------------------- |
590 | | void Profiler::removeListener(ProfileSessionListener* listener) |
591 | 0 | { |
592 | 0 | TProfileSessionListener::iterator i = std::find(mListeners.begin(), mListeners.end(), listener); |
593 | 0 | if (i != mListeners.end()) |
594 | 0 | mListeners.erase(i); |
595 | 0 | } |
596 | | //----------------------------------------------------------------------- |
597 | | } |