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.

1433 lines
41 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #include "cbase.h"
  7. #include "ifpspanel.h"
  8. #include <vgui_controls/Panel.h>
  9. #include "view.h"
  10. #include <vgui/IVGui.h>
  11. #include "VGuiMatSurface/IMatSystemSurface.h"
  12. #include <vgui_controls/Controls.h>
  13. #include <vgui/ISurface.h>
  14. #include <vgui/IScheme.h>
  15. #include <vgui/IPanel.h>
  16. #include "materialsystem/imaterialsystemhardwareconfig.h"
  17. #include "filesystem.h"
  18. #include "steam/steam_api.h"
  19. #include "../common/xbox/xboxstubs.h"
  20. #include "engineinterface.h"
  21. #include "tier0/perfstats.h"
  22. #include "tier0/cpumonitoring.h"
  23. #ifdef PORTAL
  24. #include "c_prop_portal.h"
  25. #include "iextpropportallocator.h"
  26. #include "matchmaking/imatchframework.h"
  27. #endif
  28. // memdbgon must be the last include file in a .cpp file!!!
  29. #include "tier0/memdbgon.h"
  30. static ConVar cl_showfps( "cl_showfps", "0", FCVAR_RELEASE, "Draw fps meter (1 = fps, 2 = smooth, 3 = server, 4 = Show+LogToFile, 5 = Thread and wait times +10 = detailed )" );
  31. static ConVar cl_showpos( "cl_showpos", "0", FCVAR_RELEASE, "Draw current position at top of screen" );
  32. static ConVar cl_showbattery( "cl_showbattery", "0", 0, "Draw current battery level at top of screen when on battery power" );
  33. static ConVar cl_showfps5_disp_time( "cl_showfps5_disp_time", "1.0", 0, "Time interval (s) at which thread and wait times are sampled and display is updated" );
  34. static ConVar cl_showfps5_btlneck_disp_time( "cl_showfps5_btlneck_disp_time", "5.0", 0, "Time interval (s) for which main/render/gpu bottleneck times are displayed" );
  35. extern unsigned int g_nNumBonesSetupBlendingRulesOnly;
  36. extern unsigned int g_nNumBonesSetupAll;
  37. ConVar cl_countbones( "cl_countbones", "0", FCVAR_CHEAT, "" );
  38. #ifdef _GAMECONSOLE
  39. static ConVar cl_showlowmemory( "cl_showlowmemory", "0", FCVAR_CHEAT, "Set to N to display a warning message if we have less than N MB of free memory (0 disables)." );
  40. #endif
  41. struct PerfStatRecord
  42. {
  43. float m_lastUpdateTime;
  44. int m_fps;
  45. float m_mainThreadTime;
  46. float m_mainThreadWaitTime;
  47. float m_renderThreadTime;
  48. float m_renderThreadWaitTime;
  49. };
  50. template < int kNumSamples >
  51. class FpsSpikesTracker_t
  52. {
  53. public:
  54. ApplicationPerformanceCountersInfo_t m_Samples[kNumSamples];
  55. ApplicationPerformanceCountersInfo_t m_min, m_max, m_avg, m_cur;
  56. int m_nSampleIdx;
  57. void AddSample( const ApplicationPerformanceCountersInfo_t& x )
  58. {
  59. m_Samples[ m_nSampleIdx ] = m_cur = x;
  60. m_nSampleIdx = ( m_nSampleIdx + 1 ) % kNumSamples;
  61. RecomputeData();
  62. }
  63. void RecomputeField( float ApplicationPerformanceCountersInfo_t::*pfl, ApplicationPerformanceCountersInfo_t const &x )
  64. {
  65. m_min.*pfl = MIN( m_min.*pfl, x.*pfl );
  66. m_max.*pfl = MAX( m_max.*pfl, x.*pfl );
  67. m_avg.*pfl += x.*pfl / kNumSamples;
  68. }
  69. void RecomputeData()
  70. {
  71. V_memset( &m_avg, 0, sizeof( m_avg ) );
  72. m_min = m_max = m_Samples[0];
  73. for ( int k = 0; k < kNumSamples; ++ k )
  74. {
  75. RecomputeField( &ApplicationPerformanceCountersInfo_t::msMain, m_Samples[k] );
  76. RecomputeField( &ApplicationPerformanceCountersInfo_t::msMST, m_Samples[k] );
  77. RecomputeField( &ApplicationPerformanceCountersInfo_t::msGPU, m_Samples[k] );
  78. RecomputeField( &ApplicationPerformanceCountersInfo_t::msFlip, m_Samples[k] );
  79. RecomputeField( &ApplicationPerformanceCountersInfo_t::msTotal, m_Samples[k] );
  80. }
  81. }
  82. };
  83. extern bool g_bDisplayParticlePerformance;
  84. int GetParticlePerformance();
  85. #define PERF_HISTOGRAM_BUCKET_SIZE 60
  86. //-----------------------------------------------------------------------------
  87. // Purpose: Framerate indicator panel
  88. //-----------------------------------------------------------------------------
  89. class CFPSPanel : public vgui::Panel
  90. {
  91. DECLARE_CLASS_SIMPLE( CFPSPanel, vgui::Panel );
  92. public:
  93. explicit CFPSPanel( vgui::VPANEL parent );
  94. virtual ~CFPSPanel( void );
  95. virtual void ApplySchemeSettings(vgui::IScheme *pScheme);
  96. virtual void Paint();
  97. virtual void OnTick( void );
  98. virtual void DumpStats();
  99. virtual bool ShouldDraw( void );
  100. protected:
  101. MESSAGE_FUNC_INT_INT( OnScreenSizeChanged, "OnScreenSizeChanged", oldwide, oldtall );
  102. private:
  103. void ComputeSize( void );
  104. void InitAverages()
  105. {
  106. m_AverageFPS = -1;
  107. m_lastRealTime = -1;
  108. m_high = -1;
  109. m_low = -1;
  110. memset( m_pServerTimes, 0, sizeof(m_pServerTimes) );
  111. memset( m_perfStats, 0, sizeof( m_perfStats ) );
  112. }
  113. enum { SERVER_TIME_HISTORY = 32 };
  114. vgui::HFont m_hFont;
  115. float m_AverageFPS;
  116. float m_lastRealTime;
  117. float m_pServerTimes[SERVER_TIME_HISTORY];
  118. int m_nServerTimeIndex;
  119. int m_high;
  120. int m_low;
  121. bool m_bLastDraw;
  122. int m_nLinesNeeded;
  123. TimedEvent m_tLogTimer;
  124. FileHandle_t m_fhLog;
  125. char m_szLevelname[32];
  126. int m_nNumFramesTotal;
  127. int m_nNumFramesBucket[PERF_HISTOGRAM_BUCKET_SIZE];
  128. int m_BatteryPercent;
  129. float m_lastBatteryPercent;
  130. PerfStatRecord m_perfStats[4];
  131. };
  132. //-----------------------------------------------------------------------------
  133. // Purpose:
  134. // Input : *parent -
  135. //-----------------------------------------------------------------------------
  136. CFPSPanel::CFPSPanel( vgui::VPANEL parent ) : BaseClass( NULL, "CFPSPanel" )
  137. {
  138. memset( m_pServerTimes, 0, sizeof(m_pServerTimes) );
  139. m_nServerTimeIndex = 0;
  140. SetParent( parent );
  141. SetVisible( false );
  142. SetCursor( 0 );
  143. SetFgColor( Color( 0, 0, 0, 255 ) );
  144. SetPaintBackgroundEnabled( false );
  145. m_hFont = 0;
  146. m_nLinesNeeded = 5;
  147. m_BatteryPercent = -1;
  148. m_lastBatteryPercent = -1.0f;
  149. ComputeSize();
  150. vgui::ivgui()->AddTickSignal( GetVPanel(), 250 );
  151. m_bLastDraw = false;
  152. m_tLogTimer.Init( 6 );
  153. m_fhLog = FILESYSTEM_INVALID_HANDLE;
  154. m_nNumFramesTotal = 0;
  155. memset( m_nNumFramesBucket, 0, sizeof(m_nNumFramesBucket) );
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose:
  159. //-----------------------------------------------------------------------------
  160. CFPSPanel::~CFPSPanel( void )
  161. {
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose: Updates panel to handle the new screen size
  165. //-----------------------------------------------------------------------------
  166. void CFPSPanel::OnScreenSizeChanged(int iOldWide, int iOldTall)
  167. {
  168. BaseClass::OnScreenSizeChanged(iOldWide, iOldTall);
  169. ComputeSize();
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose: Computes panel's desired size and position
  173. //-----------------------------------------------------------------------------
  174. void CFPSPanel::ComputeSize( void )
  175. {
  176. int wide, tall;
  177. vgui::ipanel()->GetSize(GetVParent(), wide, tall );
  178. int x = 0;;
  179. int y = 0;
  180. if ( IsGameConsole() )
  181. {
  182. x += XBOX_MINBORDERSAFE * wide;
  183. y += XBOX_MINBORDERSAFE * tall;
  184. }
  185. SetPos( x, y );
  186. SetSize( wide, ( m_nLinesNeeded + 2 ) * vgui::surface()->GetFontTall( m_hFont ) + 4 );
  187. }
  188. void CFPSPanel::ApplySchemeSettings(vgui::IScheme *pScheme)
  189. {
  190. BaseClass::ApplySchemeSettings(pScheme);
  191. #if defined( _GAMECONSOLE )
  192. m_hFont = pScheme->GetFont( "CloseCaption_Normal" );
  193. #else
  194. m_hFont = pScheme->GetFont( "MenuLarge" );
  195. #endif
  196. Assert( m_hFont );
  197. ComputeSize();
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose:
  201. //-----------------------------------------------------------------------------
  202. void CFPSPanel::OnTick( void )
  203. {
  204. bool bVisible = ShouldDraw();
  205. if ( IsVisible() != bVisible )
  206. {
  207. SetVisible( bVisible );
  208. }
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose:
  212. // Output : Returns true on success, false on failure.
  213. //-----------------------------------------------------------------------------
  214. bool CFPSPanel::ShouldDraw( void )
  215. {
  216. if ( g_bDisplayParticlePerformance )
  217. return true;
  218. if ( ( !cl_showfps.GetInt( ) || ( gpGlobals->absoluteframetime <= 0 ) ) && !cl_showpos.GetInt( )
  219. #ifdef CSTRIKE15
  220. && !cl_countbones.GetBool()
  221. #endif
  222. #ifdef _GAMECONSOLE
  223. && !cl_showlowmemory.GetInt()
  224. #endif
  225. )
  226. {
  227. m_bLastDraw = false;
  228. return false;
  229. }
  230. if ( !m_bLastDraw )
  231. {
  232. m_bLastDraw = true;
  233. InitAverages();
  234. }
  235. return true;
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose:
  239. //-----------------------------------------------------------------------------
  240. void GetFPSColor( int nFps, unsigned char ucColor[3] )
  241. {
  242. ucColor[0] = 255; ucColor[1] = 0; ucColor[2] = 0;
  243. int nFPSThreshold1 = 20;
  244. int nFPSThreshold2 = 15;
  245. if ( IsPC() )
  246. {
  247. nFPSThreshold1 = 60;
  248. nFPSThreshold2 = 30;
  249. }
  250. else
  251. {
  252. if ( engine->IsSplitScreenActive() )
  253. {
  254. nFPSThreshold1 = 19; //20; // 19 shows up commonly when testing on the 360
  255. nFPSThreshold2 = 15;
  256. }
  257. else
  258. {
  259. nFPSThreshold1 = 59; //30; 29 shows up commonly when testing on the 360
  260. nFPSThreshold2 = 29;
  261. }
  262. }
  263. if ( nFps >= nFPSThreshold1 )
  264. {
  265. ucColor[0] = 10;
  266. ucColor[1] = 200;
  267. }
  268. else if ( nFps >= nFPSThreshold2 )
  269. {
  270. ucColor[0] = 220;
  271. ucColor[1] = 220;
  272. }
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose: Set the color appropriately based on the CPU's frequency percentage
  276. //-----------------------------------------------------------------------------
  277. void GetCPUColor( float cpuPercentage, unsigned char ucColor[3] )
  278. {
  279. // These colors are for poor CPU performance
  280. ucColor[0] = 255; ucColor[1] = 0; ucColor[2] = 0;
  281. if ( cpuPercentage >= kCPUMonitoringWarning1 )
  282. {
  283. // Excellent CPU performance
  284. ucColor[0] = 10;
  285. ucColor[1] = 200;
  286. }
  287. else if ( cpuPercentage >= kCPUMonitoringWarning2 )
  288. {
  289. // Medium CPU performance
  290. ucColor[0] = 220;
  291. ucColor[1] = 220;
  292. }
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Purpose:
  296. // Input :
  297. //-----------------------------------------------------------------------------
  298. void CFPSPanel::Paint()
  299. {
  300. int i = 0;
  301. int x = 2;
  302. int lineHeight = vgui::surface()->GetFontTall( m_hFont ) + 1;
  303. if ( g_bDisplayParticlePerformance )
  304. {
  305. int nPerf = GetParticlePerformance();
  306. if ( nPerf )
  307. {
  308. unsigned char ucColor[3]={ 0,255,0 };
  309. g_pMatSystemSurface->DrawColoredText(
  310. m_hFont, x, 42,
  311. ucColor[0], ucColor[1], ucColor[2],
  312. 255, "Particle Performance Metric : %d", (nPerf+50)/100 );
  313. }
  314. }
  315. float realFrameTime = gpGlobals->realtime - m_lastRealTime;
  316. int nFPSMode = cl_showfps.GetInt()%10;
  317. ApplicationPerformanceCountersInfo_t apci = { 0, 0, 0, 0, 0 };
  318. ApplicationInstantCountersInfo_t aici = { 0, 0 };
  319. uint32 uiAPCI = 0;
  320. if ( cl_showfps.GetInt() >= 10 )
  321. {
  322. uiAPCI = g_pMaterialSystem->GetFrameTimestamps( apci, aici );
  323. }
  324. apci.msFlip = realFrameTime * 1000.0f;
  325. if ( nFPSMode == 3 && (!uiAPCI))
  326. {
  327. float flServerTime = engine->GetServerSimulationFrameTime();
  328. if ( flServerTime != 0.0f )
  329. {
  330. m_nServerTimeIndex = ( m_nServerTimeIndex + 1 ) & ( SERVER_TIME_HISTORY - 1 );
  331. m_pServerTimes[ m_nServerTimeIndex ] = flServerTime;
  332. }
  333. flServerTime = m_pServerTimes[ m_nServerTimeIndex ];
  334. float flTotalTime = 0.0f;
  335. float flPeakTime = 0.0f;
  336. for ( int i = 0; i < SERVER_TIME_HISTORY; ++i )
  337. {
  338. flTotalTime += m_pServerTimes[i];
  339. if ( flPeakTime < m_pServerTimes[i] )
  340. {
  341. flPeakTime = m_pServerTimes[i];
  342. }
  343. }
  344. flTotalTime /= SERVER_TIME_HISTORY;
  345. unsigned char ucColor[3];
  346. int nFps = static_cast<int>( 1.0f / ( flServerTime * 0.001f ) );
  347. GetFPSColor( nFps, ucColor );
  348. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2, ucColor[0], ucColor[1], ucColor[2], 255,
  349. "server %5.1f ms curr, %5.1f ave, %5.1f peak", flServerTime, flTotalTime, flPeakTime );
  350. }
  351. else if ( nFPSMode == 4 && m_lastRealTime > 0.0f && realFrameTime > 0.0f && engine->IsInGame() )
  352. {
  353. char levelName[32];
  354. Q_strncpy( levelName, engine->GetLevelNameShort(), sizeof( levelName ) );
  355. if ( Q_strcmp( m_szLevelname, levelName ) && m_fhLog != FILESYSTEM_INVALID_HANDLE )
  356. {
  357. DumpStats();
  358. }
  359. float flServerTime = engine->GetServerSimulationFrameTime();
  360. if ( flServerTime != 0.0f )
  361. {
  362. m_nServerTimeIndex = ( m_nServerTimeIndex + 1 ) & ( SERVER_TIME_HISTORY - 1 );
  363. m_pServerTimes[ m_nServerTimeIndex ] = flServerTime;
  364. }
  365. flServerTime = m_pServerTimes[ m_nServerTimeIndex ];
  366. const float NewWeight = 0.1f;
  367. float NewFrame = 1.0f / realFrameTime;
  368. if ( m_AverageFPS <= 0.0f )
  369. {
  370. m_AverageFPS = NewFrame;
  371. }
  372. else
  373. {
  374. m_AverageFPS *= ( 1.0f - NewWeight ) ;
  375. m_AverageFPS += ( ( NewFrame ) * NewWeight );
  376. }
  377. int nAvgFps = static_cast<int>( m_AverageFPS );
  378. float flAverageMS = 1000.0f / m_AverageFPS;
  379. float flFrameMS = realFrameTime * 1000.0f;
  380. int nFrameFps = static_cast<int>( 1.0f / realFrameTime );
  381. float flCurTime = gpGlobals->frametime;
  382. m_nNumFramesTotal++;
  383. int nBucket = MIN ( PERF_HISTOGRAM_BUCKET_SIZE, MAX ( 0, nFrameFps ) );
  384. m_nNumFramesBucket[nBucket]++;
  385. unsigned char ucColor[3];
  386. GetFPSColor( nAvgFps, ucColor );
  387. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2, ucColor[0], ucColor[1], ucColor[2], 255,
  388. "Avg FPS %3i, Frame MS %5.1f, Frame Server MS %5.1f", nAvgFps, flFrameMS, flServerTime );
  389. if ( m_fhLog == FILESYSTEM_INVALID_HANDLE )
  390. {
  391. Q_strncpy( m_szLevelname, levelName, sizeof( m_szLevelname ) );
  392. // Maximum 360 file name length
  393. char fileString[42];
  394. Q_snprintf( fileString, 42, "prof_%s.csv", m_szLevelname );
  395. m_fhLog = g_pFullFileSystem->Open( fileString, "w", "GAME" );
  396. g_pFullFileSystem->FPrintf( m_fhLog, "Time,Player 1 Position,Player 2 Position,Smooth FPS,Frame FPS,Smooth MS,Frame MS,Server Frame MS\n");
  397. }
  398. if ( ( m_tLogTimer.NextEvent( flCurTime ) && nFrameFps < 28.0f ) || nFrameFps < 15.0f )
  399. {
  400. // We always print data for two players so make sure the array always has room for at least two players.
  401. Vector vecOrigin[MAX_SPLITSCREEN_PLAYERS > 2 ? MAX_SPLITSCREEN_PLAYERS : 2] = {};
  402. QAngle angles[MAX_SPLITSCREEN_PLAYERS > 2 ? MAX_SPLITSCREEN_PLAYERS : 2] = {};
  403. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  404. {
  405. vecOrigin[hh] = MainViewOrigin(hh);
  406. angles[hh]= MainViewAngles(hh);
  407. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(hh);
  408. if ( pPlayer )
  409. {
  410. vecOrigin[hh] = pPlayer->GetAbsOrigin();
  411. angles[hh] = pPlayer->GetAbsAngles();
  412. }
  413. }
  414. char outputString[256];
  415. 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",
  416. gpGlobals->curtime,vecOrigin[0].x, vecOrigin[0].y, vecOrigin[0].z, angles[0].x, angles[0].y, angles[0].z,
  417. vecOrigin[1].x, vecOrigin[1].y, vecOrigin[1].z, angles[1].x, angles[1].y, angles[1].z, nAvgFps,
  418. nFrameFps, flAverageMS, flFrameMS, flServerTime );
  419. if ( m_fhLog != FILESYSTEM_INVALID_HANDLE )
  420. {
  421. g_pFullFileSystem->FPrintf( m_fhLog, "%s", outputString );
  422. }
  423. }
  424. }
  425. else if ( uiAPCI )
  426. {
  427. // Do not render old cl_showfps 2 if there's detailed perf information
  428. -- i;
  429. }
  430. else if ( nFPSMode && realFrameTime > 0.0 )
  431. {
  432. if ( nFPSMode == 5 )
  433. {
  434. char *perfStatTitle[4] =
  435. {
  436. "Current",
  437. "Main ",
  438. "Render ",
  439. "GPU "
  440. };
  441. static Color perfStatClr[4];
  442. Color red( 255, 0, 0, 255 );
  443. Color blue( 0, 0, 255, 255 );
  444. Color black(0, 0, 0, 255 );
  445. float dt = gpGlobals->realtime - m_perfStats[0].m_lastUpdateTime;
  446. float eps = 0.015; // ms
  447. if ( dt >= cl_showfps5_disp_time.GetFloat() )
  448. {
  449. m_perfStats[0].m_lastUpdateTime = gpGlobals->realtime;
  450. m_perfStats[0].m_mainThreadTime = g_PerfStats.m_Slots[PERF_STATS_SLOT_MAINTHREAD].m_PrevFrameTime.GetMillisecondsF();
  451. m_perfStats[0].m_mainThreadWaitTime = g_PerfStats.m_Slots[PERF_STATS_SLOT_END_FRAME].m_PrevFrameTime.GetMillisecondsF();
  452. m_perfStats[0].m_renderThreadTime = g_PerfStats.m_Slots[PERF_STATS_SLOT_RENDERTHREAD].m_PrevFrameTime.GetMillisecondsF();
  453. m_perfStats[0].m_renderThreadWaitTime = g_PerfStats.m_Slots[PERF_STATS_SLOT_FORCE_HARDWARE_SYNC].m_PrevFrameTime.GetMillisecondsF();
  454. if ( m_perfStats[0].m_mainThreadWaitTime < eps )
  455. {
  456. // We are main thread bound
  457. if ( m_perfStats[1].m_mainThreadTime < m_perfStats[0].m_mainThreadTime)
  458. {
  459. m_perfStats[1] = m_perfStats[0];
  460. }
  461. }
  462. else if ( m_perfStats[0].m_renderThreadWaitTime < eps )
  463. {
  464. // We are render thread bound
  465. if ( m_perfStats[2].m_renderThreadTime < m_perfStats[0].m_renderThreadTime )
  466. {
  467. m_perfStats[2] = m_perfStats[0];
  468. }
  469. }
  470. else
  471. {
  472. // We are gpu bound
  473. if ( m_perfStats[3].m_renderThreadWaitTime < m_perfStats[0].m_renderThreadWaitTime )
  474. {
  475. m_perfStats[3] = m_perfStats[0];
  476. }
  477. }
  478. // Determine perf stat clrs
  479. perfStatClr[0] = black;
  480. if ( m_perfStats[1].m_lastUpdateTime > m_perfStats[2].m_lastUpdateTime )
  481. {
  482. if ( m_perfStats[1].m_lastUpdateTime > m_perfStats[3].m_lastUpdateTime )
  483. {
  484. perfStatClr[1] = red;
  485. perfStatClr[2] = blue;
  486. perfStatClr[3] = blue;
  487. }
  488. else
  489. {
  490. perfStatClr[3] = red;
  491. perfStatClr[1] = blue;
  492. perfStatClr[2] = blue;
  493. }
  494. }
  495. else
  496. {
  497. perfStatClr[1] = blue;
  498. if ( m_perfStats[3].m_lastUpdateTime > m_perfStats[2].m_lastUpdateTime )
  499. {
  500. perfStatClr[3] = red;
  501. perfStatClr[2] = blue;
  502. }
  503. else
  504. {
  505. perfStatClr[2] = red;
  506. perfStatClr[3] = blue;
  507. }
  508. }
  509. }
  510. // Reset bottlenecks if time is up
  511. for ( int i = 1; i < 4; i ++)
  512. {
  513. dt = gpGlobals->realtime - m_perfStats[i].m_lastUpdateTime;
  514. if ( dt >= cl_showfps5_btlneck_disp_time.GetFloat() )
  515. {
  516. memset( &m_perfStats[i], 0, sizeof(m_perfStats[i]) );
  517. }
  518. }
  519. int iy = 2;
  520. g_pMatSystemSurface->DisableClipping( true );
  521. for ( int i = 0; i < 4; i++)
  522. {
  523. int fps = 0;
  524. if ( m_perfStats[i].m_mainThreadTime > 0.0f )
  525. {
  526. fps = (int)(1000.0f / m_perfStats[i].m_mainThreadTime);
  527. }
  528. char buff[256];
  529. Q_snprintf( buff, 256, "%s: Main: %6.2f, MainWt: %6.2f, Rdr: %6.2f, RdrWt: %6.2f (%3d fps) ",
  530. perfStatTitle[i],
  531. m_perfStats[i].m_mainThreadTime, m_perfStats[i].m_mainThreadWaitTime,
  532. m_perfStats[i].m_renderThreadTime, m_perfStats[i].m_renderThreadWaitTime, fps );
  533. int len = g_pMatSystemSurface->DrawTextLen( m_hFont, buff );
  534. int ht = 15;
  535. g_pMatSystemSurface->DrawSetColor(0xff, 0xff, 0xff, 192 );
  536. g_pMatSystemSurface->DrawFilledRect( x, iy, x + len, iy + ht );
  537. g_pMatSystemSurface->DrawColoredText( m_hFont, x, iy, perfStatClr[i].r(), perfStatClr[i].g(), perfStatClr[i].b(), perfStatClr[i].a(), buff );
  538. iy += ht;
  539. }
  540. g_pMatSystemSurface->DisableClipping( false );
  541. }
  542. else if ( m_lastRealTime != -1.0f )
  543. {
  544. int nFps = -1;
  545. unsigned char ucColor[3];
  546. if ( nFPSMode == 2 )
  547. {
  548. const float NewWeight = 0.1f;
  549. float NewFrame = 1.0f / realFrameTime;
  550. // If we're just below an integer boundary, we're good enough to call ourselves good WRT to coloration
  551. if ( (int)(NewFrame + 0.05) > (int )( NewFrame ) )
  552. {
  553. NewFrame = ceil( NewFrame );
  554. }
  555. if ( m_AverageFPS < 0.0f )
  556. {
  557. m_AverageFPS = NewFrame;
  558. m_high = (int)m_AverageFPS;
  559. m_low = (int)m_AverageFPS;
  560. }
  561. else
  562. {
  563. m_AverageFPS *= ( 1.0f - NewWeight ) ;
  564. m_AverageFPS += ( ( NewFrame ) * NewWeight );
  565. }
  566. int NewFrameInt = (int)NewFrame;
  567. if( NewFrameInt < m_low ) m_low = NewFrameInt;
  568. if( NewFrameInt > m_high ) m_high = NewFrameInt;
  569. nFps = static_cast<int>( m_AverageFPS );
  570. float averageMS = 1000.0f / m_AverageFPS;
  571. float frameMS = realFrameTime * 1000.0f;
  572. GetFPSColor( nFps, ucColor );
  573. 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() );
  574. }
  575. else
  576. {
  577. m_AverageFPS = -1;
  578. float flFps = ( 1.0f / realFrameTime );
  579. // If we're just below an integer boundary, we're good enough to call ourselves good WRT to coloration
  580. if ( (int)(flFps + 0.05) > (int )( flFps ) )
  581. {
  582. flFps = ceil( flFps );
  583. }
  584. nFps = static_cast<int>( flFps );
  585. GetFPSColor( nFps, ucColor );
  586. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2, ucColor[0], ucColor[1], ucColor[2], 255, "%3i fps on %s", nFps, engine->GetLevelName() );
  587. }
  588. const CPUFrequencyResults frequency = GetCPUFrequencyResults();
  589. double currentTime = Plat_FloatTime();
  590. const double displayTime = 5.0f; // Display frequency results for this long.
  591. if ( frequency.m_GHz > 0 && frequency.m_timeStamp + displayTime > currentTime )
  592. {
  593. // Optionally print out the CPU frequency monitoring data.
  594. GetCPUColor( frequency.m_percentage, ucColor );
  595. g_pMatSystemSurface->DrawColoredText( m_hFont, x, lineHeight + 2, ucColor[0], ucColor[1], ucColor[2], 255, "CPU frequency percent: %3.1f%% Min percent: %3.1f%%", frequency.m_percentage, frequency.m_lowestPercentage );
  596. }
  597. }
  598. }
  599. else if ( m_fhLog != FILESYSTEM_INVALID_HANDLE )
  600. {
  601. DumpStats();
  602. }
  603. m_lastRealTime = gpGlobals->realtime;
  604. if ( uiAPCI )
  605. {
  606. static FpsSpikesTracker_t<100> s_tracker;
  607. s_tracker.AddSample( apci );
  608. struct FpsDetail_t { char *sz; float fl; };
  609. static ConVarRef mat_queue_mode( "mat_queue_mode" );
  610. static ConVarRef mat_vsync( "mat_vsync" );
  611. int nMSTmode = mat_queue_mode.GetInt();
  612. char *szMSTname = ( char * )( (nMSTmode == 2) ? "QMS2" : ( (nMSTmode == 1) ? "QMS1" : ( ( nMSTmode == -1 ) ? "QMS-" : "QMS0" ) ) );
  613. char *szFlipName = ( char * ) ( mat_vsync.GetBool() ? "TOTAL" : "TOTAL" );
  614. FpsDetail_t arrGrid[6][5] = {
  615. { { "PERF/ms",0 }, { "MAX",0 }, { "AVG",0 }, { "MIN",0 }, { "CUR",0 } },
  616. { { "MAIN",0 }, { 0,s_tracker.m_max.msMain }, { 0,s_tracker.m_avg.msMain }, { 0,s_tracker.m_min.msMain }, { 0,s_tracker.m_cur.msMain } },
  617. { { szMSTname,0 }, { 0,s_tracker.m_max.msMST }, { 0,s_tracker.m_avg.msMST }, { 0,s_tracker.m_min.msMST }, { 0,s_tracker.m_cur.msMST } },
  618. { { "GPU",0 }, { 0,s_tracker.m_max.msGPU }, { 0,s_tracker.m_avg.msGPU }, { 0,s_tracker.m_min.msGPU }, { 0,s_tracker.m_cur.msGPU } },
  619. { { szFlipName,0 }, { 0,s_tracker.m_max.msFlip }, { 0,s_tracker.m_avg.msFlip }, { 0,s_tracker.m_min.msFlip }, { 0,s_tracker.m_cur.msFlip } },
  620. { { "TOTAL",0 }, { 0,s_tracker.m_max.msTotal }, { 0,s_tracker.m_avg.msTotal }, { 0,s_tracker.m_min.msTotal }, { 0,s_tracker.m_cur.msTotal } }
  621. };
  622. int numRowsToDisplay = ( cl_showfps.GetInt() >= 20 ? 6 : 5 );
  623. if (IsPS3() || IsX360() )
  624. {
  625. if(cl_showfps.GetInt() > 100)
  626. {
  627. numRowsToDisplay = 2;
  628. int32 row = cl_showfps.GetInt() - 100;
  629. row = MAX(row,1);
  630. row = MIN(row,3);
  631. V_memcpy(arrGrid[1], arrGrid[row], sizeof(arrGrid[1]));
  632. }
  633. }
  634. for ( int iRow = 0; iRow < numRowsToDisplay; ++ iRow )
  635. {
  636. if ( iRow == 2 && !nMSTmode )
  637. continue;
  638. i++;
  639. for ( int iCol = 0; iCol < 5; ++ iCol )
  640. {
  641. unsigned char ucColor[3] = { 255, 255, 255 };
  642. if ( !arrGrid[iRow][iCol].sz ) GetFPSColor( ( arrGrid[iRow][iCol].fl > 0.1 ) ? ( 1000.0f / arrGrid[iRow][iCol].fl ) : 1000, ucColor );
  643. g_pMatSystemSurface->DrawColoredText( m_hFont, x + iCol*lineHeight*6, 2 + i * lineHeight,
  644. ucColor[0], ucColor[1], ucColor[2], 255,
  645. arrGrid[iRow][iCol].sz ? arrGrid[iRow][iCol].sz : (char*)"%0.2f",
  646. arrGrid[iRow][iCol].fl );
  647. }
  648. if ( iRow == 0 )
  649. {
  650. g_pMatSystemSurface->DrawColoredText( m_hFont, x + 5*lineHeight*6, 2 + i * lineHeight,
  651. 255, 255, 255, 255,
  652. "%s @ %dfps",
  653. engine->GetLevelNameShort(),
  654. ( s_tracker.m_avg.msFlip > 0.1 ) ? int( ( 1000.0f / s_tracker.m_avg.msFlip ) + 0.2 ) : 1000 );
  655. }
  656. }
  657. i++;
  658. }
  659. if( aici.m_nDeferredWordsAllocated )
  660. {
  661. g_pMatSystemSurface->DrawColoredText( m_hFont, x + 6*lineHeight*6, 2 + 2 * lineHeight, 255, 255, 255, 255,
  662. "defer %u kB", ( aici.m_nDeferredWordsAllocated + 255 ) / 256 );
  663. }
  664. int nShowPosMode = cl_showpos.GetInt();
  665. if ( nShowPosMode > 0 )
  666. {
  667. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  668. {
  669. Vector vecOrigin = MainViewOrigin(hh);
  670. QAngle angles = MainViewAngles(hh);
  671. Vector vel( 0, 0, 0 );
  672. char szName[ 32 ] = { 0 };
  673. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(hh);
  674. if ( pPlayer )
  675. {
  676. Q_strncpy( szName, pPlayer->GetPlayerName(), sizeof( szName ) );
  677. vel = pPlayer->GetLocalVelocity();
  678. }
  679. if ( !engine->IsConnected() )
  680. {
  681. V_sprintf_safe( szName, "unconnected" );
  682. }
  683. else if ( engine->GetDemoPlaybackParameters() && engine->GetDemoPlaybackParameters()->m_bAnonymousPlayerIdentity )
  684. {
  685. V_sprintf_safe( szName, "unknown" );
  686. }
  687. if ( nShowPosMode == 2 && pPlayer )
  688. {
  689. vecOrigin = pPlayer->GetAbsOrigin();
  690. angles = pPlayer->GetAbsAngles();
  691. }
  692. if ( uiAPCI )
  693. {
  694. i++;
  695. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2+ i * lineHeight,
  696. 255, 255, 255, 255,
  697. "pos: %.02f %.02f %.02f (%s)",
  698. vecOrigin.x, vecOrigin.y, vecOrigin.z, szName );
  699. i++;
  700. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight,
  701. 255, 255, 255, 255,
  702. "ang: %.02f %.02f %.02f (vel: %.2f)",
  703. angles.x, angles.y, angles.z, vel.Length2D() );
  704. }
  705. else
  706. {
  707. i++;
  708. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight,
  709. 255, 255, 255, 255,
  710. "name: %s", szName );
  711. i++;
  712. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2+ i * lineHeight,
  713. 255, 255, 255, 255,
  714. "pos: %.02f %.02f %.02f",
  715. vecOrigin.x, vecOrigin.y, vecOrigin.z );
  716. i++;
  717. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight,
  718. 255, 255, 255, 255,
  719. "ang: %.02f %.02f %.02f",
  720. angles.x, angles.y, angles.z );
  721. i++;
  722. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight,
  723. 255, 255, 255, 255,
  724. "vel: %.2f",
  725. vel.Length2D() );
  726. }
  727. }
  728. #ifdef PORTAL
  729. if ( uiAPCI )
  730. {
  731. static IPortalServerDllPropPortalLocator *s_pPortalLocator;
  732. if ( !s_pPortalLocator )
  733. {
  734. if ( g_pMatchFramework )
  735. {
  736. s_pPortalLocator = ( IPortalServerDllPropPortalLocator * )
  737. g_pMatchFramework->GetMatchExtensions()->GetRegisteredExtensionInterface( IEXTPROPPORTALLOCATOR_INTERFACE_NAME );
  738. }
  739. }
  740. if ( s_pPortalLocator )
  741. {
  742. CUtlVector < IPortalServerDllPropPortalLocator::PortalInfo_t > arrPortals;
  743. arrPortals.EnsureCapacity( 4 );
  744. s_pPortalLocator->LocateAllPortals( arrPortals );
  745. i++;
  746. for ( int j = 0; j < arrPortals.Count(); ++ j )
  747. {
  748. IPortalServerDllPropPortalLocator::PortalInfo_t const &pi = arrPortals[j];
  749. i++;
  750. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight,
  751. 255, 255, 255, 255,
  752. "P %d %d %.02f %.02f %.02f %.02f %.02f %.02f",
  753. pi.iLinkageGroupId, pi.nPortal,
  754. pi.vecOrigin.x, pi.vecOrigin.y, pi.vecOrigin.z,
  755. pi.vecAngle.x, pi.vecAngle.y, pi.vecAngle.z
  756. );
  757. }
  758. }
  759. }
  760. #endif
  761. }
  762. #ifdef CSTRIKE15
  763. if ( cl_countbones.GetBool() )
  764. {
  765. i++;
  766. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight,
  767. 255, 255, 255, 255,
  768. "All computed bones: %i",
  769. g_nNumBonesSetupAll );
  770. i++;
  771. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight,
  772. 255, 255, 255, 255,
  773. "Blending rules only: %i",
  774. g_nNumBonesSetupBlendingRulesOnly );
  775. }
  776. #endif
  777. if ( cl_showbattery.GetInt() > 0 )
  778. {
  779. if ( steamapicontext && steamapicontext->SteamUtils() &&
  780. ( m_lastBatteryPercent == -1.0f || (gpGlobals->realtime - m_lastBatteryPercent) > 10.0f ) )
  781. {
  782. m_BatteryPercent = steamapicontext->SteamUtils()->GetCurrentBatteryPower();
  783. m_lastBatteryPercent = gpGlobals->realtime;
  784. }
  785. if ( m_BatteryPercent > 0 )
  786. {
  787. if ( m_BatteryPercent == 255 )
  788. {
  789. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight,
  790. 255, 255, 255, 255,
  791. "battery: On AC" );
  792. }
  793. else
  794. {
  795. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight,
  796. 255, 255, 255, 255,
  797. "battery: %d%%",
  798. m_BatteryPercent );
  799. }
  800. }
  801. }
  802. #ifdef _GAMECONSOLE
  803. // Display a warning message if free memory dips below a certain threshold
  804. size_t nUsedMem = 0, nFreeMem = 0, nMemoryThreshold = 1024*1024*cl_showlowmemory.GetInt();
  805. if ( nMemoryThreshold )
  806. {
  807. g_pMemAlloc->GlobalMemoryStatus( &nUsedMem, &nFreeMem );
  808. if ( nFreeMem < nMemoryThreshold )
  809. {
  810. i += 2;
  811. g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight, 255, 150, 40, 255,
  812. "WARNING: low memory! (%3.1fMB) Report if seen at a test station...\n", nFreeMem/(float)(1024*1024) );
  813. }
  814. }
  815. #endif // _GAMECONSOLE
  816. if ( m_nLinesNeeded != i )
  817. {
  818. m_nLinesNeeded = i;
  819. ComputeSize();
  820. }
  821. }
  822. //-----------------------------------------------------------------------------
  823. // Purpose: Outputs the frame rate histogram and closes the file
  824. //-----------------------------------------------------------------------------
  825. void CFPSPanel::DumpStats()
  826. {
  827. Assert ( m_fhLog != FILESYSTEM_INVALID_HANDLE );
  828. if ( m_nNumFramesTotal > 0 )
  829. {
  830. g_pFullFileSystem->FPrintf( m_fhLog, "\n\nTotal Frames : %3i\n\n", m_nNumFramesTotal );
  831. g_pFullFileSystem->FPrintf( m_fhLog, "Frame Rate, Number of Frames, Percent of Frames\n" );
  832. for ( int i = 0; i < PERF_HISTOGRAM_BUCKET_SIZE; i++ )
  833. {
  834. float flPercent = m_nNumFramesBucket[i];
  835. flPercent /= m_nNumFramesTotal;
  836. flPercent *= 100.0f;
  837. g_pFullFileSystem->FPrintf( m_fhLog, "%3i, %3i, %5.1f\n", i, m_nNumFramesBucket[i], flPercent );
  838. m_nNumFramesBucket[i] = 0;
  839. }
  840. }
  841. g_pFullFileSystem->Close( m_fhLog );
  842. m_fhLog = FILESYSTEM_INVALID_HANDLE;
  843. m_nNumFramesTotal = 0;
  844. }
  845. class CFPS : public IFPSPanel
  846. {
  847. private:
  848. CFPSPanel *fpsPanel;
  849. public:
  850. CFPS( void )
  851. {
  852. fpsPanel = NULL;
  853. }
  854. void Create( vgui::VPANEL parent )
  855. {
  856. fpsPanel = new CFPSPanel( parent );
  857. }
  858. void Destroy( void )
  859. {
  860. if ( fpsPanel )
  861. {
  862. fpsPanel->SetParent( (vgui::Panel *)NULL );
  863. delete fpsPanel;
  864. }
  865. }
  866. };
  867. static CFPS g_FPSPanel;
  868. IFPSPanel *fps = ( IFPSPanel * )&g_FPSPanel;
  869. #if defined( TRACK_BLOCKING_IO )
  870. 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" );
  871. void ShowBlockingChanged( ConVar *var, char const *pOldString )
  872. {
  873. filesystem->EnableBlockingFileAccessTracking( var->GetBool() );
  874. }
  875. static ConVar cl_showblocking( "cl_showblocking", "0", 0, "Show blocking i/o on top of fps panel", ShowBlockingChanged );
  876. static ConVar cl_blocking_recentsize( "cl_blocking_recentsize", "40", 0, "Number of items to store in recent spew history." );
  877. //-----------------------------------------------------------------------------
  878. // Purpose: blocking i/o indicator
  879. //-----------------------------------------------------------------------------
  880. class CBlockingFileIOPanel : public vgui::Panel
  881. {
  882. typedef vgui::Panel BaseClass;
  883. public:
  884. CBlockingFileIOPanel( vgui::VPANEL parent );
  885. virtual ~CBlockingFileIOPanel( void );
  886. virtual void ApplySchemeSettings(vgui::IScheme *pScheme);
  887. virtual void Paint();
  888. virtual void OnTick( void );
  889. virtual bool ShouldDraw( void );
  890. void SpewRecent();
  891. private:
  892. void DrawIOTime( int x, int y, int w, int h, int slot, char const *label, const Color& clr );
  893. vgui::HFont m_hFont;
  894. struct Graph_t
  895. {
  896. float m_flCurrent;
  897. float m_flHistory;
  898. float m_flHistorySpike;
  899. float m_flLatchTime;
  900. CUtlSymbol m_LastFile;
  901. };
  902. Graph_t m_History[ FILESYSTEM_BLOCKING_NUMBINS ];
  903. struct RecentPeaks_t
  904. {
  905. float time;
  906. CUtlSymbol fileName;
  907. float elapsed;
  908. byte reason;
  909. byte ioType;
  910. };
  911. CUtlLinkedList< RecentPeaks_t, unsigned short > m_Recent;
  912. void SpewItem( const RecentPeaks_t& item );
  913. };
  914. #define IO_PANEL_WIDTH 400
  915. #define IO_DECAY_FRAC 0.95f
  916. //-----------------------------------------------------------------------------
  917. // Purpose:
  918. // Input : *parent -
  919. //-----------------------------------------------------------------------------
  920. CBlockingFileIOPanel::CBlockingFileIOPanel( vgui::VPANEL parent ) : BaseClass( NULL, "CBlockingFileIOPanel" )
  921. {
  922. SetParent( parent );
  923. int wide, tall;
  924. vgui::ipanel()->GetSize( parent, wide, tall );
  925. int x = 2;
  926. int y = 100;
  927. if ( IsGameConsole() )
  928. {
  929. x += XBOX_MAXBORDERSAFE * wide;
  930. y += XBOX_MAXBORDERSAFE * tall;
  931. }
  932. SetPos( x, y );
  933. SetSize( IO_PANEL_WIDTH, 140 );
  934. SetVisible( false );
  935. SetCursor( 0 );
  936. SetFgColor( Color( 0, 0, 0, 255 ) );
  937. SetPaintBackgroundEnabled( false );
  938. m_hFont = 0;
  939. vgui::ivgui()->AddTickSignal( GetVPanel(), 250 );
  940. SetZPos( 1000 );
  941. Q_memset( m_History, 0, sizeof( m_History ) );
  942. SetPaintBackgroundEnabled( false );
  943. SetPaintBorderEnabled( false );
  944. MakePopup();
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Purpose:
  948. //-----------------------------------------------------------------------------
  949. CBlockingFileIOPanel::~CBlockingFileIOPanel( void )
  950. {
  951. }
  952. void CBlockingFileIOPanel::ApplySchemeSettings(vgui::IScheme *pScheme)
  953. {
  954. BaseClass::ApplySchemeSettings(pScheme);
  955. m_hFont = pScheme->GetFont( "Default" );
  956. Assert( m_hFont );
  957. SetKeyBoardInputEnabled( false );
  958. SetMouseInputEnabled( false );
  959. }
  960. //-----------------------------------------------------------------------------
  961. // Purpose:
  962. //-----------------------------------------------------------------------------
  963. void CBlockingFileIOPanel::OnTick( void )
  964. {
  965. bool bVisible = ShouldDraw();
  966. if ( IsVisible() != bVisible )
  967. {
  968. SetVisible( bVisible );
  969. }
  970. }
  971. //-----------------------------------------------------------------------------
  972. // Purpose:
  973. // Output : Returns true on success, false on failure.
  974. //-----------------------------------------------------------------------------
  975. bool CBlockingFileIOPanel::ShouldDraw( void )
  976. {
  977. if ( !cl_showblocking.GetInt() )
  978. {
  979. return false;
  980. }
  981. return true;
  982. }
  983. //-----------------------------------------------------------------------------
  984. // Purpose:
  985. // Input :
  986. //-----------------------------------------------------------------------------
  987. void CBlockingFileIOPanel::Paint()
  988. {
  989. int x = 2;
  990. int maxRecent = clamp( 0, cl_blocking_recentsize.GetInt(), 1000 );
  991. int bval = cl_showblocking.GetInt();
  992. if ( bval > 0 )
  993. {
  994. IBlockingFileItemList *list = filesystem->RetrieveBlockingFileAccessInfo();
  995. if ( list )
  996. {
  997. int i;
  998. int c = ARRAYSIZE( m_History );
  999. for ( i = 0; i < c; ++i )
  1000. {
  1001. m_History[ i ].m_flCurrent = 0.0f;
  1002. }
  1003. // Grab mutex (prevents async thread from filling in even more data...)
  1004. list->LockMutex();
  1005. {
  1006. for ( int j = list->First() ; j != list->InvalidIndex(); j = list->Next( j ) )
  1007. {
  1008. const FileBlockingItem& item = list->Get( j );
  1009. m_History[ item.m_ItemType ].m_flCurrent += item.m_flElapsed;
  1010. RecentPeaks_t recent;
  1011. recent.time = gpGlobals->realtime;
  1012. recent.elapsed = item.m_flElapsed;
  1013. recent.fileName = item.GetFileName();
  1014. recent.reason = item.m_ItemType;
  1015. recent.ioType = item.m_nAccessType;
  1016. while ( m_Recent.Count() > maxRecent )
  1017. {
  1018. m_Recent.Remove( m_Recent.Head() );
  1019. }
  1020. m_Recent.AddToTail( recent );
  1021. m_History[ item.m_ItemType ].m_LastFile = item.GetFileName();
  1022. // Only care about time consuming synch or async blocking calls
  1023. if ( item.m_ItemType == FILESYSTEM_BLOCKING_SYNCHRONOUS ||
  1024. item.m_ItemType == FILESYSTEM_BLOCKING_ASYNCHRONOUS_BLOCK )
  1025. {
  1026. if ( item.m_flElapsed > cl_blocking_threshold.GetFloat() )
  1027. {
  1028. SpewItem( recent );
  1029. }
  1030. }
  1031. }
  1032. list->Reset();
  1033. }
  1034. // Finished
  1035. list->UnlockMutex();
  1036. // Now draw some bars...
  1037. int itemHeight = ( vgui::surface()->GetFontTall( m_hFont ) + 2 );
  1038. int y = 2;
  1039. int w = GetWide();
  1040. DrawIOTime( x, y, w, itemHeight, FILESYSTEM_BLOCKING_SYNCHRONOUS, "Synchronous", Color( 255, 0, 0, 255 ) );
  1041. y += 2*( itemHeight + 2 );
  1042. DrawIOTime( x, y, w, itemHeight, FILESYSTEM_BLOCKING_ASYNCHRONOUS_BLOCK, "Async Block", Color( 255, 100, 0, 255 ) );
  1043. y += 2*( itemHeight + 2 );
  1044. DrawIOTime( x, y, w, itemHeight, FILESYSTEM_BLOCKING_CALLBACKTIMING, "Callback", Color( 255, 255, 0, 255 ) );
  1045. y += 2*( itemHeight + 2 );
  1046. DrawIOTime( x, y, w, itemHeight, FILESYSTEM_BLOCKING_ASYNCHRONOUS, "Asynchronous", Color( 0, 255, 0, 255 ) );
  1047. for ( i = 0; i < c; ++i )
  1048. {
  1049. if ( m_History[ i ].m_flCurrent > m_History[ i ].m_flHistory )
  1050. {
  1051. m_History[ i ].m_flHistory = m_History[ i ].m_flCurrent;
  1052. m_History[ i ].m_flHistorySpike = m_History[ i ].m_flCurrent;
  1053. m_History[ i ].m_flLatchTime = gpGlobals->realtime;
  1054. }
  1055. else
  1056. {
  1057. // After this long, start to decay the previous history value
  1058. if ( gpGlobals->realtime > m_History[ i ].m_flLatchTime + 1.0f )
  1059. {
  1060. m_History[ i ].m_flHistory = m_History[ i ].m_flHistory * IO_DECAY_FRAC + ( 1.0f - IO_DECAY_FRAC ) * m_History[ i ].m_flCurrent;
  1061. }
  1062. }
  1063. }
  1064. }
  1065. }
  1066. }
  1067. static ConVar cl_blocking_msec( "cl_blocking_msec", "100", 0, "Vertical scale of blocking graph in milliseconds" );
  1068. static const char *GetBlockReason( int reason )
  1069. {
  1070. switch ( reason )
  1071. {
  1072. case FILESYSTEM_BLOCKING_SYNCHRONOUS:
  1073. return "Synchronous";
  1074. case FILESYSTEM_BLOCKING_ASYNCHRONOUS:
  1075. return "Asynchronous";
  1076. case FILESYSTEM_BLOCKING_CALLBACKTIMING:
  1077. return "Async Callback";
  1078. case FILESYSTEM_BLOCKING_ASYNCHRONOUS_BLOCK:
  1079. return "Async Blocked";
  1080. }
  1081. return "???";
  1082. }
  1083. static const char *GetIOType( int iotype )
  1084. {
  1085. if ( FileBlockingItem::FB_ACCESS_APPEND == iotype )
  1086. {
  1087. return "Append";
  1088. }
  1089. else if ( FileBlockingItem::FB_ACCESS_CLOSE == iotype )
  1090. {
  1091. return "Close";
  1092. }
  1093. else if ( FileBlockingItem::FB_ACCESS_OPEN == iotype)
  1094. {
  1095. return "Open";
  1096. }
  1097. else if ( FileBlockingItem::FB_ACCESS_READ == iotype)
  1098. {
  1099. return "Read";
  1100. }
  1101. else if ( FileBlockingItem::FB_ACCESS_SIZE == iotype)
  1102. {
  1103. return "Size";
  1104. }
  1105. else if ( FileBlockingItem::FB_ACCESS_WRITE == iotype)
  1106. {
  1107. return "Write";
  1108. }
  1109. return "???";
  1110. }
  1111. void CBlockingFileIOPanel::SpewItem( const RecentPeaks_t& item )
  1112. {
  1113. switch ( item.reason )
  1114. {
  1115. default:
  1116. Assert( 0 );
  1117. // break; -- intentionally fall through
  1118. case FILESYSTEM_BLOCKING_ASYNCHRONOUS:
  1119. case FILESYSTEM_BLOCKING_CALLBACKTIMING:
  1120. Msg( "%8.3f %16.16s i/o [%6.6s] took %8.3f msec: %33.33s\n",
  1121. item.time,
  1122. GetBlockReason( item.reason ),
  1123. GetIOType( item.ioType ),
  1124. item.elapsed * 1000.0f,
  1125. item.fileName.String()
  1126. );
  1127. break;
  1128. case FILESYSTEM_BLOCKING_SYNCHRONOUS:
  1129. case FILESYSTEM_BLOCKING_ASYNCHRONOUS_BLOCK:
  1130. Warning( "%8.3f %16.16s i/o [%6.6s] took %8.3f msec: %33.33s\n",
  1131. item.time,
  1132. GetBlockReason( item.reason ),
  1133. GetIOType( item.ioType ),
  1134. item.elapsed * 1000.0f,
  1135. item.fileName.String()
  1136. );
  1137. break;
  1138. }
  1139. }
  1140. void CBlockingFileIOPanel::SpewRecent()
  1141. {
  1142. FOR_EACH_LL( m_Recent, i )
  1143. {
  1144. const RecentPeaks_t& item = m_Recent[ i ];
  1145. SpewItem( item );
  1146. }
  1147. }
  1148. void CBlockingFileIOPanel::DrawIOTime( int x, int y, int w, int h, int slot, char const *label, const Color& clr )
  1149. {
  1150. float t = m_History[ slot ].m_flCurrent;
  1151. float history = m_History[ slot ].m_flHistory;
  1152. float latchedtime = m_History[ slot ].m_flLatchTime;
  1153. float historyspike = m_History[ slot ].m_flHistorySpike;
  1154. // 250 msec is considered a huge spike
  1155. float maxTime = cl_blocking_msec.GetFloat() * 0.001f;
  1156. if ( maxTime < 0.000001f )
  1157. return;
  1158. float frac = clamp( t / maxTime, 0.0f, 1.0f );
  1159. float hfrac = clamp( history / maxTime, 0.0f, 1.0f );
  1160. float spikefrac = clamp( historyspike / maxTime, 0.0f, 1.0f );
  1161. g_pMatSystemSurface->DrawColoredText( m_hFont, x + 2, y + 1,
  1162. clr[0], clr[1], clr[2], clr[3],
  1163. "%s",
  1164. label );
  1165. int textWidth = 95;
  1166. x += textWidth;
  1167. w -= ( textWidth + 5 );
  1168. int prevFileWidth = 140;
  1169. w -= prevFileWidth;
  1170. bool bDrawHistorySpike = false;
  1171. if ( m_History[ slot ].m_LastFile.IsValid() &&
  1172. ( gpGlobals->realtime < latchedtime + 10.0f ) )
  1173. {
  1174. bDrawHistorySpike = true;
  1175. g_pMatSystemSurface->DrawColoredText( m_hFont, x + w + 5, y + 1,
  1176. 255, 255, 255, 200, "[%8.3f ms]", m_History[ slot ].m_flHistorySpike * 1000.0f );
  1177. g_pMatSystemSurface->DrawColoredText( m_hFont, x, y + h + 1,
  1178. 255, 255, 255, 200, "%s", m_History[ slot ].m_LastFile.String() );
  1179. }
  1180. y += 2;
  1181. h -= 4;
  1182. int barWide = ( int )( w * frac + 0.5f );
  1183. int historyWide = ( int ) ( w * hfrac + 0.5f );
  1184. int spikeWide = ( int ) ( w * spikefrac + 0.5f );
  1185. int useWide = MAX( barWide, historyWide );
  1186. vgui::surface()->DrawSetColor( Color( 0, 0, 0, 31 ) );
  1187. vgui::surface()->DrawFilledRect( x, y, x + w, y + h );
  1188. vgui::surface()->DrawSetColor( Color( 255, 255, 255, 128 ) );
  1189. vgui::surface()->DrawOutlinedRect( x, y, x + w, y + h );
  1190. vgui::surface()->DrawSetColor( clr );
  1191. vgui::surface()->DrawFilledRect( x+1, y+1, x + useWide, y + h -1 );
  1192. if ( bDrawHistorySpike )
  1193. {
  1194. vgui::surface()->DrawSetColor( Color( 255, 255, 255, 192 ) );
  1195. vgui::surface()->DrawFilledRect( x + spikeWide, y + 1, x + spikeWide + 1, y + h - 1 );
  1196. }
  1197. }
  1198. class CBlockingFileIO : public IShowBlockingPanel
  1199. {
  1200. private:
  1201. CBlockingFileIOPanel *ioPanel;
  1202. public:
  1203. CBlockingFileIO( void )
  1204. {
  1205. ioPanel = NULL;
  1206. }
  1207. void Create( vgui::VPANEL parent )
  1208. {
  1209. ioPanel = new CBlockingFileIOPanel( parent );
  1210. }
  1211. void Destroy( void )
  1212. {
  1213. if ( ioPanel )
  1214. {
  1215. ioPanel->SetParent( (vgui::Panel *)NULL );
  1216. delete ioPanel;
  1217. }
  1218. }
  1219. void Spew()
  1220. {
  1221. if ( ioPanel )
  1222. {
  1223. ioPanel->SpewRecent();
  1224. }
  1225. }
  1226. };
  1227. static CBlockingFileIO g_IOPanel;
  1228. IShowBlockingPanel *iopanel = ( IShowBlockingPanel * )&g_IOPanel;
  1229. CON_COMMAND( spewblocking, "Spew current blocking file list." )
  1230. {
  1231. g_IOPanel.Spew();
  1232. }
  1233. #endif // TRACK_BLOCKING_IO