Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
7.8 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "framefunction.h"
  9. #include "gclogger.h"
  10. #include "gcsdk/scheduledfunction.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. namespace GCSDK
  14. {
  15. //-------------------------------------------------------------------------
  16. CBaseFrameFunction::CBaseFrameFunction( const char *pchName, EFrameType eFrameType ) :
  17. m_sName( pchName ),
  18. m_EFrameType( eFrameType ),
  19. m_nNumCalls( 0 ),
  20. m_nTrackedTime( 0 ),
  21. m_bRegistered( false )
  22. {
  23. }
  24. //-------------------------------------------------------------------------
  25. CBaseFrameFunction::~CBaseFrameFunction()
  26. {
  27. Deregister();
  28. }
  29. //called to handle registering for updates and unregistering. Not registered by default
  30. void CBaseFrameFunction::Register()
  31. {
  32. if( !m_bRegistered )
  33. {
  34. GFrameFunctionMgr().Register( this );
  35. m_bRegistered = true;
  36. }
  37. }
  38. void CBaseFrameFunction::Deregister()
  39. {
  40. if( m_bRegistered )
  41. {
  42. GFrameFunctionMgr().Deregister( this );
  43. m_bRegistered = false;
  44. }
  45. }
  46. //-------------------------------------------------------------------------
  47. CFrameFunctionMgr::CFrameFunctionMgr() :
  48. m_nNumProfileFrames( 0 ),
  49. m_bCompletedHighPri( false )
  50. {
  51. }
  52. //-------------------------------------------------------------------------
  53. void CFrameFunctionMgr::Register( CBaseFrameFunction* pFrameFunc )
  54. {
  55. if( !pFrameFunc )
  56. return;
  57. //see which type this frame function is
  58. CBaseFrameFunction::EFrameType eType = pFrameFunc->m_EFrameType;
  59. if( eType >= CBaseFrameFunction::k_EFrameType_Count )
  60. return;
  61. //don't allow for duplicates
  62. if( m_MainLoopFrameFuncs[ eType ].Find( pFrameFunc ) == m_MainLoopFrameFuncs[ eType ].InvalidIndex() )
  63. {
  64. m_MainLoopFrameFuncs[ eType ].AddToTail( pFrameFunc );
  65. }
  66. }
  67. //-------------------------------------------------------------------------
  68. void CFrameFunctionMgr::Deregister( CBaseFrameFunction* pFrameFunc )
  69. {
  70. if( !pFrameFunc )
  71. return;
  72. //see which type this frame function is
  73. CBaseFrameFunction::EFrameType eType = pFrameFunc->m_EFrameType;
  74. if( eType >= CBaseFrameFunction::k_EFrameType_Count )
  75. return;
  76. m_MainLoopFrameFuncs[ eType ].FindAndRemove( pFrameFunc );
  77. }
  78. //-------------------------------------------------------------------------
  79. void CFrameFunctionMgr::RunFrame( const CLimitTimer& limitTimer )
  80. {
  81. VPROF_BUDGET( "CFrameFunctionMgr::RunFrame", VPROF_BUDGETGROUP_STEAM );
  82. //track the number of frames we've profiled
  83. m_nNumProfileFrames++;
  84. m_bCompletedHighPri = false;
  85. //run through each of our 'once a frame' updates
  86. RunFrameList( CBaseFrameFunction::k_EFrameType_RunOnce, limitTimer );
  87. }
  88. //-------------------------------------------------------------------------
  89. bool CFrameFunctionMgr::RunFrameTick( const CLimitTimer& limitTimer )
  90. {
  91. VPROF_BUDGET( "CFrameFunctionMgr::RunFrameTick", VPROF_BUDGETGROUP_STEAM );
  92. //run high priority if we haven't finished it yet
  93. if( !m_bCompletedHighPri )
  94. {
  95. if( RunFrameList( CBaseFrameFunction::k_EFrameType_RunUntilCompleted, limitTimer ) )
  96. {
  97. //we need to update another frame
  98. return true;
  99. }
  100. else
  101. {
  102. //we have completed high priority
  103. m_bCompletedHighPri = true;
  104. }
  105. }
  106. //if we are still here, we have completed high priority, so run until we are done with low priority
  107. return RunFrameList( CBaseFrameFunction::k_EFrameType_RunUntilCompletedLowPri, limitTimer );
  108. }
  109. //-------------------------------------------------------------------------
  110. bool CFrameFunctionMgr::RunFrameList( CBaseFrameFunction::EFrameType eType, const CLimitTimer& limitTimer )
  111. {
  112. bool bResult = false;
  113. //run through each of our 'once a frame' updates
  114. FOR_EACH_VEC( m_MainLoopFrameFuncs[ eType ], nCurrFunc )
  115. {
  116. CBaseFrameFunction* pFunc = m_MainLoopFrameFuncs[ eType ][ nCurrFunc ];
  117. uint64 nTimeStart = limitTimer.CMicroSecLeft();
  118. {
  119. VPROF_BUDGET( pFunc->m_sName.Get(), VPROF_BUDGETGROUP_STEAM );
  120. bResult |= pFunc->BRun( limitTimer );
  121. }
  122. //track the time spent in this function
  123. pFunc->m_nNumCalls++;
  124. pFunc->m_nTrackedTime += nTimeStart - limitTimer.CMicroSecLeft();
  125. }
  126. return bResult;
  127. }
  128. //-------------------------------------------------------------------------
  129. bool CFrameFunctionMgr::SortFrameFuncByTime( const CBaseFrameFunction* pLhs, const CBaseFrameFunction* pRhs )
  130. {
  131. //sort by time first, then name if the same time (unlikely)
  132. if( pLhs->m_nTrackedTime != pRhs->m_nTrackedTime )
  133. return pLhs->m_nTrackedTime > pRhs->m_nTrackedTime;
  134. return stricmp( pLhs->m_sName.Get(), pRhs->m_sName.Get() ) < 0;
  135. }
  136. void CFrameFunctionMgr::DumpProfile()
  137. {
  138. //collect all of our functions and sort them based upon time elapsed
  139. CUtlVector< CBaseFrameFunction* > FrameFuncs;
  140. uint64 nTotalTime = 0;
  141. uint32 nTotalCalls = 0;
  142. for( uint32 nCurrType = 0; nCurrType < CBaseFrameFunction::k_EFrameType_Count; nCurrType++ )
  143. {
  144. FOR_EACH_VEC (m_MainLoopFrameFuncs[ nCurrType ], nCurrFunc )
  145. {
  146. CBaseFrameFunction* pFunc = m_MainLoopFrameFuncs[ nCurrType ][ nCurrFunc ];
  147. FrameFuncs.AddToTail( pFunc );
  148. nTotalTime += pFunc->m_nTrackedTime;
  149. nTotalCalls += pFunc->m_nNumCalls;
  150. }
  151. }
  152. double fInvFrame = ( m_nNumProfileFrames > 0 ) ? 1.0 / m_nNumProfileFrames : 1.0;
  153. std::sort( FrameFuncs.begin(), FrameFuncs.end(), SortFrameFuncByTime );
  154. //now dump out the timings in a grid
  155. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%s", "Name Time(ms) Calls Time% Time/F Calls/F\n" );
  156. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%s", "---------------------------------------- ------------ ---------- ------ ---------- ----------\n" );
  157. //print out each API
  158. FOR_EACH_VEC( FrameFuncs, nFunc )
  159. {
  160. CBaseFrameFunction* pFunc = FrameFuncs[ nFunc ];
  161. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%-40s %12.2f %10d %5.1f%% %10.3f %10.1f\n",
  162. pFunc->m_sName.Get(), pFunc->m_nTrackedTime / 1000.0, pFunc->m_nNumCalls, pFunc->m_nTrackedTime / ( double )nTotalTime, pFunc->m_nTrackedTime * fInvFrame / 1000.0, pFunc->m_nNumCalls * fInvFrame );
  163. }
  164. //print out the footer and totals
  165. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%s", "---------------------------------------- ------------ ---------- ------ ---------- ----------\n" );
  166. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%-40s %12.2f %10d %5.1f%% %10.3f %10.1f\n",
  167. "Totals", nTotalTime / 1000.0, nTotalCalls, 100.0, nTotalTime * fInvFrame / 1000.0, nTotalCalls * fInvFrame );
  168. }
  169. //-------------------------------------------------------------------------
  170. void CFrameFunctionMgr::ClearProfile()
  171. {
  172. //run through all of our functions and clear timings
  173. for( uint32 nCurrType = 0; nCurrType < CBaseFrameFunction::k_EFrameType_Count; nCurrType++ )
  174. {
  175. FOR_EACH_VEC (m_MainLoopFrameFuncs[ nCurrType ], nCurrFunc )
  176. {
  177. CBaseFrameFunction* pFunc = m_MainLoopFrameFuncs[ nCurrType ][ nCurrFunc ];
  178. pFunc->m_nTrackedTime = 0;
  179. pFunc->m_nNumCalls = 0;
  180. }
  181. }
  182. m_nNumProfileFrames = 0;
  183. }
  184. //-------------------------------------------------------------------------
  185. CFrameFunctionMgr& GFrameFunctionMgr()
  186. {
  187. static CFrameFunctionMgr s_Singleton;
  188. return s_Singleton;
  189. }
  190. //-------------------------------------------------------------------------
  191. //utility class for dumping out the profile results after time has expired
  192. static void DumpFrameFunctionProfile()
  193. {
  194. GFrameFunctionMgr().DumpProfile();
  195. }
  196. GC_CON_COMMAND( framefunc_profile, "Turns on frame function profiling for N seconds and dumps the results" )
  197. {
  198. if( args.ArgC() < 2 )
  199. {
  200. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Proper usage is framefunc_profile <n> where n is the number of seconds to profile for\n" );
  201. return;
  202. }
  203. float fSeconds = MAX( 1.0f, atof( args[ 1 ] ) );
  204. GFrameFunctionMgr().ClearProfile();
  205. static CGlobalScheduledFunction s_DumpProfile;
  206. s_DumpProfile.ScheduleMS( DumpFrameFunctionProfile, fSeconds * 1000.0f );
  207. }
  208. } //namespace GCSDK