Counter Strike : Global Offensive Source Code
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.

241 lines
6.0 KiB

  1. //========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: module for gathering performance stats for upload so that we can
  4. // monitor performance regressions and improvements
  5. //
  6. //=====================================================================================//
  7. #include "cbase.h"
  8. #include "statgather.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. #define STATS_WINDOW_SIZE ( 60 * 10 ) // # of records to hold
  12. #define STATS_RECORD_INTERVAL 1 // # of seconds between data grabs. 2 * 300 = every 10 minutes
  13. struct StatsBufferRecord_t
  14. {
  15. float m_flFrameRate; // fps
  16. };
  17. const int PERFDATA_LEVEL = 1;
  18. const int PERFDATA_SHUTDOWN = 2;
  19. class CStatsRecorder : public CAutoGameSystem
  20. {
  21. StatsBufferRecord_t m_StatsBuffer[STATS_WINDOW_SIZE];
  22. bool m_bBufferFull;
  23. float m_flLastRealTime;
  24. float m_flLastSampleTime;
  25. template<class T> T AverageStat( T StatsBufferRecord_t::*field ) const
  26. {
  27. T sum = 0;
  28. for( int i = 0; i < STATS_WINDOW_SIZE; i++ )
  29. sum += m_StatsBuffer[i].*field;
  30. return sum / STATS_WINDOW_SIZE;
  31. }
  32. template<class T> T MaxStat( T StatsBufferRecord_t::*field ) const
  33. {
  34. T maxsofar = -16000000;
  35. for( int i = 0; i < STATS_WINDOW_SIZE; i++ )
  36. maxsofar = MAX( maxsofar, m_StatsBuffer[i].*field );
  37. return maxsofar;
  38. }
  39. template<class T> T MinStat( T StatsBufferRecord_t::*field ) const
  40. {
  41. T minsofar = 16000000;
  42. for( int i = 0; i < STATS_WINDOW_SIZE; i++ )
  43. minsofar = MIN( minsofar, m_StatsBuffer[i].*field );
  44. return minsofar;
  45. }
  46. inline void AdvanceIndex( void )
  47. {
  48. m_nWriteIndex++;
  49. if ( m_nWriteIndex == STATS_WINDOW_SIZE )
  50. {
  51. m_nWriteIndex = 0;
  52. m_bBufferFull = true;
  53. }
  54. }
  55. void LevelInitPreEntity()
  56. {
  57. m_flTimeLevelStart = gpGlobals->curtime;
  58. }
  59. void LevelShutdownPreEntity()
  60. {
  61. float flLevelTime = gpGlobals->curtime - m_flTimeLevelStart;
  62. m_flTotalTimeInLevels += flLevelTime;
  63. m_iNumLevels ++;
  64. UploadPerfData( PERFDATA_LEVEL );
  65. }
  66. void Shutdown()
  67. {
  68. UploadPerfData( PERFDATA_SHUTDOWN );
  69. }
  70. public:
  71. int m_nWriteIndex;
  72. float m_flTimeLevelStart;
  73. float m_flTotalTimeInLevels;
  74. int m_iNumLevels;
  75. CStatsRecorder( void )
  76. {
  77. m_bBufferFull = false;
  78. m_nWriteIndex = 0;
  79. m_flLastRealTime = -1;
  80. m_flLastSampleTime = -1;
  81. m_flTimeLevelStart = 0;
  82. m_flTotalTimeInLevels = 0;
  83. m_iNumLevels = 0;
  84. }
  85. char const *GetPerfStatsString( int iType );
  86. void UploadPerfData( int iType );
  87. void UpdatePerfStats( void );
  88. };
  89. char s_cPerfString[2048];
  90. static inline char const *SafeString( char const *pStr )
  91. {
  92. return ( pStr ) ? pStr : "?";
  93. }
  94. // get the string record for sending to the server. Contains perf data and hardware/software
  95. // info. Returns NULL if there isn't a good record to send (i.e. not enough data yet).
  96. // A successful Get() resets the stats
  97. char const *CStatsRecorder::GetPerfStatsString( int iType )
  98. {
  99. switch ( iType )
  100. {
  101. case PERFDATA_LEVEL:
  102. {
  103. if ( ! m_bBufferFull )
  104. return NULL;
  105. float flAverageFrameRate = AverageStat( &StatsBufferRecord_t::m_flFrameRate );
  106. float flMinFrameRate = MinStat( &StatsBufferRecord_t::m_flFrameRate );
  107. float flMaxFrameRate = MaxStat( &StatsBufferRecord_t::m_flFrameRate );
  108. const CPUInformation &cpu = GetCPUInformation();
  109. MaterialAdapterInfo_t gpu;
  110. materials->GetDisplayAdapterInfo( materials->GetCurrentAdapter(), gpu );
  111. CMatRenderContextPtr pRenderContext( materials );
  112. int dest_width,dest_height;
  113. pRenderContext->GetRenderTargetDimensions( dest_width, dest_height );
  114. char szMap[MAX_PATH+1]="";
  115. Q_FileBase( engine->GetLevelName(), szMap, ARRAYSIZE( szMap ) );
  116. V_snprintf( s_cPerfString, sizeof( s_cPerfString ),
  117. "PERFDATA:AvgFps=%4.2f MinFps=%4.2f MaxFps=%4.2f CPUID=\"%s\" CPUGhz=%2.2f "
  118. "NumCores=%d GPUDrv=\"%s\" "
  119. "GPUVendor=%d GPUDeviceID=%d "
  120. "GPUDriverVersion=\"%d.%d\" DxLvl=%d "
  121. "Width=%d Height=%d MapName=%s",
  122. flAverageFrameRate,
  123. flMinFrameRate,
  124. flMaxFrameRate,
  125. cpu.m_szProcessorID,
  126. cpu.m_Speed * ( 1.0 / 1.0e9 ),
  127. cpu.m_nPhysicalProcessors,
  128. SafeString( gpu.m_pDriverName ),
  129. gpu.m_VendorID,
  130. gpu.m_DeviceID,
  131. gpu.m_nDriverVersionHigh,
  132. gpu.m_nDriverVersionLow,
  133. g_pMaterialSystemHardwareConfig->GetDXSupportLevel(),
  134. dest_width, dest_height, szMap
  135. );
  136. // get rid of chars that we hate in vendor strings
  137. for( char *i = s_cPerfString; *i; i++ )
  138. {
  139. if ( ( i[0]=='\n' ) || ( i[0]=='\r' ) || ( i[0]==';' ) )
  140. i[0]=' ';
  141. }
  142. // clear buffer
  143. m_nWriteIndex = 0;
  144. m_bBufferFull = false;
  145. return s_cPerfString;
  146. }
  147. case PERFDATA_SHUTDOWN:
  148. V_snprintf( s_cPerfString, sizeof( s_cPerfString ), "PERFDATA:TotalLevelTime=%d NumLevels=%d",
  149. (int) m_flTotalTimeInLevels, m_iNumLevels );
  150. return s_cPerfString;
  151. default:
  152. Assert( false );
  153. return NULL;
  154. }
  155. }
  156. void CStatsRecorder::UpdatePerfStats( void )
  157. {
  158. float flCurTime = Plat_FloatTime();
  159. if (
  160. ( m_flLastSampleTime == -1 ) ||
  161. ( flCurTime - m_flLastSampleTime >= STATS_RECORD_INTERVAL ) )
  162. {
  163. if ( ( m_flLastRealTime > 0 ) && ( flCurTime > m_flLastRealTime ) )
  164. {
  165. float flFrameRate = 1.0 / ( flCurTime - m_flLastRealTime );
  166. StatsBufferRecord_t &stat = m_StatsBuffer[m_nWriteIndex];
  167. stat.m_flFrameRate = flFrameRate;
  168. AdvanceIndex();
  169. m_flLastSampleTime = flCurTime;
  170. }
  171. }
  172. m_flLastRealTime = flCurTime;
  173. }
  174. static CStatsRecorder s_StatsRecorder;
  175. void UpdatePerfStats( void )
  176. {
  177. s_StatsRecorder.UpdatePerfStats();
  178. }
  179. static void ShowPerfStats( void )
  180. {
  181. char const *pStr = s_StatsRecorder.GetPerfStatsString( PERFDATA_LEVEL );
  182. if ( pStr )
  183. Warning( "%s\n", pStr );
  184. else
  185. Warning( "%d records stored. buffer not full.\n", s_StatsRecorder.m_nWriteIndex );
  186. }
  187. static ConCommand perfstats( "cl_perfstats", ShowPerfStats, "Dump the perf monitoring string" );
  188. // upload performance to steam, if we have any. This is a blocking call.
  189. void CStatsRecorder::UploadPerfData( int iType )
  190. {
  191. if( g_pClientGameStatsUploader )
  192. {
  193. char const *pPerfData = GetPerfStatsString( iType );
  194. if ( pPerfData )
  195. {
  196. g_pClientGameStatsUploader->UploadGameStats( "",
  197. 1,
  198. 1 + strlen( pPerfData ),
  199. pPerfData );
  200. }
  201. }
  202. }