PerfTimer.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: PerfTimer.cpp ///////////////////////////////////////////////////////////////////////////
  24. // Author:
  25. ///////////////////////////////////////////////////////////////////////////////////////////////////
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "Common/PerfTimer.h"
  28. #include "Common/GlobalData.h"
  29. #include "GameClient/DebugDisplay.h"
  30. #include "GameClient/Display.h"
  31. #include "GameClient/GraphDraw.h"
  32. __forceinline void ProfileGetTime(__int64 &t)
  33. {
  34. _asm
  35. {
  36. mov ecx,[t]
  37. push eax
  38. push edx
  39. rdtsc
  40. mov [ecx],eax
  41. mov [ecx+4],edx
  42. pop edx
  43. pop eax
  44. };
  45. }
  46. #ifdef _INTERNAL
  47. // for occasional debugging...
  48. //#pragma optimize("", off)
  49. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  50. #endif
  51. //-------------------------------------------------------------------------------------------------
  52. //-------------------------------------------------------------------------------------------------
  53. //-------------------------------------------------------------------------------------------------
  54. #if defined(PERF_TIMERS) || defined(DUMP_PERF_STATS)
  55. //-------------------------------------------------------------------------------------------------
  56. static Int64 s_ticksPerSec = 0;
  57. static double s_ticksPerMSec = 0;
  58. static double s_ticksPerUSec = 0;
  59. //-------------------------------------------------------------------------------------------------
  60. void GetPrecisionTimerTicksPerSec(Int64* t)
  61. {
  62. *t = s_ticksPerSec;
  63. }
  64. //Kris: Plugged in Martin's code to optimize timer setup.
  65. #define HOFFESOMMER_REPLACEMENT_CODE
  66. //-------------------------------------------------------------------------------------------------
  67. void InitPrecisionTimer()
  68. {
  69. #ifdef HOFFESOMMER_REPLACEMENT_CODE
  70. // measure clock cycles 3 times for 20 msec each
  71. // then take the 2 counts that are closest, average
  72. _int64 n[ 3 ];
  73. for( int k = 0; k < 3; k++ )
  74. {
  75. // wait for end of current tick
  76. unsigned timeEnd = timeGetTime() + 2;
  77. while( timeGetTime() < timeEnd ); //do nothing
  78. // get cycles
  79. _int64 start, startQPC, endQPC;
  80. QueryPerformanceCounter( (LARGE_INTEGER *)&startQPC );
  81. ProfileGetTime( start );
  82. timeEnd += 20;
  83. while( timeGetTime() < timeEnd ); //do nothing
  84. ProfileGetTime( n[ k ] );
  85. n[ k ] -= start;
  86. // convert to 1 second
  87. if( QueryPerformanceCounter( (LARGE_INTEGER*)&endQPC ) )
  88. {
  89. QueryPerformanceFrequency( (LARGE_INTEGER*)&s_ticksPerSec );
  90. n[ k ] = ( n[ k ] * s_ticksPerSec ) / ( endQPC - startQPC );
  91. }
  92. else
  93. {
  94. n[ k ] = ( n[ k ] * 1000 ) / 20;
  95. }
  96. }
  97. // find two closest values
  98. _int64 d01 = n[ 1 ] - n[ 0 ];
  99. _int64 d02 = n[ 2 ] - n[ 0 ];
  100. _int64 d12 = n[ 2 ] - n[ 1 ];
  101. if( d01 < 0 )
  102. {
  103. d01 = -d01;
  104. }
  105. if( d02 < 0 )
  106. {
  107. d02 = -d02;
  108. }
  109. if( d12 < 0 )
  110. {
  111. d12 = -d12;
  112. }
  113. _int64 avg;
  114. if( d01 < d02 )
  115. {
  116. avg = d01 < d12 ? n[ 0 ] + n[ 1 ] : n[ 1 ] + n[ 2 ];
  117. }
  118. else
  119. {
  120. avg = d02 < d12 ? n[ 0 ] + n[ 2 ] : n[ 1 ] + n[ 2 ];
  121. }
  122. //s_ticksPerMSec = 1.0 * TotalTicks / totalTime;
  123. s_ticksPerMSec = avg / 2000.0f;
  124. s_ticksPerSec = s_ticksPerMSec * 1000.0f;
  125. s_ticksPerUSec = s_ticksPerSec / 1000000.0f;
  126. #else
  127. //Kris: With total disrespect, this code costs 5 real seconds of init time
  128. //whenever we fire up the game.
  129. #ifdef USE_QPF
  130. QueryPerformanceFrequency((LARGE_INTEGER*)&s_ticksPerSec);
  131. #else
  132. // Init the precision timers
  133. Int64 totalTime = 0;
  134. Int64 TotalTicks = 0;
  135. static int TESTS = 5;
  136. for (int i = 0; i < TESTS; ++i)
  137. {
  138. int TimeStart;
  139. int TimeStop;
  140. Int64 StartTicks;
  141. Int64 EndTicks;
  142. TimeStart = timeGetTime();
  143. GetPrecisionTimer(&StartTicks);
  144. for(;;)
  145. {
  146. TimeStop = timeGetTime();
  147. if ((TimeStop - TimeStart) > 1000)
  148. {
  149. GetPrecisionTimer(&EndTicks);
  150. break;
  151. }
  152. }
  153. TotalTicks += (EndTicks - StartTicks);
  154. totalTime += (TimeStop - TimeStart);
  155. }
  156. s_ticksPerMSec = 1.0 * TotalTicks / totalTime;
  157. s_ticksPerSec = s_ticksPerMSec * 1000.0f;
  158. #endif
  159. s_ticksPerMSec = s_ticksPerSec / 1000.0f;
  160. s_ticksPerUSec = s_ticksPerSec / 1000000.0f;
  161. #ifdef NOT_IN_USE
  162. Int64 bogus[8];
  163. GetPrecisionTimer(&start);
  164. for (Int ii = 0; ii < ITERS; ++ii)
  165. {
  166. GetPrecisionTimer(&bogus[0]);
  167. GetPrecisionTimer(&bogus[1]);
  168. GetPrecisionTimer(&bogus[2]);
  169. GetPrecisionTimer(&bogus[3]);
  170. GetPrecisionTimer(&bogus[4]);
  171. GetPrecisionTimer(&bogus[5]);
  172. GetPrecisionTimer(&bogus[6]);
  173. GetPrecisionTimer(&bogus[7]);
  174. }
  175. TheTicksToGetTicks = (bogus[7] - start) / (ITERS*8);
  176. DEBUG_LOG(("TheTicksToGetTicks is %d (%f usec)\n",(int)TheTicksToGetTicks,TheTicksToGetTicks/s_ticksPerUSec));
  177. #endif
  178. #endif
  179. }
  180. #endif // defined(PERF_TIMERS) || defined(DUMP_PERF_STATS)
  181. //-------------------------------------------------------------------------------------------------
  182. //-------------------------------------------------------------------------------------------------
  183. //-------------------------------------------------------------------------------------------------
  184. #ifdef PERF_TIMERS
  185. //-------------------------------------------------------------------------------------------------
  186. //-------------------------------------------------------------------------------------------------
  187. //-------------------------------------------------------------------------------------------------
  188. /*static*/ Bool AutoPerfGatherIgnore::s_ignoring = false;
  189. //-------------------------------------------------------------------------------------------------
  190. typedef std::vector< std::pair< AsciiString, AsciiString > > StringPairVec;
  191. //-------------------------------------------------------------------------------------------------
  192. // PerfMetrics class. Basically, request a handle with your name and it will return. We use a vector
  193. // of pairs of asciistrings for this
  194. class PerfMetricsOutput
  195. {
  196. private:
  197. StringPairVec m_outputStats;
  198. public:
  199. AsciiString& getStatsString(const AsciiString& id)
  200. {
  201. for (int i = 0; i < m_outputStats.size(); ++i)
  202. {
  203. if (m_outputStats[i].first == id)
  204. return m_outputStats[i].second;
  205. }
  206. std::pair<AsciiString, AsciiString> newPair;
  207. newPair.first = id;
  208. m_outputStats.push_back(newPair);
  209. return m_outputStats.back().second;
  210. }
  211. void clearStatsString(const AsciiString& id)
  212. {
  213. for (int i = 0; i < m_outputStats.size(); ++i)
  214. {
  215. if (m_outputStats[i].first == id)
  216. {
  217. m_outputStats.erase(&m_outputStats[i]);
  218. return;
  219. }
  220. }
  221. }
  222. StringPairVec& friend_getAllStatsStrings() { return m_outputStats; }
  223. };
  224. //-------------------------------------------------------------------------------------------------
  225. static PerfMetricsOutput s_output;
  226. static FILE* s_perfStatsFile = NULL;
  227. static Int s_perfDumpOptions = 0;
  228. static UnsignedInt s_lastDumpedFrame = 0;
  229. static char s_buf[256] = "";
  230. PerfGather* PerfGather::m_active[MAX_ACTIVE_STACK] = { 0 };
  231. PerfGather** PerfGather::m_activeHead = &PerfGather::m_active[0];
  232. Int64 PerfGather::s_stopStartOverhead = -1;
  233. //-------------------------------------------------------------------------------------------------
  234. //-------------------------------------------------------------------------------------------------
  235. //-------------------------------------------------------------------------------------------------
  236. //-------------------------------------------------------------------------------------------------
  237. /*static*/ PerfGather*& PerfGather::getHeadPtr()
  238. {
  239. // funky technique for order-of-init problem. trust me. (srj)
  240. static PerfGather* s_head = NULL;
  241. return s_head;
  242. }
  243. //-------------------------------------------------------------------------------------------------
  244. void PerfGather::addToList()
  245. {
  246. PerfGather*& head = getHeadPtr();
  247. this->m_next = head;
  248. if (head)
  249. head->m_prev = this;
  250. head = this;
  251. }
  252. //-------------------------------------------------------------------------------------------------
  253. void PerfGather::removeFromList()
  254. {
  255. PerfGather*& head = getHeadPtr();
  256. if (this->m_next)
  257. this->m_next->m_prev = this->m_prev;
  258. if (this->m_prev)
  259. this->m_prev->m_next = this->m_next;
  260. else
  261. head = this->m_next;
  262. this->m_prev = 0;
  263. this->m_next = 0;
  264. }
  265. //-------------------------------------------------------------------------------------------------
  266. PerfGather::PerfGather(const char *identifier) :
  267. m_identifier(identifier),
  268. m_startTime(0),
  269. m_runningTimeGross(0),
  270. m_runningTimeNet(0),
  271. m_callCount(0),
  272. m_next(0),
  273. m_prev(0)
  274. {
  275. //Added By Sadullah Nader
  276. //Initializations inserted
  277. m_ignore = FALSE;
  278. //
  279. DEBUG_ASSERTCRASH(strchr(m_identifier, ',') == NULL, ("PerfGather names must not contain commas"));
  280. addToList();
  281. }
  282. //-------------------------------------------------------------------------------------------------
  283. PerfGather::~PerfGather()
  284. {
  285. removeFromList();
  286. }
  287. //-------------------------------------------------------------------------------------------------
  288. void PerfGather::reset()
  289. {
  290. m_startTime = 0;
  291. m_runningTimeGross = 0;
  292. m_runningTimeNet = 0;
  293. m_callCount = 0;
  294. }
  295. //-------------------------------------------------------------------------------------------------
  296. /*static*/ void PerfGather::resetAll()
  297. {
  298. for (PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
  299. {
  300. head->reset();
  301. }
  302. }
  303. //-------------------------------------------------------------------------------------------------
  304. /*static*/ void PerfGather::initPerfDump(const char* fname, Int options)
  305. {
  306. PerfGather::termPerfDump();
  307. strcpy(s_buf, fname);
  308. char tmp[256];
  309. strcpy(tmp, s_buf);
  310. strcat(tmp, ".csv");
  311. s_perfStatsFile = fopen(tmp, "w");
  312. s_perfDumpOptions = options;
  313. if (s_perfStatsFile == NULL)
  314. {
  315. DEBUG_CRASH(("could not open/create perf file %s -- is it open in another app?",s_buf));
  316. return;
  317. }
  318. if (s_stopStartOverhead == -1)
  319. {
  320. const Int ITERS = 100000;
  321. Int64 start, end;
  322. PerfGather pf("timer");
  323. GetPrecisionTimer(&start);
  324. for (Int ii = 0; ii < ITERS; ++ii)
  325. {
  326. pf.startTimer(); pf.stopTimer();
  327. pf.startTimer(); pf.stopTimer();
  328. pf.startTimer(); pf.stopTimer();
  329. pf.startTimer(); pf.stopTimer();
  330. pf.startTimer(); pf.stopTimer();
  331. pf.startTimer(); pf.stopTimer();
  332. pf.startTimer(); pf.stopTimer();
  333. pf.startTimer(); pf.stopTimer();
  334. }
  335. GetPrecisionTimer(&end);
  336. s_stopStartOverhead = (end - start) / (ITERS*8);
  337. DEBUG_LOG(("s_stopStartOverhead is %d (%f usec)\n",(int)s_stopStartOverhead,s_stopStartOverhead/s_ticksPerUSec));
  338. }
  339. }
  340. //-------------------------------------------------------------------------------------------------
  341. /*static*/ void PerfGather::dumpAll(UnsignedInt frame)
  342. {
  343. if (frame < s_lastDumpedFrame)
  344. {
  345. // must have reset or started a new game.
  346. termPerfDump();
  347. initPerfDump(s_buf, s_perfDumpOptions);
  348. }
  349. if (!s_perfStatsFile)
  350. {
  351. DEBUG_CRASH(("not inited"));
  352. return;
  353. }
  354. if (frame >= 1 && frame <= 30)
  355. {
  356. // always skip the first second or so, since it loads everything and skews the results horribly
  357. }
  358. else
  359. {
  360. if (s_lastDumpedFrame == 0)
  361. {
  362. fprintf(s_perfStatsFile, "Frame");
  363. if (s_perfDumpOptions & PERF_GROSSTIME)
  364. {
  365. for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
  366. {
  367. fprintf(s_perfStatsFile, ",Gross:%s", head->m_identifier);
  368. }
  369. }
  370. if (s_perfDumpOptions & PERF_NETTIME)
  371. {
  372. for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
  373. {
  374. fprintf(s_perfStatsFile, ",Net:%s", head->m_identifier);
  375. }
  376. }
  377. if (s_perfDumpOptions & PERF_CALLCOUNT)
  378. {
  379. for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
  380. {
  381. fprintf(s_perfStatsFile, ",Count:%s", head->m_identifier);
  382. }
  383. }
  384. fprintf(s_perfStatsFile, "\n");
  385. }
  386. // a strange value so we can find it in the dump, if necessary.
  387. // there's nothing magic about this value, it's purely determined from sample dumps...
  388. // const Real CLIP_BIG_SPIKES = 1e10f;
  389. const Real CLIP_BIG_SPIKES = 100000.0f;
  390. // make this a nonnumeric thing so Excel won't try to graph it...
  391. fprintf(s_perfStatsFile, "Frame%08d", frame);
  392. if (s_perfDumpOptions & PERF_GROSSTIME)
  393. {
  394. for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
  395. {
  396. double t = head->m_runningTimeGross;
  397. t /= s_ticksPerUSec;
  398. if (t > CLIP_BIG_SPIKES)
  399. t = CLIP_BIG_SPIKES;
  400. fprintf(s_perfStatsFile, ",%f", t);
  401. }
  402. }
  403. if (s_perfDumpOptions & PERF_NETTIME)
  404. {
  405. for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
  406. {
  407. double t = head->m_runningTimeNet;
  408. t /= s_ticksPerUSec;
  409. if (t > CLIP_BIG_SPIKES)
  410. t = CLIP_BIG_SPIKES;
  411. fprintf(s_perfStatsFile, ",%f", t);
  412. }
  413. }
  414. if (s_perfDumpOptions & PERF_CALLCOUNT)
  415. {
  416. for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
  417. {
  418. fprintf(s_perfStatsFile, ",%d", head->m_callCount);
  419. }
  420. }
  421. fprintf(s_perfStatsFile, "\n");
  422. fflush(s_perfStatsFile);
  423. s_lastDumpedFrame = frame;
  424. }
  425. }
  426. //-------------------------------------------------------------------------------------------------
  427. // This function will queue up stuff to draw on the next frame. We also need to adjust the
  428. // perf timers to not include time spent paused by the script engine.
  429. /*static*/ void PerfGather::displayGraph(UnsignedInt frame)
  430. {
  431. if (!TheGraphDraw) {
  432. return;
  433. }
  434. if (frame >= 1 && frame <= 30)
  435. {
  436. // always skip the first second or so, since it loads everything and skews the results horribly
  437. }
  438. else
  439. {
  440. const Real CLIP_BIG_SPIKES = 100000.0f;
  441. if (s_perfDumpOptions & PERF_GROSSTIME)
  442. {
  443. for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
  444. {
  445. Real t = head->m_runningTimeGross;
  446. t /= s_ticksPerUSec;
  447. if (t > CLIP_BIG_SPIKES)
  448. t = CLIP_BIG_SPIKES;
  449. TheGraphDraw->addEntry(head->m_identifier, REAL_TO_INT(t));
  450. }
  451. }
  452. if (s_perfDumpOptions & PERF_NETTIME)
  453. {
  454. for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
  455. {
  456. Real t = head->m_runningTimeNet;
  457. t /= s_ticksPerUSec;
  458. if (t > CLIP_BIG_SPIKES)
  459. t = CLIP_BIG_SPIKES;
  460. TheGraphDraw->addEntry(head->m_identifier, REAL_TO_INT(t));
  461. }
  462. }
  463. if (s_perfDumpOptions & PERF_CALLCOUNT)
  464. {
  465. for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
  466. {
  467. Real t = head->m_callCount;
  468. TheGraphDraw->addEntry(head->m_identifier, REAL_TO_INT(t));
  469. }
  470. }
  471. }
  472. }
  473. //-------------------------------------------------------------------------------------------------
  474. /*static*/ void PerfGather::termPerfDump()
  475. {
  476. if (s_perfStatsFile)
  477. {
  478. fflush(s_perfStatsFile);
  479. fclose(s_perfStatsFile);
  480. s_perfStatsFile = NULL;
  481. }
  482. s_lastDumpedFrame = 0;
  483. }
  484. //-------------------------------------------------------------------------------------------------
  485. //-------------------------------------------------------------------------------------------------
  486. //-------------------------------------------------------------------------------------------------
  487. //-------------------------------------------------------------------------------------------------
  488. PerfTimer::PerfTimer( const char *identifier, Bool crashWithInfo, Int startFrame, Int endFrame) :
  489. m_identifier(identifier),
  490. m_crashWithInfo(crashWithInfo),
  491. m_startFrame(startFrame),
  492. m_endFrame(endFrame),
  493. m_callCount(0),
  494. m_runningTime(0),
  495. m_outputInfo(true),
  496. //Added By Sadullah Nader
  497. //Intializations inserted
  498. m_lastFrame(-1)
  499. {
  500. }
  501. //-------------------------------------------------------------------------------------------------
  502. PerfTimer::~PerfTimer( )
  503. {
  504. if (m_endFrame == -1) {
  505. outputInfo();
  506. }
  507. }
  508. //-------------------------------------------------------------------------------------------------
  509. void PerfTimer::outputInfo( void )
  510. {
  511. if (TheGlobalData->m_showMetrics) {
  512. return;
  513. }
  514. if (m_outputInfo && !TheGlobalData->m_showMetrics) {
  515. m_outputInfo = false;
  516. } else {
  517. return;
  518. }
  519. if (!s_ticksPerSec) {
  520. // DEBUG_CRASH here
  521. return;
  522. }
  523. #if defined(_DEBUG) || defined(_INTERNAL)
  524. double totalTimeInMS = 1000.0 * m_runningTime / s_ticksPerSec;
  525. double avgTimePerFrame = totalTimeInMS / (m_lastFrame - m_startFrame + 1);
  526. double avgTimePerCall = totalTimeInMS / m_callCount;
  527. #endif
  528. if (m_crashWithInfo) {
  529. DEBUG_CRASH(("%s\n"
  530. "Average Time (per call): %.4f ms\n"
  531. "Average Time (per frame): %.4f ms\n"
  532. "Average calls per frame: %.2f\n"
  533. "Number of calls: %d\n"
  534. "Max possible FPS: %.4f\n",
  535. m_identifier,
  536. avgTimePerCall,
  537. avgTimePerFrame,
  538. 1.0f * m_callCount / (m_lastFrame - m_startFrame + 1),
  539. m_callCount,
  540. 1000.0f / avgTimePerFrame));
  541. } else {
  542. DEBUG_LOG(("%s\n"
  543. "Average Time (per call): %.4f ms\n"
  544. "Average Time (per frame): %.4f ms\n"
  545. "Average calls per frame: %.2f\n"
  546. "Number of calls: %d\n"
  547. "Max possible FPS: %.4f\n",
  548. m_identifier,
  549. avgTimePerCall,
  550. avgTimePerFrame,
  551. 1.0f * m_callCount / (m_lastFrame - m_startFrame + 1),
  552. m_callCount,
  553. 1000.0f / avgTimePerFrame));
  554. }
  555. }
  556. //-------------------------------------------------------------------------------------------------
  557. void PerfTimer::showMetrics( void )
  558. {
  559. #if defined(_DEBUG) || defined(_INTERNAL)
  560. double totalTimeInMS = 1000.0 * m_runningTime / s_ticksPerSec;
  561. double avgTimePerFrame = totalTimeInMS / (m_lastFrame - m_startFrame + 1);
  562. double avgTimePerCall = totalTimeInMS / m_callCount;
  563. #endif
  564. // we want to work on the thing in the array, so just store a reference.
  565. AsciiString &outputStats = s_output.getStatsString(m_identifier);
  566. outputStats.format("%s: %.2fms / call, %.2fms / frame \n",
  567. m_identifier,
  568. avgTimePerCall,
  569. avgTimePerFrame);
  570. m_callCount = 0;
  571. m_runningTime = 0;
  572. UnsignedInt frm = (TheGameLogic ? TheGameLogic->getFrame() : m_startFrame);
  573. m_startFrame = frm + 1;
  574. m_endFrame = m_startFrame + PERFMETRICS_BETWEEN_METRICS;
  575. }
  576. //-------------------------------------------------------------------------------StatMetricsDisplay
  577. void StatMetricsDisplay( DebugDisplayInterface *dd, void *, FILE *fp )
  578. {
  579. dd->printf("Performance Metrics: \n");
  580. // no copies will take place because we are storing a reference to the thing
  581. StringPairVec &stats = s_output.friend_getAllStatsStrings();
  582. for (int i = 0; i < stats.size(); ++i) {
  583. dd->printf("%s", stats[i].second.str());
  584. }
  585. }
  586. #endif // PERF_TIMERS