2023-10-03 17:23:56 +03:00
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
2020-04-22 12:56:21 -04:00
//
// Purpose:
//
//=====================================================================================//
# include "cbase.h"
# include "ifpspanel.h"
# include <vgui_controls/Panel.h>
# include "view.h"
# include <vgui/IVGui.h>
2023-10-03 17:23:56 +03:00
# include "vguimatsurface/imatsystemsurface.h"
2020-04-22 12:56:21 -04:00
# include <vgui_controls/Controls.h>
# include <vgui/ISurface.h>
# include <vgui/IScheme.h>
# include <vgui/IPanel.h>
# include "materialsystem/imaterialsystemhardwareconfig.h"
# include "filesystem.h"
# include "../common/xbox/xboxstubs.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
2023-10-03 17:23:56 +03:00
static ConVar cl_showfps ( " cl_showfps " , " 0 " , FCVAR_RELEASE , " Draw fps meter at top of screen (1 = fps, 2 = smooth fps, 3 = server MS, 4 = Show FPS and Log to file ) " ) ;
static ConVar cl_showpos ( " cl_showpos " , " 0 " , FCVAR_RELEASE , " Draw current position at top of screen " ) ;
2020-04-22 12:56:21 -04:00
extern bool g_bDisplayParticlePerformance ;
int GetParticlePerformance ( ) ;
2023-10-03 17:23:56 +03:00
# define PERF_HISTOGRAM_BUCKET_SIZE 60
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
// Purpose: Framerate indicator panel
//-----------------------------------------------------------------------------
class CFPSPanel : public vgui : : Panel
{
DECLARE_CLASS_SIMPLE ( CFPSPanel , vgui : : Panel ) ;
public :
CFPSPanel ( vgui : : VPANEL parent ) ;
virtual ~ CFPSPanel ( void ) ;
virtual void ApplySchemeSettings ( vgui : : IScheme * pScheme ) ;
virtual void Paint ( ) ;
virtual void OnTick ( void ) ;
2023-10-03 17:23:56 +03:00
virtual void DumpStats ( ) ;
2020-04-22 12:56:21 -04:00
virtual bool ShouldDraw ( void ) ;
2023-10-03 17:23:56 +03:00
2020-04-22 12:56:21 -04:00
protected :
MESSAGE_FUNC_INT_INT ( OnScreenSizeChanged , " OnScreenSizeChanged " , oldwide , oldtall ) ;
private :
void ComputeSize ( void ) ;
void InitAverages ( )
{
m_AverageFPS = - 1 ;
m_lastRealTime = - 1 ;
m_high = - 1 ;
m_low = - 1 ;
2023-10-03 17:23:56 +03:00
memset ( m_pServerTimes , 0 , sizeof ( m_pServerTimes ) ) ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
enum { SERVER_TIME_HISTORY = 32 } ;
2020-04-22 12:56:21 -04:00
vgui : : HFont m_hFont ;
float m_AverageFPS ;
float m_lastRealTime ;
2023-10-03 17:23:56 +03:00
float m_pServerTimes [ SERVER_TIME_HISTORY ] ;
int m_nServerTimeIndex ;
2020-04-22 12:56:21 -04:00
int m_high ;
int m_low ;
bool m_bLastDraw ;
2023-10-03 17:23:56 +03:00
int m_nLinesNeeded ;
TimedEvent m_tLogTimer ;
FileHandle_t m_fhLog ;
char m_szLevelname [ 32 ] ;
int m_nNumFramesTotal ;
int m_nNumFramesBucket [ PERF_HISTOGRAM_BUCKET_SIZE ] ;
2020-04-22 12:56:21 -04:00
} ;
# define FPS_PANEL_WIDTH 300
//-----------------------------------------------------------------------------
// Purpose:
// Input : *parent -
//-----------------------------------------------------------------------------
CFPSPanel : : CFPSPanel ( vgui : : VPANEL parent ) : BaseClass ( NULL , " CFPSPanel " )
{
2023-10-03 17:23:56 +03:00
memset ( m_pServerTimes , 0 , sizeof ( m_pServerTimes ) ) ;
m_nServerTimeIndex = 0 ;
2020-04-22 12:56:21 -04:00
SetParent ( parent ) ;
SetVisible ( false ) ;
SetCursor ( null ) ;
SetFgColor ( Color ( 0 , 0 , 0 , 255 ) ) ;
SetPaintBackgroundEnabled ( false ) ;
2023-10-03 17:23:56 +03:00
2020-04-22 12:56:21 -04:00
m_hFont = 0 ;
2023-10-03 17:23:56 +03:00
m_nLinesNeeded = 5 ;
2020-04-22 12:56:21 -04:00
ComputeSize ( ) ;
vgui : : ivgui ( ) - > AddTickSignal ( GetVPanel ( ) , 250 ) ;
m_bLastDraw = false ;
2023-10-03 17:23:56 +03:00
m_tLogTimer . Init ( 6 ) ;
m_fhLog = FILESYSTEM_INVALID_HANDLE ;
m_nNumFramesTotal = 0 ;
memset ( m_nNumFramesBucket , 0 , sizeof ( m_nNumFramesBucket ) ) ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CFPSPanel : : ~ CFPSPanel ( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Updates panel to handle the new screen size
//-----------------------------------------------------------------------------
void CFPSPanel : : OnScreenSizeChanged ( int iOldWide , int iOldTall )
{
BaseClass : : OnScreenSizeChanged ( iOldWide , iOldTall ) ;
ComputeSize ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Computes panel's desired size and position
//-----------------------------------------------------------------------------
void CFPSPanel : : ComputeSize ( void )
{
int wide , tall ;
vgui : : ipanel ( ) - > GetSize ( GetVParent ( ) , wide , tall ) ;
2023-10-03 17:23:56 +03:00
int x = 0 ; ;
2020-04-22 12:56:21 -04:00
int y = 0 ;
if ( IsX360 ( ) )
{
2023-10-03 17:23:56 +03:00
x + = XBOX_MINBORDERSAFE * wide ;
2020-04-22 12:56:21 -04:00
y + = XBOX_MINBORDERSAFE * tall ;
}
SetPos ( x , y ) ;
2023-10-03 17:23:56 +03:00
SetSize ( FPS_PANEL_WIDTH , ( m_nLinesNeeded + 2 ) * vgui : : surface ( ) - > GetFontTall ( m_hFont ) + 4 ) ;
2020-04-22 12:56:21 -04:00
}
void CFPSPanel : : ApplySchemeSettings ( vgui : : IScheme * pScheme )
{
BaseClass : : ApplySchemeSettings ( pScheme ) ;
m_hFont = pScheme - > GetFont ( " DefaultFixedOutline " ) ;
Assert ( m_hFont ) ;
ComputeSize ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFPSPanel : : OnTick ( void )
{
2023-10-03 17:23:56 +03:00
SetVisible ( ShouldDraw ( ) ) ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CFPSPanel : : ShouldDraw ( void )
{
if ( g_bDisplayParticlePerformance )
return true ;
if ( ( ! cl_showfps . GetInt ( ) | | ( gpGlobals - > absoluteframetime < = 0 ) ) & &
( ! cl_showpos . GetInt ( ) ) )
{
m_bLastDraw = false ;
return false ;
}
if ( ! m_bLastDraw )
{
m_bLastDraw = true ;
InitAverages ( ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void GetFPSColor ( int nFps , unsigned char ucColor [ 3 ] )
{
ucColor [ 0 ] = 255 ; ucColor [ 1 ] = 0 ; ucColor [ 2 ] = 0 ;
int nFPSThreshold1 = 20 ;
int nFPSThreshold2 = 15 ;
if ( IsPC ( ) & & g_pMaterialSystemHardwareConfig - > GetDXSupportLevel ( ) > = 95 )
{
nFPSThreshold1 = 60 ;
nFPSThreshold2 = 50 ;
}
2023-10-03 17:23:56 +03:00
else
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
if ( engine - > IsSplitScreenActive ( ) )
{
nFPSThreshold1 = 19 ; //20; // 19 shows up commonly when testing on the 360
nFPSThreshold2 = 15 ;
}
else
{
nFPSThreshold1 = 29 ; //30; 29 shows up commonly when testing on the 360
nFPSThreshold2 = 20 ;
}
2020-04-22 12:56:21 -04:00
}
if ( nFps > = nFPSThreshold1 )
{
ucColor [ 0 ] = 0 ;
ucColor [ 1 ] = 255 ;
}
else if ( nFps > = nFPSThreshold2 )
{
ucColor [ 1 ] = 255 ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
//-----------------------------------------------------------------------------
void CFPSPanel : : Paint ( )
{
int i = 0 ;
int x = 2 ;
2023-10-03 17:23:56 +03:00
int lineHeight = vgui : : surface ( ) - > GetFontTall ( m_hFont ) + 1 ;
2020-04-22 12:56:21 -04:00
if ( g_bDisplayParticlePerformance )
{
int nPerf = GetParticlePerformance ( ) ;
if ( nPerf )
{
unsigned char ucColor [ 3 ] = { 0 , 255 , 0 } ;
g_pMatSystemSurface - > DrawColoredText (
m_hFont , x , 42 ,
ucColor [ 0 ] , ucColor [ 1 ] , ucColor [ 2 ] ,
255 , " Particle Performance Metric : %d " , ( nPerf + 50 ) / 100 ) ;
}
}
float realFrameTime = gpGlobals - > realtime - m_lastRealTime ;
2023-10-03 17:23:56 +03:00
int nFPSMode = cl_showfps . GetInt ( ) ;
if ( nFPSMode = = 3 )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
float flServerTime = engine - > GetServerSimulationFrameTime ( ) ;
if ( flServerTime ! = 0.0f )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
m_nServerTimeIndex = ( m_nServerTimeIndex + 1 ) & ( SERVER_TIME_HISTORY - 1 ) ;
m_pServerTimes [ m_nServerTimeIndex ] = flServerTime ;
}
flServerTime = m_pServerTimes [ m_nServerTimeIndex ] ;
float flTotalTime = 0.0f ;
float flPeakTime = 0.0f ;
for ( int i = 0 ; i < SERVER_TIME_HISTORY ; + + i )
{
flTotalTime + = m_pServerTimes [ i ] ;
if ( flPeakTime < m_pServerTimes [ i ] )
{
flPeakTime = m_pServerTimes [ i ] ;
}
}
flTotalTime / = SERVER_TIME_HISTORY ;
unsigned char ucColor [ 3 ] ;
int nFps = static_cast < int > ( 1.0f / ( flServerTime * 0.001f ) ) ;
GetFPSColor ( nFps , ucColor ) ;
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x , 2 , ucColor [ 0 ] , ucColor [ 1 ] , ucColor [ 2 ] , 255 ,
" server %5.1f ms curr, %5.1f ave, %5.1f peak " , flServerTime , flTotalTime , flPeakTime ) ;
}
else if ( nFPSMode = = 4 & & m_lastRealTime > 0.0f & & realFrameTime > 0.0f & & engine - > IsInGame ( ) )
{
char levelName [ 32 ] ;
Q_strncpy ( levelName , engine - > GetLevelNameShort ( ) , sizeof ( levelName ) ) ;
if ( Q_strcmp ( m_szLevelname , levelName ) & & m_fhLog ! = FILESYSTEM_INVALID_HANDLE )
{
DumpStats ( ) ;
}
float flServerTime = engine - > GetServerSimulationFrameTime ( ) ;
if ( flServerTime ! = 0.0f )
{
m_nServerTimeIndex = ( m_nServerTimeIndex + 1 ) & ( SERVER_TIME_HISTORY - 1 ) ;
m_pServerTimes [ m_nServerTimeIndex ] = flServerTime ;
}
flServerTime = m_pServerTimes [ m_nServerTimeIndex ] ;
const float NewWeight = 0.1f ;
float NewFrame = 1.0f / realFrameTime ;
if ( m_AverageFPS < = 0.0f )
{
m_AverageFPS = NewFrame ;
}
else
{
m_AverageFPS * = ( 1.0f - NewWeight ) ;
m_AverageFPS + = ( ( NewFrame ) * NewWeight ) ;
}
int nAvgFps = static_cast < int > ( m_AverageFPS ) ;
float flAverageMS = 1000.0f / m_AverageFPS ;
float flFrameMS = realFrameTime * 1000.0f ;
int nFrameFps = static_cast < int > ( 1.0f / realFrameTime ) ;
float flCurTime = gpGlobals - > frametime ;
m_nNumFramesTotal + + ;
int nBucket = MIN ( PERF_HISTOGRAM_BUCKET_SIZE , MAX ( 0 , nFrameFps ) ) ;
m_nNumFramesBucket [ nBucket ] + + ;
unsigned char ucColor [ 3 ] ;
GetFPSColor ( nAvgFps , ucColor ) ;
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x , 2 , ucColor [ 0 ] , ucColor [ 1 ] , ucColor [ 2 ] , 255 ,
" Avg FPS %3i, Frame MS %5.1f, Frame Server MS %5.1f " , nAvgFps , flFrameMS , flServerTime ) ;
if ( m_fhLog = = FILESYSTEM_INVALID_HANDLE )
{
Q_strncpy ( m_szLevelname , levelName , sizeof ( m_szLevelname ) ) ;
// Maximum 360 file name length
char fileString [ 42 ] ;
Q_snprintf ( fileString , 42 , " prof_%s.csv " , m_szLevelname ) ;
m_fhLog = g_pFullFileSystem - > Open ( fileString , " w " , " GAME " ) ;
g_pFullFileSystem - > FPrintf ( m_fhLog , " Time,Player 1 Position,Player 2 Position,Smooth FPS,Frame FPS,Smooth MS,Frame MS,Server Frame MS \n " ) ;
}
if ( ( m_tLogTimer . NextEvent ( flCurTime ) & & nFrameFps < 28.0f ) | | nFrameFps < 15.0f )
{
Vector vecOrigin [ MAX_SPLITSCREEN_PLAYERS ] ;
QAngle angles [ MAX_SPLITSCREEN_PLAYERS ] ;
FOR_EACH_VALID_SPLITSCREEN_PLAYER ( hh )
{
vecOrigin [ hh ] = MainViewOrigin ( hh ) ;
angles [ hh ] = MainViewAngles ( hh ) ;
C_BasePlayer * pPlayer = C_BasePlayer : : GetLocalPlayer ( hh ) ;
if ( pPlayer )
{
vecOrigin [ hh ] = pPlayer - > GetAbsOrigin ( ) ;
angles [ hh ] = pPlayer - > GetAbsAngles ( ) ;
}
}
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
char outputString [ 256 ] ;
Q_snprintf ( outputString , 256 , " %5.1f,setpos %0.2f %0.2f %0.2f ; setang %0.2f %0.2f %0.2f,setpos %0.2f %0.2f %0.2f ; setang %0.2f %0.2f %0.2f,%3i,%3i,%4.1f,%4.1f,%5.1f \n " ,
gpGlobals - > curtime , vecOrigin [ 0 ] . x , vecOrigin [ 0 ] . y , vecOrigin [ 0 ] . z , angles [ 0 ] . x , angles [ 0 ] . y , angles [ 0 ] . z ,
vecOrigin [ 1 ] . x , vecOrigin [ 1 ] . y , vecOrigin [ 1 ] . z , angles [ 1 ] . x , angles [ 1 ] . y , angles [ 1 ] . z , nAvgFps ,
nFrameFps , flAverageMS , flFrameMS , flServerTime ) ;
if ( m_fhLog ! = FILESYSTEM_INVALID_HANDLE )
{
g_pFullFileSystem - > FPrintf ( m_fhLog , " %s " , outputString ) ;
}
}
}
else if ( nFPSMode & & realFrameTime > 0.0 )
{
if ( m_lastRealTime ! = - 1.0f )
{
2020-04-22 12:56:21 -04:00
int nFps = - 1 ;
unsigned char ucColor [ 3 ] ;
2023-10-03 17:23:56 +03:00
if ( nFPSMode = = 2 )
2020-04-22 12:56:21 -04:00
{
const float NewWeight = 0.1f ;
float NewFrame = 1.0f / realFrameTime ;
2023-10-03 17:23:56 +03:00
// If we're just below an integer boundary, we're good enough to call ourselves good WRT to coloration
if ( ( int ) ( NewFrame + 0.05 ) > ( int ) ( NewFrame ) )
{
NewFrame = ceil ( NewFrame ) ;
}
2020-04-22 12:56:21 -04:00
if ( m_AverageFPS < 0.0f )
{
m_AverageFPS = NewFrame ;
m_high = ( int ) m_AverageFPS ;
m_low = ( int ) m_AverageFPS ;
}
else
{
m_AverageFPS * = ( 1.0f - NewWeight ) ;
m_AverageFPS + = ( ( NewFrame ) * NewWeight ) ;
}
int NewFrameInt = ( int ) NewFrame ;
if ( NewFrameInt < m_low ) m_low = NewFrameInt ;
if ( NewFrameInt > m_high ) m_high = NewFrameInt ;
nFps = static_cast < int > ( m_AverageFPS ) ;
2023-10-03 17:23:56 +03:00
float averageMS = 1000.0f / m_AverageFPS ;
2020-04-22 12:56:21 -04:00
float frameMS = realFrameTime * 1000.0f ;
GetFPSColor ( nFps , ucColor ) ;
2023-10-03 17:23:56 +03:00
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x , 2 , ucColor [ 0 ] , ucColor [ 1 ] , ucColor [ 2 ] , 255 , " %3i fps (%3i, %3i) smth:%4.1f ms frm:%4.1f ms on %s " , nFps , m_low , m_high , averageMS , frameMS , engine - > GetLevelName ( ) ) ;
}
2020-04-22 12:56:21 -04:00
else
{
m_AverageFPS = - 1 ;
2023-10-03 17:23:56 +03:00
float flFps = ( 1.0f / realFrameTime ) ;
// If we're just below an integer boundary, we're good enough to call ourselves good WRT to coloration
if ( ( int ) ( flFps + 0.05 ) > ( int ) ( flFps ) )
{
flFps = ceil ( flFps ) ;
}
nFps = static_cast < int > ( flFps ) ;
2020-04-22 12:56:21 -04:00
GetFPSColor ( nFps , ucColor ) ;
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x , 2 , ucColor [ 0 ] , ucColor [ 1 ] , ucColor [ 2 ] , 255 , " %3i fps on %s " , nFps , engine - > GetLevelName ( ) ) ;
}
}
}
2023-10-03 17:23:56 +03:00
else if ( m_fhLog ! = FILESYSTEM_INVALID_HANDLE )
{
DumpStats ( ) ;
}
2020-04-22 12:56:21 -04:00
m_lastRealTime = gpGlobals - > realtime ;
int nShowPosMode = cl_showpos . GetInt ( ) ;
if ( nShowPosMode > 0 )
{
2023-10-03 17:23:56 +03:00
FOR_EACH_VALID_SPLITSCREEN_PLAYER ( hh )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
Vector vecOrigin = MainViewOrigin ( hh ) ;
QAngle angles = MainViewAngles ( hh ) ;
Vector vel ( 0 , 0 , 0 ) ;
char szName [ 32 ] = { 0 } ;
C_BasePlayer * pPlayer = C_BasePlayer : : GetLocalPlayer ( hh ) ;
2020-04-22 12:56:21 -04:00
if ( pPlayer )
2023-10-03 17:23:56 +03:00
{
Q_strncpy ( szName , pPlayer - > GetPlayerName ( ) , sizeof ( szName ) ) ;
vel = pPlayer - > GetLocalVelocity ( ) ;
}
if ( nShowPosMode = = 2 & & pPlayer )
2020-04-22 12:56:21 -04:00
{
vecOrigin = pPlayer - > GetAbsOrigin ( ) ;
angles = pPlayer - > GetAbsAngles ( ) ;
}
2023-10-03 17:23:56 +03:00
i + + ;
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x , 2 + i * lineHeight ,
255 , 255 , 255 , 255 ,
" name: %s " , szName ) ;
i + + ;
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x , 2 + i * lineHeight ,
255 , 255 , 255 , 255 ,
" pos: %.02f %.02f %.02f " ,
vecOrigin . x , vecOrigin . y , vecOrigin . z ) ;
i + + ;
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x , 2 + i * lineHeight ,
255 , 255 , 255 , 255 ,
" ang: %.02f %.02f %.02f " ,
angles . x , angles . y , angles . z ) ;
i + + ;
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x , 2 + i * lineHeight ,
255 , 255 , 255 , 255 ,
" vel: %.2f " ,
vel . Length ( ) ) ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
}
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
if ( m_nLinesNeeded ! = i )
{
m_nLinesNeeded = i ;
ComputeSize ( ) ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
}
2022-08-19 23:19:53 +03:00
2023-10-03 17:23:56 +03:00
//-----------------------------------------------------------------------------
// Purpose: Outputs the frame rate histogram and closes the file
//-----------------------------------------------------------------------------
void CFPSPanel : : DumpStats ( )
{
Assert ( m_fhLog ! = FILESYSTEM_INVALID_HANDLE ) ;
if ( m_nNumFramesTotal > 0 )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
g_pFullFileSystem - > FPrintf ( m_fhLog , " \n \n Total Frames : %3i \n \n " , m_nNumFramesTotal ) ;
g_pFullFileSystem - > FPrintf ( m_fhLog , " Frame Rate, Number of Frames, Percent of Frames \n " ) ;
for ( int i = 0 ; i < = PERF_HISTOGRAM_BUCKET_SIZE ; i + + )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
float flPercent = m_nNumFramesBucket [ i ] ;
flPercent / = m_nNumFramesTotal ;
flPercent * = 100.0f ;
g_pFullFileSystem - > FPrintf ( m_fhLog , " %3i, %3i, %5.1f \n " , i , m_nNumFramesBucket [ i ] , flPercent ) ;
m_nNumFramesBucket [ i ] = 0 ;
2020-04-22 12:56:21 -04:00
}
}
2023-10-03 17:23:56 +03:00
g_pFullFileSystem - > Close ( m_fhLog ) ;
m_fhLog = FILESYSTEM_INVALID_HANDLE ;
m_nNumFramesTotal = 0 ;
2020-04-22 12:56:21 -04:00
}
class CFPS : public IFPSPanel
{
private :
CFPSPanel * fpsPanel ;
public :
CFPS ( void )
{
fpsPanel = NULL ;
}
void Create ( vgui : : VPANEL parent )
{
fpsPanel = new CFPSPanel ( parent ) ;
}
void Destroy ( void )
{
if ( fpsPanel )
{
fpsPanel - > SetParent ( ( vgui : : Panel * ) NULL ) ;
2022-03-01 23:00:42 +03:00
delete fpsPanel ;
2020-04-22 12:56:21 -04:00
}
}
} ;
static CFPS g_FPSPanel ;
IFPSPanel * fps = ( IFPSPanel * ) & g_FPSPanel ;
2023-10-03 17:23:56 +03:00
# if defined( TRACK_BLOCKING_IO )
2020-04-22 12:56:21 -04:00
static ConVar cl_blocking_threshold ( " cl_blocking_threshold " , " 0.000 " , 0 , " If file ops take more than this amount of time, add to 'spewblocking' history list " ) ;
void ShowBlockingChanged ( ConVar * var , char const * pOldString )
{
filesystem - > EnableBlockingFileAccessTracking ( var - > GetBool ( ) ) ;
}
static ConVar cl_showblocking ( " cl_showblocking " , " 0 " , 0 , " Show blocking i/o on top of fps panel " , ShowBlockingChanged ) ;
static ConVar cl_blocking_recentsize ( " cl_blocking_recentsize " , " 40 " , 0 , " Number of items to store in recent spew history. " ) ;
//-----------------------------------------------------------------------------
// Purpose: blocking i/o indicator
//-----------------------------------------------------------------------------
class CBlockingFileIOPanel : public vgui : : Panel
{
typedef vgui : : Panel BaseClass ;
public :
CBlockingFileIOPanel ( vgui : : VPANEL parent ) ;
virtual ~ CBlockingFileIOPanel ( void ) ;
virtual void ApplySchemeSettings ( vgui : : IScheme * pScheme ) ;
virtual void Paint ( ) ;
virtual void OnTick ( void ) ;
virtual bool ShouldDraw ( void ) ;
void SpewRecent ( ) ;
private :
void DrawIOTime ( int x , int y , int w , int h , int slot , char const * label , const Color & clr ) ;
vgui : : HFont m_hFont ;
struct Graph_t
{
float m_flCurrent ;
float m_flHistory ;
float m_flHistorySpike ;
float m_flLatchTime ;
CUtlSymbol m_LastFile ;
} ;
Graph_t m_History [ FILESYSTEM_BLOCKING_NUMBINS ] ;
struct RecentPeaks_t
{
float time ;
CUtlSymbol fileName ;
float elapsed ;
byte reason ;
byte ioType ;
} ;
CUtlLinkedList < RecentPeaks_t , unsigned short > m_Recent ;
void SpewItem ( const RecentPeaks_t & item ) ;
} ;
# define IO_PANEL_WIDTH 400
# define IO_DECAY_FRAC 0.95f
//-----------------------------------------------------------------------------
// Purpose:
// Input : *parent -
//-----------------------------------------------------------------------------
CBlockingFileIOPanel : : CBlockingFileIOPanel ( vgui : : VPANEL parent ) : BaseClass ( NULL , " CBlockingFileIOPanel " )
{
SetParent ( parent ) ;
int wide , tall ;
vgui : : ipanel ( ) - > GetSize ( parent , wide , tall ) ;
int x = 2 ;
int y = 100 ;
if ( IsX360 ( ) )
{
x + = XBOX_MAXBORDERSAFE * wide ;
y + = XBOX_MAXBORDERSAFE * tall ;
}
SetPos ( x , y ) ;
SetSize ( IO_PANEL_WIDTH , 140 ) ;
SetVisible ( false ) ;
SetCursor ( null ) ;
SetFgColor ( Color ( 0 , 0 , 0 , 255 ) ) ;
SetPaintBackgroundEnabled ( false ) ;
m_hFont = 0 ;
vgui : : ivgui ( ) - > AddTickSignal ( GetVPanel ( ) , 250 ) ;
SetZPos ( 1000 ) ;
Q_memset ( m_History , 0 , sizeof ( m_History ) ) ;
SetPaintBackgroundEnabled ( false ) ;
SetPaintBorderEnabled ( false ) ;
MakePopup ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBlockingFileIOPanel : : ~ CBlockingFileIOPanel ( void )
{
}
void CBlockingFileIOPanel : : ApplySchemeSettings ( vgui : : IScheme * pScheme )
{
BaseClass : : ApplySchemeSettings ( pScheme ) ;
m_hFont = pScheme - > GetFont ( " Default " ) ;
Assert ( m_hFont ) ;
SetKeyBoardInputEnabled ( false ) ;
SetMouseInputEnabled ( false ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBlockingFileIOPanel : : OnTick ( void )
{
2023-10-03 17:23:56 +03:00
SetVisible ( ShouldDraw ( ) ) ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBlockingFileIOPanel : : ShouldDraw ( void )
{
if ( ! cl_showblocking . GetInt ( ) )
{
return false ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
//-----------------------------------------------------------------------------
void CBlockingFileIOPanel : : Paint ( )
{
int x = 2 ;
int maxRecent = clamp ( 0 , cl_blocking_recentsize . GetInt ( ) , 1000 ) ;
int bval = cl_showblocking . GetInt ( ) ;
if ( bval > 0 )
{
IBlockingFileItemList * list = filesystem - > RetrieveBlockingFileAccessInfo ( ) ;
if ( list )
{
int i ;
int c = ARRAYSIZE ( m_History ) ;
for ( i = 0 ; i < c ; + + i )
{
m_History [ i ] . m_flCurrent = 0.0f ;
}
// Grab mutex (prevents async thread from filling in even more data...)
list - > LockMutex ( ) ;
{
for ( int j = list - > First ( ) ; j ! = list - > InvalidIndex ( ) ; j = list - > Next ( j ) )
{
const FileBlockingItem & item = list - > Get ( j ) ;
m_History [ item . m_ItemType ] . m_flCurrent + = item . m_flElapsed ;
RecentPeaks_t recent ;
recent . time = gpGlobals - > realtime ;
recent . elapsed = item . m_flElapsed ;
recent . fileName = item . GetFileName ( ) ;
recent . reason = item . m_ItemType ;
recent . ioType = item . m_nAccessType ;
while ( m_Recent . Count ( ) > maxRecent )
{
m_Recent . Remove ( m_Recent . Head ( ) ) ;
}
m_Recent . AddToTail ( recent ) ;
m_History [ item . m_ItemType ] . m_LastFile = item . GetFileName ( ) ;
// Only care about time consuming synch or async blocking calls
if ( item . m_ItemType = = FILESYSTEM_BLOCKING_SYNCHRONOUS | |
item . m_ItemType = = FILESYSTEM_BLOCKING_ASYNCHRONOUS_BLOCK )
{
if ( item . m_flElapsed > cl_blocking_threshold . GetFloat ( ) )
{
SpewItem ( recent ) ;
}
}
}
list - > Reset ( ) ;
}
// Finished
list - > UnlockMutex ( ) ;
// Now draw some bars...
int itemHeight = ( vgui : : surface ( ) - > GetFontTall ( m_hFont ) + 2 ) ;
int y = 2 ;
int w = GetWide ( ) ;
DrawIOTime ( x , y , w , itemHeight , FILESYSTEM_BLOCKING_SYNCHRONOUS , " Synchronous " , Color ( 255 , 0 , 0 , 255 ) ) ;
y + = 2 * ( itemHeight + 2 ) ;
DrawIOTime ( x , y , w , itemHeight , FILESYSTEM_BLOCKING_ASYNCHRONOUS_BLOCK , " Async Block " , Color ( 255 , 100 , 0 , 255 ) ) ;
y + = 2 * ( itemHeight + 2 ) ;
DrawIOTime ( x , y , w , itemHeight , FILESYSTEM_BLOCKING_CALLBACKTIMING , " Callback " , Color ( 255 , 255 , 0 , 255 ) ) ;
y + = 2 * ( itemHeight + 2 ) ;
DrawIOTime ( x , y , w , itemHeight , FILESYSTEM_BLOCKING_ASYNCHRONOUS , " Asynchronous " , Color ( 0 , 255 , 0 , 255 ) ) ;
for ( i = 0 ; i < c ; + + i )
{
if ( m_History [ i ] . m_flCurrent > m_History [ i ] . m_flHistory )
{
m_History [ i ] . m_flHistory = m_History [ i ] . m_flCurrent ;
m_History [ i ] . m_flHistorySpike = m_History [ i ] . m_flCurrent ;
m_History [ i ] . m_flLatchTime = gpGlobals - > realtime ;
}
else
{
// After this long, start to decay the previous history value
if ( gpGlobals - > realtime > m_History [ i ] . m_flLatchTime + 1.0f )
{
m_History [ i ] . m_flHistory = m_History [ i ] . m_flHistory * IO_DECAY_FRAC + ( 1.0f - IO_DECAY_FRAC ) * m_History [ i ] . m_flCurrent ;
}
}
}
}
}
}
static ConVar cl_blocking_msec ( " cl_blocking_msec " , " 100 " , 0 , " Vertical scale of blocking graph in milliseconds " ) ;
static const char * GetBlockReason ( int reason )
{
switch ( reason )
{
case FILESYSTEM_BLOCKING_SYNCHRONOUS :
return " Synchronous " ;
case FILESYSTEM_BLOCKING_ASYNCHRONOUS :
return " Asynchronous " ;
case FILESYSTEM_BLOCKING_CALLBACKTIMING :
return " Async Callback " ;
case FILESYSTEM_BLOCKING_ASYNCHRONOUS_BLOCK :
return " Async Blocked " ;
}
return " ??? " ;
}
static const char * GetIOType ( int iotype )
{
if ( FileBlockingItem : : FB_ACCESS_APPEND = = iotype )
{
return " Append " ;
}
else if ( FileBlockingItem : : FB_ACCESS_CLOSE = = iotype )
{
return " Close " ;
}
else if ( FileBlockingItem : : FB_ACCESS_OPEN = = iotype )
{
return " Open " ;
}
else if ( FileBlockingItem : : FB_ACCESS_READ = = iotype )
{
return " Read " ;
}
else if ( FileBlockingItem : : FB_ACCESS_SIZE = = iotype )
{
return " Size " ;
}
else if ( FileBlockingItem : : FB_ACCESS_WRITE = = iotype )
{
return " Write " ;
}
return " ??? " ;
}
void CBlockingFileIOPanel : : SpewItem ( const RecentPeaks_t & item )
{
switch ( item . reason )
{
default :
Assert ( 0 ) ;
// break; -- intentionally fall through
case FILESYSTEM_BLOCKING_ASYNCHRONOUS :
case FILESYSTEM_BLOCKING_CALLBACKTIMING :
Msg ( " %8.3f %16.16s i/o [%6.6s] took %8.3f msec: %33.33s \n " ,
item . time ,
GetBlockReason ( item . reason ) ,
GetIOType ( item . ioType ) ,
item . elapsed * 1000.0f ,
item . fileName . String ( )
) ;
break ;
case FILESYSTEM_BLOCKING_SYNCHRONOUS :
case FILESYSTEM_BLOCKING_ASYNCHRONOUS_BLOCK :
Warning ( " %8.3f %16.16s i/o [%6.6s] took %8.3f msec: %33.33s \n " ,
item . time ,
GetBlockReason ( item . reason ) ,
GetIOType ( item . ioType ) ,
item . elapsed * 1000.0f ,
item . fileName . String ( )
) ;
break ;
}
}
void CBlockingFileIOPanel : : SpewRecent ( )
{
FOR_EACH_LL ( m_Recent , i )
{
const RecentPeaks_t & item = m_Recent [ i ] ;
SpewItem ( item ) ;
}
}
void CBlockingFileIOPanel : : DrawIOTime ( int x , int y , int w , int h , int slot , char const * label , const Color & clr )
{
float t = m_History [ slot ] . m_flCurrent ;
float history = m_History [ slot ] . m_flHistory ;
float latchedtime = m_History [ slot ] . m_flLatchTime ;
float historyspike = m_History [ slot ] . m_flHistorySpike ;
// 250 msec is considered a huge spike
float maxTime = cl_blocking_msec . GetFloat ( ) * 0.001f ;
if ( maxTime < 0.000001f )
return ;
float frac = clamp ( t / maxTime , 0.0f , 1.0f ) ;
float hfrac = clamp ( history / maxTime , 0.0f , 1.0f ) ;
float spikefrac = clamp ( historyspike / maxTime , 0.0f , 1.0f ) ;
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x + 2 , y + 1 ,
clr [ 0 ] , clr [ 1 ] , clr [ 2 ] , clr [ 3 ] ,
" %s " ,
label ) ;
int textWidth = 95 ;
x + = textWidth ;
w - = ( textWidth + 5 ) ;
int prevFileWidth = 140 ;
w - = prevFileWidth ;
bool bDrawHistorySpike = false ;
if ( m_History [ slot ] . m_LastFile . IsValid ( ) & &
( gpGlobals - > realtime < latchedtime + 10.0f ) )
{
bDrawHistorySpike = true ;
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x + w + 5 , y + 1 ,
255 , 255 , 255 , 200 , " [%8.3f ms] " , m_History [ slot ] . m_flHistorySpike * 1000.0f ) ;
g_pMatSystemSurface - > DrawColoredText ( m_hFont , x , y + h + 1 ,
255 , 255 , 255 , 200 , " %s " , m_History [ slot ] . m_LastFile . String ( ) ) ;
}
y + = 2 ;
h - = 4 ;
int barWide = ( int ) ( w * frac + 0.5f ) ;
int historyWide = ( int ) ( w * hfrac + 0.5f ) ;
int spikeWide = ( int ) ( w * spikefrac + 0.5f ) ;
int useWide = MAX ( barWide , historyWide ) ;
vgui : : surface ( ) - > DrawSetColor ( Color ( 0 , 0 , 0 , 31 ) ) ;
vgui : : surface ( ) - > DrawFilledRect ( x , y , x + w , y + h ) ;
vgui : : surface ( ) - > DrawSetColor ( Color ( 255 , 255 , 255 , 128 ) ) ;
vgui : : surface ( ) - > DrawOutlinedRect ( x , y , x + w , y + h ) ;
vgui : : surface ( ) - > DrawSetColor ( clr ) ;
vgui : : surface ( ) - > DrawFilledRect ( x + 1 , y + 1 , x + useWide , y + h - 1 ) ;
if ( bDrawHistorySpike )
{
vgui : : surface ( ) - > DrawSetColor ( Color ( 255 , 255 , 255 , 192 ) ) ;
vgui : : surface ( ) - > DrawFilledRect ( x + spikeWide , y + 1 , x + spikeWide + 1 , y + h - 1 ) ;
}
}
class CBlockingFileIO : public IShowBlockingPanel
{
private :
CBlockingFileIOPanel * ioPanel ;
public :
CBlockingFileIO ( void )
{
ioPanel = NULL ;
}
void Create ( vgui : : VPANEL parent )
{
ioPanel = new CBlockingFileIOPanel ( parent ) ;
}
void Destroy ( void )
{
if ( ioPanel )
{
ioPanel - > SetParent ( ( vgui : : Panel * ) NULL ) ;
2022-03-01 23:00:42 +03:00
delete ioPanel ;
2020-04-22 12:56:21 -04:00
}
}
void Spew ( )
{
if ( ioPanel )
{
ioPanel - > SpewRecent ( ) ;
}
}
} ;
static CBlockingFileIO g_IOPanel ;
IShowBlockingPanel * iopanel = ( IShowBlockingPanel * ) & g_IOPanel ;
CON_COMMAND ( spewblocking , " Spew current blocking file list. " )
{
g_IOPanel . Spew ( ) ;
}
# endif // TRACK_BLOCKING_IO