diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/statgather.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'game/client/statgather.cpp')
| -rw-r--r-- | game/client/statgather.cpp | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/game/client/statgather.cpp b/game/client/statgather.cpp new file mode 100644 index 0000000..e26028c --- /dev/null +++ b/game/client/statgather.cpp @@ -0,0 +1,241 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: module for gathering performance stats for upload so that we can +// monitor performance regressions and improvements +// +//=====================================================================================// + + +#include "cbase.h" +#include "statgather.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +#define STATS_WINDOW_SIZE ( 60 * 10 ) // # of records to hold +#define STATS_RECORD_INTERVAL 1 // # of seconds between data grabs. 2 * 300 = every 10 minutes + + +struct StatsBufferRecord_t +{ + float m_flFrameRate; // fps + +}; + +const int PERFDATA_LEVEL = 1; +const int PERFDATA_SHUTDOWN = 2; + +class CStatsRecorder : public CAutoGameSystem +{ + + StatsBufferRecord_t m_StatsBuffer[STATS_WINDOW_SIZE]; + bool m_bBufferFull; + float m_flLastRealTime; + float m_flLastSampleTime; + + template<class T> T AverageStat( T StatsBufferRecord_t::*field ) const + { + T sum = 0; + for( int i = 0; i < STATS_WINDOW_SIZE; i++ ) + sum += m_StatsBuffer[i].*field; + return sum / STATS_WINDOW_SIZE; + } + + template<class T> T MaxStat( T StatsBufferRecord_t::*field ) const + { + T maxsofar = -16000000; + for( int i = 0; i < STATS_WINDOW_SIZE; i++ ) + maxsofar = MAX( maxsofar, m_StatsBuffer[i].*field ); + return maxsofar; + } + + template<class T> T MinStat( T StatsBufferRecord_t::*field ) const + { + T minsofar = 16000000; + for( int i = 0; i < STATS_WINDOW_SIZE; i++ ) + minsofar = MIN( minsofar, m_StatsBuffer[i].*field ); + return minsofar; + } + + inline void AdvanceIndex( void ) + { + m_nWriteIndex++; + if ( m_nWriteIndex == STATS_WINDOW_SIZE ) + { + m_nWriteIndex = 0; + m_bBufferFull = true; + } + } + + void LevelInitPreEntity() + { + m_flTimeLevelStart = gpGlobals->curtime; + } + + void LevelShutdownPreEntity() + { + float flLevelTime = gpGlobals->curtime - m_flTimeLevelStart; + m_flTotalTimeInLevels += flLevelTime; + m_iNumLevels ++; + UploadPerfData( PERFDATA_LEVEL ); + } + + void Shutdown() + { + UploadPerfData( PERFDATA_SHUTDOWN ); + } + +public: + int m_nWriteIndex; + float m_flTimeLevelStart; + float m_flTotalTimeInLevels; + int m_iNumLevels; + CStatsRecorder( void ) + { + m_bBufferFull = false; + m_nWriteIndex = 0; + m_flLastRealTime = -1; + m_flLastSampleTime = -1; + m_flTimeLevelStart = 0; + m_flTotalTimeInLevels = 0; + m_iNumLevels = 0; + } + + char const *GetPerfStatsString( int iType ); + void UploadPerfData( int iType ); + void UpdatePerfStats( void ); +}; + +char s_cPerfString[2048]; + +static inline char const *SafeString( char const *pStr ) +{ + return ( pStr ) ? pStr : "?"; +} + +// get the string record for sending to the server. Contains perf data and hardware/software +// info. Returns NULL if there isn't a good record to send (i.e. not enough data yet). +// A successful Get() resets the stats +char const *CStatsRecorder::GetPerfStatsString( int iType ) +{ + switch ( iType ) + { + case PERFDATA_LEVEL: + { + if ( ! m_bBufferFull ) + return NULL; + + float flAverageFrameRate = AverageStat( &StatsBufferRecord_t::m_flFrameRate ); + float flMinFrameRate = MinStat( &StatsBufferRecord_t::m_flFrameRate ); + float flMaxFrameRate = MaxStat( &StatsBufferRecord_t::m_flFrameRate ); + + const CPUInformation &cpu = GetCPUInformation(); + MaterialAdapterInfo_t gpu; + materials->GetDisplayAdapterInfo( materials->GetCurrentAdapter(), gpu ); + + CMatRenderContextPtr pRenderContext( materials ); + int dest_width,dest_height; + pRenderContext->GetRenderTargetDimensions( dest_width, dest_height ); + + char szMap[MAX_PATH+1]=""; + Q_FileBase( engine->GetLevelName(), szMap, ARRAYSIZE( szMap ) ); + + V_snprintf( s_cPerfString, sizeof( s_cPerfString ), + "PERFDATA:AvgFps=%4.2f MinFps=%4.2f MaxFps=%4.2f CPUID=\"%s\" CPUGhz=%2.2f " + "NumCores=%d GPUDrv=\"%s\" " + "GPUVendor=%d GPUDeviceID=%d " + "GPUDriverVersion=\"%d.%d\" DxLvl=%d " + "Width=%d Height=%d MapName=%s", + flAverageFrameRate, + flMinFrameRate, + flMaxFrameRate, + cpu.m_szProcessorID, + cpu.m_Speed * ( 1.0 / 1.0e9 ), + cpu.m_nPhysicalProcessors, + SafeString( gpu.m_pDriverName ), + gpu.m_VendorID, + gpu.m_DeviceID, + gpu.m_nDriverVersionHigh, + gpu.m_nDriverVersionLow, + g_pMaterialSystemHardwareConfig->GetDXSupportLevel(), + dest_width, dest_height, szMap + ); + // get rid of chars that we hate in vendor strings + for( char *i = s_cPerfString; *i; i++ ) + { + if ( ( i[0]=='\n' ) || ( i[0]=='\r' ) || ( i[0]==';' ) ) + i[0]=' '; + } + + // clear buffer + m_nWriteIndex = 0; + m_bBufferFull = false; + + return s_cPerfString; + } + case PERFDATA_SHUTDOWN: + V_snprintf( s_cPerfString, sizeof( s_cPerfString ), "PERFDATA:TotalLevelTime=%d NumLevels=%d", + (int) m_flTotalTimeInLevels, m_iNumLevels ); + return s_cPerfString; + + default: + Assert( false ); + return NULL; + } +} + +void CStatsRecorder::UpdatePerfStats( void ) +{ + float flCurTime = Plat_FloatTime(); + if ( + ( m_flLastSampleTime == -1 ) || + ( flCurTime - m_flLastSampleTime >= STATS_RECORD_INTERVAL ) ) + { + if ( ( m_flLastRealTime > 0 ) && ( flCurTime > m_flLastRealTime ) ) + { + float flFrameRate = 1.0 / ( flCurTime - m_flLastRealTime ); + StatsBufferRecord_t &stat = m_StatsBuffer[m_nWriteIndex]; + stat.m_flFrameRate = flFrameRate; + AdvanceIndex(); + m_flLastSampleTime = flCurTime; + } + } + m_flLastRealTime = flCurTime; +} + +static CStatsRecorder s_StatsRecorder; + +void UpdatePerfStats( void ) +{ + s_StatsRecorder.UpdatePerfStats(); +} + +static void ShowPerfStats( void ) +{ + char const *pStr = s_StatsRecorder.GetPerfStatsString( PERFDATA_LEVEL ); + if ( pStr ) + Warning( "%s\n", pStr ); + else + Warning( "%d records stored. buffer not full.\n", s_StatsRecorder.m_nWriteIndex ); +} + +static ConCommand perfstats( "cl_perfstats", ShowPerfStats, "Dump the perf monitoring string" ); + + +// upload performance to steam, if we have any. This is a blocking call. +void CStatsRecorder::UploadPerfData( int iType ) +{ + if( g_pClientGameStatsUploader ) + { + char const *pPerfData = GetPerfStatsString( iType ); + + if ( pPerfData ) + { + g_pClientGameStatsUploader->UploadGameStats( "", + 1, + 1 + strlen( pPerfData ), + pPerfData ); + } + } +} |