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.

1725 lines
46 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "cbase.h"
  10. #include "hud.h"
  11. #include "inetgraphpanel.h"
  12. #include "kbutton.h"
  13. #include <inetchannelinfo.h>
  14. #include "input.h"
  15. #include <vgui/IVGui.h>
  16. #include "VGuiMatSurface/IMatSystemSurface.h"
  17. #include <vgui_controls/Panel.h>
  18. #include <vgui_controls/Controls.h>
  19. #include <vgui/ISurface.h>
  20. #include <vgui/IScheme.h>
  21. #include <vgui/ILocalize.h>
  22. #include "tier0/vprof.h"
  23. #include "tier0/cpumonitoring.h"
  24. #include "cdll_bounded_cvars.h"
  25. #include "materialsystem/imaterialsystemstub.h"
  26. #include "materialsystem/imesh.h"
  27. #include "materialsystem/imaterial.h"
  28. #include "matchmaking/imatchframework.h"
  29. #include "cs_gamerules.h"
  30. #ifdef _PS3
  31. #include "ps3/ps3_core.h"
  32. #endif
  33. // memdbgon must be the last include file in a .cpp file!!!
  34. #include "tier0/memdbgon.h"
  35. using namespace vgui;
  36. static ConVar net_scale ( "net_scale", "5", FCVAR_ARCHIVE );
  37. static ConVar net_graphpos ( "net_graphpos", "1", FCVAR_ARCHIVE );
  38. static ConVar net_graphsolid ( "net_graphsolid", "1", FCVAR_ARCHIVE );
  39. static ConVar net_graphtext ( "net_graphtext", "1", FCVAR_ARCHIVE, "Draw text fields" );
  40. static ConVar net_graphmsecs ( "net_graphmsecs", "400", FCVAR_ARCHIVE, "The latency graph represents this many milliseconds." );
  41. static ConVar net_graphshowlatency( "net_graphshowlatency", "1", FCVAR_ARCHIVE, "Draw the ping/packet loss graph." );
  42. static ConVar net_graphshowinterp ( "net_graphshowinterp", "1", FCVAR_ARCHIVE, "Draw the interpolation graph." );
  43. static ConVar net_graphshowsvframerate ( "net_graphshowsvframerate", "0", FCVAR_ARCHIVE, "Draw the server framerate graph." );
  44. static ConVar net_graphholdsvframerate ( "net_graphholdsvframerate", "0", FCVAR_ARCHIVE, "Hold worst case in server framerate line." );
  45. void NetgraphChangeCallback( IConVar *var, const char *pOldValue, float flOldValue );
  46. ConVar net_graph ( "net_graph","0", FCVAR_ARCHIVE, "Draw the network usage data, = 2 prints in/out data, = 3 draws data on payload,", NetgraphChangeCallback );
  47. static ConVar net_graphheight ( "net_graphheight", "64", FCVAR_ARCHIVE, "Height of netgraph panel", NetgraphChangeCallback );
  48. static ConVar net_graphproportionalfont( "net_graphproportionalfont", "1", FCVAR_ARCHIVE, "Determines whether netgraph font is proportional or not", NetgraphChangeCallback );
  49. #define TIMINGS 1024 // Number of values to track (must be power of 2) b/c of masking
  50. #define FRAMERATE_AVG_FRAC 0.9
  51. #define PACKETLOSS_AVG_FRAC 0.5
  52. #define PACKETCHOKE_AVG_FRAC 0.5
  53. #define NUM_LATENCY_SAMPLES 8
  54. #define GRAPH_RED (0.9 * 255)
  55. #define GRAPH_GREEN (0.9 * 255)
  56. #define GRAPH_BLUE (0.7 * 255)
  57. #define LERP_HEIGHT 24
  58. #define COLOR_DROPPED 0
  59. #define COLOR_INVALID 1
  60. #define COLOR_SKIPPED 2
  61. #define COLOR_CHOKED 3
  62. #define COLOR_NORMAL 4
  63. //-----------------------------------------------------------------------------
  64. // Purpose: Displays the NetGraph
  65. //-----------------------------------------------------------------------------
  66. class CNetGraphPanel : public Panel
  67. {
  68. typedef Panel BaseClass;
  69. private:
  70. typedef struct
  71. {
  72. int latency;
  73. int choked;
  74. } packet_latency_t;
  75. typedef struct
  76. {
  77. unsigned short msgbytes[INetChannelInfo::TOTAL+1];
  78. int sampleY;
  79. int sampleHeight;
  80. } netbandwidthgraph_t;
  81. typedef struct
  82. {
  83. float cmd_lerp;
  84. int size;
  85. bool sent;
  86. } cmdinfo_t;
  87. typedef struct
  88. {
  89. byte color[3];
  90. byte alpha;
  91. } netcolor_t;
  92. typedef struct
  93. {
  94. float flSvFrameTime;
  95. float flSvFrameRateStdDev;
  96. float flSvFrameStartTimeStdDev;
  97. } svframerate_t;
  98. byte colors[ LERP_HEIGHT ][3];
  99. byte sendcolor[ 3 ];
  100. byte holdcolor[ 3 ];
  101. byte extrap_base_color[ 3 ];
  102. struct color
  103. {
  104. color(byte r_, byte g_, byte b_, byte a_) : r(r_), g(g_), b(b_), a(a_) {}
  105. color() {}
  106. byte r, g, b, a;
  107. };
  108. color textColorDefault;
  109. color textColorWarn1;
  110. color textColorWarn2;
  111. color textColorWarn3;
  112. packet_latency_t m_PacketLatency[ TIMINGS ];
  113. cmdinfo_t m_Cmdinfo[ TIMINGS ];
  114. netbandwidthgraph_t m_Graph[ TIMINGS ];
  115. svframerate_t m_SvFrameRate[ TIMINGS ];
  116. float m_Framerate;
  117. float m_AvgLatency;
  118. float m_AvgPacketLoss;
  119. float m_AvgPacketChoke;
  120. int m_IncomingSequence;
  121. int m_OutgoingSequence;
  122. int m_UpdateWindowSize;
  123. float m_IncomingData;
  124. float m_OutgoingData;
  125. float m_AvgPacketIn;
  126. float m_AvgPacketOut;
  127. int m_StreamRecv[MAX_FLOWS];
  128. int m_StreamTotal[MAX_FLOWS];
  129. netcolor_t netcolors[5];
  130. HFont m_hFontProportional;
  131. HFont m_hFont;
  132. HFont m_hFontSmall;
  133. const ConVar *cl_updaterate;
  134. const ConVar *cl_cmdrate;
  135. public:
  136. explicit CNetGraphPanel( VPANEL parent );
  137. virtual ~CNetGraphPanel( void );
  138. virtual void ApplySchemeSettings(IScheme *pScheme);
  139. virtual void Paint();
  140. virtual void OnTick( void );
  141. virtual bool ShouldDraw( void );
  142. void InitColors( void );
  143. int GraphValue( void );
  144. struct CLineSegment
  145. {
  146. int x1, y1, x2, y2;
  147. byte color[4];
  148. byte color2[4];
  149. };
  150. CUtlVector< CLineSegment > m_Rects;
  151. inline void DrawLine( vrect_t *rect, unsigned char *color, unsigned char alpha );
  152. inline void DrawLine2( vrect_t *rect, unsigned char *color, unsigned char *color2, unsigned char alpha, unsigned char alpha2 );
  153. void ResetLineSegments();
  154. void DrawLineSegments();
  155. int DrawDataSegment( vrect_t *rcFill, int bytes, byte r, byte g, byte b, byte alpha = 255);
  156. void DrawServerType( int xright, int y );
  157. void DrawHatches( int x, int y, int maxmsgbytes );
  158. void DrawStreamProgress( int x, int y, int width );
  159. void DrawTimes( vrect_t vrect, cmdinfo_t *cmdinfo, int x, int w, int graphtype );
  160. void DrawSvFrameRate( vrect_t vrect, svframerate_t *svframerate, int x, int w, int graphtype );
  161. void DrawTextFields( int graphvalue, int x, int y, int w, netbandwidthgraph_t *graph, cmdinfo_t *cmdinfo );
  162. void GraphGetXY( vrect_t *rect, int width, int *x, int *y );
  163. void GetCommandInfo( INetChannelInfo *netchannel, cmdinfo_t *cmdinfo );
  164. void GetFrameData( INetChannelInfo *netchannel, int *biggest_message, float *avg_message, float *f95thpercentile );
  165. void ColorForHeight( packet_latency_t *packet, byte *color, int *ping, byte *alpha );
  166. void GetColorValues( int color, byte *cv, byte *alpha );
  167. void OnFontChanged();
  168. inline void DrawColoredText( vgui::HFont font, int x, int y, color c, const char* sz )
  169. {
  170. g_pMatSystemSurface->DrawColoredText( font, x, y, c.r, c.g, c.b, c.a, "%s", sz );
  171. }
  172. inline int TextWidth( HFont font, const char* sz)
  173. {
  174. return g_pMatSystemSurface->DrawTextLen(font, "%s", sz);
  175. }
  176. color GetColorFromVariance( float fValue, float fBase, float fVariance1, float fVariance2, float fVariance3 );
  177. private:
  178. void PaintLineArt( int x, int y, int w, int graphtype, int maxmsgbytes );
  179. void DrawLargePacketSizes( int x, int w, int graphtype, float warning_threshold );
  180. HFont GetNetgraphFont()
  181. {
  182. return net_graphproportionalfont.GetBool() ? m_hFontProportional : m_hFont;
  183. }
  184. void ComputeNetgraphHeight();
  185. void UpdateEstimatedServerFramerate( INetChannelInfo *netchannel );
  186. CMaterialReference m_WhiteMaterial;
  187. int m_EstimatedWidth;
  188. int m_nNetGraphHeight;
  189. float m_flServerFrameComputationTime;
  190. float m_flServerFramerateStdDeviation;
  191. float m_flServerFrameStartTimeStdDeviation;
  192. int m_nServerFramerateSample;
  193. };
  194. CNetGraphPanel *g_pNetGraphPanel = NULL;
  195. //-----------------------------------------------------------------------------
  196. // Purpose:
  197. // Input : *parent -
  198. //-----------------------------------------------------------------------------
  199. CNetGraphPanel::CNetGraphPanel( VPANEL parent )
  200. : BaseClass( NULL, "CNetGraphPanel" )
  201. {
  202. int w, h;
  203. surface()->GetScreenSize( w, h );
  204. SetParent( parent );
  205. SetSize( w, h );
  206. SetPos( 0, 0 );
  207. SetVisible( false );
  208. SetCursor( 0 );
  209. m_hFont = 0;
  210. m_hFontProportional = 0;
  211. m_hFontSmall = 0;
  212. m_EstimatedWidth = 1;
  213. m_nNetGraphHeight = 100;
  214. SetFgColor( Color( 0, 0, 0, 255 ) );
  215. SetPaintBackgroundEnabled( false );
  216. InitColors();
  217. cl_updaterate = cvar->FindVar( "cl_updaterate" );
  218. cl_cmdrate = cvar->FindVar( "cl_cmdrate" );
  219. assert( cl_updaterate && cl_cmdrate );
  220. memset( sendcolor, 0, 3 );
  221. memset( holdcolor, 0, 3 );
  222. sendcolor[ 0 ] = sendcolor[ 1 ] = 255;
  223. memset( extrap_base_color, 255, 3 );
  224. memset( m_PacketLatency, 0, TIMINGS * sizeof( packet_latency_t ) );
  225. memset( m_Cmdinfo, 0, TIMINGS * sizeof( cmdinfo_t ) );
  226. memset( m_Graph, 0, TIMINGS * sizeof( netbandwidthgraph_t ) );
  227. memset( m_SvFrameRate, 0, TIMINGS * sizeof( svframerate_t ) );
  228. m_Framerate = 0.0f;
  229. m_AvgLatency = 0.0f;
  230. m_AvgPacketLoss = 0.0f;
  231. m_AvgPacketChoke = 0.0f;
  232. m_IncomingSequence = 0;
  233. m_OutgoingSequence = 0;
  234. m_UpdateWindowSize = 0;
  235. m_IncomingData = 0;
  236. m_OutgoingData = 0;
  237. m_AvgPacketIn = 0.0f;
  238. m_AvgPacketOut = 0.0f;
  239. m_flServerFrameComputationTime = 0;
  240. m_flServerFramerateStdDeviation = 0;
  241. m_flServerFrameStartTimeStdDeviation = 0;
  242. m_nServerFramerateSample = 0;
  243. netcolors[COLOR_DROPPED].color[0] = 255;
  244. netcolors[COLOR_DROPPED].color[1] = 0;
  245. netcolors[COLOR_DROPPED].color[2] = 0;
  246. netcolors[COLOR_DROPPED].alpha = 255;
  247. netcolors[COLOR_INVALID].color[0] = 0;
  248. netcolors[COLOR_INVALID].color[1] = 0;
  249. netcolors[COLOR_INVALID].color[2] = 255;
  250. netcolors[COLOR_INVALID].alpha = 255;
  251. netcolors[COLOR_SKIPPED].color[0] = 240;
  252. netcolors[COLOR_SKIPPED].color[1] = 127;
  253. netcolors[COLOR_SKIPPED].color[2] = 63;
  254. netcolors[COLOR_SKIPPED].alpha = 255;
  255. netcolors[COLOR_CHOKED].color[0] = 225;
  256. netcolors[COLOR_CHOKED].color[1] = 225;
  257. netcolors[COLOR_CHOKED].color[2] = 0;
  258. netcolors[COLOR_CHOKED].alpha = 255;
  259. netcolors[COLOR_NORMAL].color[0] = 63;
  260. netcolors[COLOR_NORMAL].color[1] = 255;
  261. netcolors[COLOR_NORMAL].color[2] = 63;
  262. netcolors[COLOR_NORMAL].alpha = 232;
  263. ivgui()->AddTickSignal( GetVPanel(), 500 );
  264. m_WhiteMaterial.Init( "vgui/white", TEXTURE_GROUP_OTHER );
  265. g_pNetGraphPanel = this;
  266. }
  267. //-----------------------------------------------------------------------------
  268. // Purpose:
  269. //-----------------------------------------------------------------------------
  270. CNetGraphPanel::~CNetGraphPanel( void )
  271. {
  272. g_pNetGraphPanel = NULL;
  273. }
  274. extern ConVar sv_max_allowed_net_graph;
  275. void NetgraphChangeCallback( IConVar *var, const char *pOldValue, float flOldValue )
  276. {
  277. if ( net_graph.GetInt() > sv_max_allowed_net_graph.GetInt() )
  278. {
  279. net_graph.SetValue( sv_max_allowed_net_graph.GetInt() );
  280. Msg( "Server does not allow net_graph values above %d\n", sv_max_allowed_net_graph.GetInt() );
  281. }
  282. if ( g_pNetGraphPanel )
  283. {
  284. g_pNetGraphPanel->OnFontChanged();
  285. }
  286. }
  287. void CNetGraphPanel::OnFontChanged()
  288. {
  289. if ( !m_hFontProportional )
  290. return;
  291. // Estimate the width of our panel.
  292. char str[512];
  293. wchar_t ustr[512];
  294. Q_snprintf( str, sizeof( str ), "fps: 435 var: 4.091 ms ping: 533 ms lerp 112.3 ms 0/0 offline" );
  295. g_pVGuiLocalize->ConvertANSIToUnicode( str, ustr, sizeof( ustr ) );
  296. int textTall;
  297. g_pMatSystemSurface->GetTextSize( m_hFontProportional, ustr, m_EstimatedWidth, textTall );
  298. int w, h;
  299. surface()->GetScreenSize( w, h );
  300. SetSize( w, h );
  301. SetPos( 0, 0 );
  302. ComputeNetgraphHeight();
  303. }
  304. void CNetGraphPanel::ApplySchemeSettings(IScheme *pScheme)
  305. {
  306. BaseClass::ApplySchemeSettings(pScheme);
  307. m_hFont = pScheme->GetFont( "DefaultFixedOutline", false );
  308. m_hFontProportional = pScheme->GetFont( "DefaultFixedOutline", true );
  309. m_hFontSmall = pScheme->GetFont( "DefaultVerySmall", false );
  310. OnFontChanged();
  311. }
  312. void CNetGraphPanel::ComputeNetgraphHeight()
  313. {
  314. m_nNetGraphHeight = net_graphheight.GetInt();
  315. HFont fnt = GetNetgraphFont();
  316. int tall = surface()->GetFontTall( fnt );
  317. int lines = 4;
  318. if ( net_graph.GetInt() > 1 )
  319. {
  320. lines = 6;
  321. }
  322. m_nNetGraphHeight = MAX( lines * tall, m_nNetGraphHeight );
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose: Copies data from netcolor_t array into fields passed in
  326. // Input : color -
  327. // *cv -
  328. // *alpha -
  329. //-----------------------------------------------------------------------------
  330. void CNetGraphPanel::GetColorValues( int color, byte *cv, byte *alpha )
  331. {
  332. int i;
  333. netcolor_t *pc = &netcolors[ color ];
  334. for ( i = 0; i < 3; i++ )
  335. {
  336. cv[ i ] = pc->color[ i ];
  337. }
  338. *alpha = pc->alpha;
  339. }
  340. //-----------------------------------------------------------------------------
  341. // Purpose: Sets appropriate color values
  342. // Input : *packet -
  343. // *color -
  344. // *ping -
  345. // *alpha -
  346. //-----------------------------------------------------------------------------
  347. void CNetGraphPanel::ColorForHeight( packet_latency_t *packet, byte *color, int *ping, byte *alpha )
  348. {
  349. int h = packet->latency;
  350. *ping = 0;
  351. switch ( h )
  352. {
  353. case 9999:
  354. GetColorValues( COLOR_DROPPED, color, alpha );
  355. break;
  356. case 9998:
  357. GetColorValues( COLOR_INVALID, color, alpha );
  358. break;
  359. case 9997:
  360. GetColorValues( COLOR_SKIPPED, color, alpha );
  361. break;
  362. default:
  363. *ping = 1;
  364. if (packet->choked )
  365. {
  366. GetColorValues( COLOR_CHOKED, color, alpha );
  367. }
  368. else
  369. {
  370. GetColorValues( COLOR_NORMAL, color, alpha );
  371. }
  372. break;
  373. }
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose: Set up blend colors for comman/client-frame/interpolation graph
  377. //-----------------------------------------------------------------------------
  378. void CNetGraphPanel::InitColors( void )
  379. {
  380. int i, j;
  381. byte mincolor[2][3];
  382. byte maxcolor[2][3];
  383. float dc[2][3];
  384. int hfrac;
  385. float f;
  386. mincolor[0][0] = 63;
  387. mincolor[0][1] = 0;
  388. mincolor[0][2] = 100;
  389. maxcolor[0][0] = 0;
  390. maxcolor[0][1] = 63;
  391. maxcolor[0][2] = 255;
  392. mincolor[1][0] = 255;
  393. mincolor[1][1] = 127;
  394. mincolor[1][2] = 0;
  395. maxcolor[1][0] = 250;
  396. maxcolor[1][1] = 0;
  397. maxcolor[1][2] = 0;
  398. for ( i = 0; i < 3; i++ )
  399. {
  400. dc[0][i] = (float)(maxcolor[0][i] - mincolor[0][i]);
  401. dc[1][i] = (float)(maxcolor[1][i] - mincolor[1][i]);
  402. }
  403. hfrac = LERP_HEIGHT / 3;
  404. for ( i = 0; i < LERP_HEIGHT; i++ )
  405. {
  406. if ( i < hfrac )
  407. {
  408. f = (float)i / (float)hfrac;
  409. for ( j = 0; j < 3; j++ )
  410. {
  411. colors[ i ][j] = mincolor[0][j] + f * dc[0][j];
  412. }
  413. }
  414. else
  415. {
  416. f = (float)(i-hfrac) / (float)(LERP_HEIGHT - hfrac );
  417. for ( j = 0; j < 3; j++ )
  418. {
  419. colors[ i ][j] = mincolor[1][j] + f * dc[1][j];
  420. }
  421. }
  422. }
  423. textColorDefault = color(GRAPH_RED, GRAPH_GREEN, GRAPH_BLUE, 255);
  424. textColorWarn1 = color(255, 255, 31, 255);
  425. textColorWarn2 = color(255, 125, 31, 255);
  426. textColorWarn3 = color(255, 31, 31, 255);
  427. }
  428. //-----------------------------------------------------------------------------
  429. // Purpose: Draw client framerate / usercommand graph
  430. // Input : vrect -
  431. // *cmdinfo -
  432. // x -
  433. // w -
  434. //-----------------------------------------------------------------------------
  435. void CNetGraphPanel::DrawTimes( vrect_t vrect, cmdinfo_t *cmdinfo, int x, int w, int graphtype )
  436. {
  437. if ( !net_graphshowinterp.GetBool() || graphtype < 3 || net_graphshowsvframerate.GetBool() )
  438. return;
  439. int i;
  440. int j;
  441. int extrap_point;
  442. int a, h;
  443. vrect_t rcFill;
  444. ResetLineSegments();
  445. extrap_point = LERP_HEIGHT / 3;
  446. for (a=0 ; a<w ; a++)
  447. {
  448. i = ( m_OutgoingSequence - a ) & ( TIMINGS - 1 );
  449. h = MIN( ( cmdinfo[i].cmd_lerp / 3.0 ) * LERP_HEIGHT, LERP_HEIGHT );
  450. if ( h < 0 )
  451. {
  452. h = LERP_HEIGHT;
  453. }
  454. rcFill.x = x + w -a - 1;
  455. rcFill.width = 1;
  456. rcFill.height = 1;
  457. rcFill.y = vrect.y + vrect.height - 4;
  458. if ( h >= extrap_point )
  459. {
  460. int start = 0;
  461. h -= extrap_point;
  462. rcFill.y -= extrap_point;
  463. if ( !net_graphsolid.GetInt() )
  464. {
  465. rcFill.y -= (h - 1);
  466. start = (h - 1);
  467. }
  468. for ( j = start; j < h; j++ )
  469. {
  470. DrawLine(&rcFill, colors[j + extrap_point], 255 );
  471. rcFill.y--;
  472. }
  473. }
  474. else
  475. {
  476. int oldh;
  477. oldh = h;
  478. rcFill.y -= h;
  479. h = extrap_point - h;
  480. if ( !net_graphsolid.GetInt() )
  481. {
  482. h = 1;
  483. }
  484. for ( j = 0; j < h; j++ )
  485. {
  486. DrawLine(&rcFill, colors[j + oldh], 255 );
  487. rcFill.y--;
  488. }
  489. }
  490. rcFill.y = vrect.y + vrect.height - 4 - extrap_point;
  491. DrawLine( &rcFill, extrap_base_color, 255 );
  492. rcFill.y = vrect.y + vrect.height - 3;
  493. if ( cmdinfo[ i ].sent )
  494. {
  495. DrawLine( &rcFill, sendcolor, 255 );
  496. }
  497. else
  498. {
  499. DrawLine( &rcFill, holdcolor, 200 );
  500. }
  501. }
  502. DrawLineSegments();
  503. }
  504. void CNetGraphPanel::DrawSvFrameRate( vrect_t vrect, svframerate_t *svframerate, int x, int w, int graphtype )
  505. {
  506. if ( !net_graphshowsvframerate.GetBool() || graphtype < 3 )
  507. return;
  508. int i;
  509. int j;
  510. int extrap_point;
  511. int a, h;
  512. vrect_t rcFill;
  513. ResetLineSegments();
  514. extrap_point = LERP_HEIGHT / 2;
  515. float fTickInterval = gpGlobals->interval_per_tick;
  516. for (a=0 ; a<w ; a++)
  517. {
  518. i = ( m_nServerFramerateSample - a ) & ( TIMINGS - 1 );
  519. h = extrap_point + MAX( -LERP_HEIGHT/2, MIN( ( ( svframerate[i].flSvFrameTime - fTickInterval ) * net_graphshowsvframerate.GetInt() ) * LERP_HEIGHT/2, LERP_HEIGHT/2 ) );
  520. rcFill.x = x + w -a - 1;
  521. rcFill.width = 1;
  522. rcFill.height = 1;
  523. rcFill.y = vrect.y + vrect.height - 4;
  524. if ( h >= extrap_point )
  525. {
  526. int start = 0;
  527. h -= extrap_point;
  528. rcFill.y -= extrap_point;
  529. if ( !net_graphsolid.GetInt() )
  530. {
  531. rcFill.y -= (h - 1);
  532. start = (h - 1);
  533. }
  534. for ( j = start; j < h; j++ )
  535. {
  536. DrawLine(&rcFill, colors[j + extrap_point], 255 );
  537. rcFill.y--;
  538. }
  539. }
  540. else
  541. {
  542. int oldh;
  543. oldh = h;
  544. rcFill.y -= h;
  545. h = extrap_point - h;
  546. if ( !net_graphsolid.GetInt() )
  547. {
  548. h = 1;
  549. }
  550. for ( j = 0; j < h; j++ )
  551. {
  552. DrawLine(&rcFill, colors[j + oldh], 255 );
  553. rcFill.y--;
  554. }
  555. }
  556. rcFill.y = vrect.y + vrect.height - 4 - extrap_point;
  557. DrawLine( &rcFill, extrap_base_color, 255 );
  558. rcFill.y = vrect.y + vrect.height - 3;
  559. if ( svframerate[ i ].flSvFrameRateStdDev > 1 )
  560. {
  561. DrawLine( &rcFill, sendcolor, 255 );
  562. }
  563. else
  564. {
  565. DrawLine( &rcFill, extrap_base_color, 200 );
  566. }
  567. }
  568. DrawLineSegments();
  569. }
  570. //-----------------------------------------------------------------------------
  571. // Purpose: Compute frame database for rendering m_NetChannel computes choked, and lost packets, too.
  572. // Also computes latency data and sets max packet size
  573. // Input : *packet_latency -
  574. // *graph -
  575. // *choke_count -
  576. // *loss_count -
  577. // *biggest_message -
  578. // 1 -
  579. //-----------------------------------------------------------------------------
  580. void CNetGraphPanel::GetFrameData( INetChannelInfo *netchannel, int *biggest_message, float *avg_message, float *f95thpercentile )
  581. {
  582. float frame_received_time;
  583. // float frame_latency;
  584. *biggest_message = 0;
  585. *avg_message = 0.0f;
  586. *f95thpercentile = 0.0f;
  587. int msg_count = 0;
  588. m_IncomingSequence = netchannel->GetSequenceNr( FLOW_INCOMING );
  589. m_OutgoingSequence = netchannel->GetSequenceNr( FLOW_OUTGOING );
  590. m_UpdateWindowSize = netchannel->GetBufferSize();
  591. m_AvgPacketLoss = netchannel->GetAvgLoss( FLOW_INCOMING );
  592. m_AvgPacketChoke = netchannel->GetAvgChoke( FLOW_INCOMING );
  593. m_AvgLatency = netchannel->GetAvgLatency( FLOW_OUTGOING );
  594. m_IncomingData = netchannel->GetAvgData( FLOW_INCOMING ) / 1024.0f;
  595. m_OutgoingData = netchannel->GetAvgData( FLOW_OUTGOING ) / 1024.0f;
  596. m_AvgPacketIn = netchannel->GetAvgPackets( FLOW_INCOMING );
  597. m_AvgPacketOut = netchannel->GetAvgPackets( FLOW_OUTGOING );
  598. for ( int i=0; i<MAX_FLOWS; i++ )
  599. netchannel->GetStreamProgress( i, &m_StreamRecv[i], &m_StreamTotal[i] );
  600. float flAdjust = 0.0f;
  601. if ( cl_updaterate->GetFloat() > 0.001f )
  602. {
  603. flAdjust = -0.5f / cl_updaterate->GetFloat();
  604. m_AvgLatency += flAdjust;
  605. }
  606. // Can't be below zero
  607. m_AvgLatency = MAX( 0.0, m_AvgLatency );
  608. flAdjust *= 1000.0f;
  609. // Fill in frame data
  610. for ( int seqnr =m_IncomingSequence - m_UpdateWindowSize + 1
  611. ; seqnr <= m_IncomingSequence
  612. ; seqnr++)
  613. {
  614. frame_received_time = netchannel->GetPacketTime( FLOW_INCOMING, seqnr );
  615. netbandwidthgraph_t *nbwg = &m_Graph[ seqnr & ( TIMINGS - 1 )];
  616. packet_latency_t *lat = &m_PacketLatency[ seqnr & ( TIMINGS - 1 ) ];
  617. netchannel->GetPacketResponseLatency( FLOW_INCOMING, seqnr, &lat->latency, &lat->choked );
  618. if ( lat->latency < 9995 )
  619. {
  620. lat->latency += flAdjust;
  621. lat->latency = MAX( lat->latency, 0 );
  622. }
  623. for ( int i=0; i<=INetChannelInfo::TOTAL; i++ )
  624. {
  625. nbwg->msgbytes[i] = netchannel->GetPacketBytes( FLOW_INCOMING, seqnr, i );
  626. }
  627. // Assert ( nbwg->msgbytes[INetChannelInfo::TOTAL] > 0 );
  628. if ( nbwg->msgbytes[INetChannelInfo::TOTAL] > *biggest_message )
  629. {
  630. *biggest_message = nbwg->msgbytes[INetChannelInfo::TOTAL];
  631. }
  632. *avg_message += (float)( nbwg->msgbytes[INetChannelInfo::TOTAL] );
  633. msg_count++;
  634. }
  635. if ( *biggest_message > 1000 )
  636. {
  637. *biggest_message = 1000;
  638. }
  639. if ( msg_count >= 1 )
  640. {
  641. *avg_message /= msg_count;
  642. int deviationsquared = 0;
  643. // Compute std deviation
  644. // Fill in frame data
  645. for (int seqnr=m_IncomingSequence - m_UpdateWindowSize + 1
  646. ; seqnr <= m_IncomingSequence
  647. ; seqnr++)
  648. {
  649. int bytes = m_Graph[ seqnr & ( TIMINGS - 1 )].msgbytes[INetChannelInfo::TOTAL] - ( *avg_message );
  650. deviationsquared += ( bytes * bytes );
  651. }
  652. float var = ( float )( deviationsquared ) / (float)( msg_count - 1 );
  653. float stddev = sqrt( var );
  654. *f95thpercentile = *avg_message + 2.0f * stddev;
  655. }
  656. }
  657. //-----------------------------------------------------------------------------
  658. // Purpose: Fills in command interpolation/holdback & message size data
  659. // Input : *cmdinfo -
  660. //-----------------------------------------------------------------------------
  661. void CNetGraphPanel::GetCommandInfo( INetChannelInfo *netchannel, cmdinfo_t *cmdinfo )
  662. {
  663. for ( int seqnr = m_OutgoingSequence - m_UpdateWindowSize + 1
  664. ; seqnr <= m_OutgoingSequence
  665. ; seqnr++)
  666. {
  667. // Also set up the lerp point.
  668. cmdinfo_t *ci = &cmdinfo[ seqnr & ( TIMINGS - 1 ) ];
  669. ci->cmd_lerp = netchannel->GetCommandInterpolationAmount( FLOW_OUTGOING, seqnr );
  670. ci->sent = netchannel->IsValidPacket( FLOW_OUTGOING, seqnr );
  671. ci->size = netchannel->GetPacketBytes( FLOW_OUTGOING, seqnr, INetChannelInfo::TOTAL);
  672. }
  673. }
  674. //-----------------------------------------------------------------------------
  675. // Purpose: Draws overlay text fields showing framerate, latency, bandwidth breakdowns,
  676. // and, optionally, packet loss and choked packet percentages
  677. // Input : graphvalue -
  678. // x -
  679. // y -
  680. // *graph -
  681. // *cmdinfo -
  682. // count -
  683. // avg -
  684. // *framerate -
  685. // 0.0 -
  686. // avg -
  687. //-----------------------------------------------------------------------------
  688. void CNetGraphPanel::DrawTextFields( int graphvalue, int x, int y, int w, netbandwidthgraph_t *graph, cmdinfo_t *cmdinfo )
  689. {
  690. if ( !net_graphtext.GetBool() )
  691. return;
  692. static int lastout;
  693. float fTickInterval = gpGlobals->interval_per_tick;
  694. float fTickRate = (fTickInterval > 0) ? (1.0f / fTickInterval) : 0.0f;
  695. if ( fTickRate <= 0.000001f )
  696. fTickRate = 0.000001f;
  697. char sz[ 256 ];
  698. int out;
  699. HFont font = GetNetgraphFont();
  700. // Move rolling average
  701. m_Framerate = FRAMERATE_AVG_FRAC * m_Framerate + ( 1.0 - FRAMERATE_AVG_FRAC ) * gpGlobals->absoluteframetime;
  702. // Print it out
  703. y -= m_nNetGraphHeight;
  704. int saveY = y;
  705. if ( m_Framerate <= 0.0f )
  706. m_Framerate = 1.0f;
  707. if ( engine->IsPlayingDemo() )
  708. m_AvgLatency = 0.0f;
  709. int textTall = surface()->GetFontTall( font );
  710. Q_snprintf( sz, sizeof( sz ), "fps: %5i var: %4.1f ms ping: %i ms", (int)(1.0f / m_Framerate), gpGlobals->absoluteframestarttimestddev*1000.0f, (int)(m_AvgLatency*1000.0f) );
  711. DrawColoredText( font, x, y, textColorDefault, sz );
  712. // Draw update rate
  713. color upratecolor = textColorDefault;
  714. if ( cl_updaterate->GetFloat() < fTickRate )
  715. upratecolor = GetColorFromVariance(cl_updaterate->GetFloat(), fTickRate, 0.0f, 0.2f, 0.5f);
  716. else if ( cl_updaterate->GetFloat() > fTickRate )
  717. upratecolor = textColorWarn3;
  718. Q_snprintf( sz, sizeof( sz ), "up:%5.1f/s", cl_updaterate->GetFloat() );
  719. DrawColoredText( font, x + w - TextWidth(font, sz) - 1, y, upratecolor, sz );
  720. y += textTall;
  721. int textWidth;
  722. if ( graphvalue >= 2 )
  723. {
  724. out = cmdinfo[ ( ( m_OutgoingSequence - 1 ) & ( TIMINGS - 1 ) ) ].size;
  725. if ( !out )
  726. {
  727. out = lastout;
  728. }
  729. else
  730. {
  731. lastout = out;
  732. }
  733. int totalsize = graph[ ( m_IncomingSequence & ( TIMINGS - 1 ) ) ].msgbytes[INetChannelInfo::TOTAL];
  734. Q_snprintf( sz, sizeof( sz ), "in: %5i %5.2fk/s ", totalsize, m_IncomingData );
  735. DrawColoredText( font, x, y, textColorDefault, sz );
  736. textWidth = TextWidth( font, sz );
  737. color interpcolor = textColorDefault;
  738. float flInterp = GetClientInterpAmount();
  739. if ( flInterp > 0.001f )
  740. {
  741. // flInterp is below recommended setting!!!
  742. if ( flInterp < ( 2.0f / cl_updaterate->GetFloat() ) )
  743. {
  744. interpcolor = GetColorFromVariance(flInterp, 2.0f / cl_updaterate->GetFloat(), 0.0f, 0.2f, 0.5f);
  745. }
  746. // Server tick rate lower than interp can possibly deal with
  747. if ( flInterp < fTickInterval )
  748. {
  749. interpcolor = textColorWarn3;
  750. }
  751. }
  752. Q_snprintf( sz, sizeof( sz ), "lerp: %4.1fms", GetClientInterpAmount() * 1000.0f );
  753. DrawColoredText( font, x + textWidth, y, interpcolor, sz );
  754. Q_snprintf( sz, sizeof( sz ), "%3.1f/s", m_AvgPacketIn );
  755. DrawColoredText( font, x + w - TextWidth( font, sz ) - 1, y, textColorDefault, sz );
  756. y += textTall;
  757. Q_snprintf( sz, sizeof( sz ), "out: %5i %5.2fk/s", out, m_OutgoingData );
  758. DrawColoredText( font, x, y, textColorDefault, sz );
  759. Q_snprintf( sz, sizeof( sz ), "%3.1f/s", m_AvgPacketOut );
  760. DrawColoredText( font, x + w - TextWidth( font, sz ) - 1, y, textColorDefault, sz );
  761. y += textTall;
  762. }
  763. color cmdratecolor = textColorDefault;
  764. if ( cl_cmdrate->GetFloat() < fTickRate )
  765. cmdratecolor = GetColorFromVariance(cl_cmdrate->GetFloat(), fTickRate, 0.0f, 0.2f, 0.5f);
  766. else if ( cl_cmdrate->GetFloat() > fTickRate )
  767. cmdratecolor = textColorWarn3;
  768. Q_snprintf( sz, sizeof( sz ), "cmd:%5.1f/s", cl_cmdrate->GetFloat() );
  769. DrawColoredText( GetNetgraphFont(), x + w - TextWidth(font, sz) - 1, y, cmdratecolor, sz );
  770. DrawServerType( x + w, y + textTall );
  771. Q_snprintf( sz, sizeof( sz ), "loss: %3i%% choke: %2i%%", (int)(m_AvgPacketLoss*100.0f), (int)(m_AvgPacketChoke*100.0f) );
  772. textWidth = TextWidth(font, sz);
  773. DrawColoredText( font, x, y, textColorDefault, sz );
  774. y += textTall;
  775. Q_snprintf( sz, sizeof( sz ), "tick:%5.1f ", fTickRate);
  776. DrawColoredText( font, x, y, textColorDefault, sz );
  777. int tickTextWide = TextWidth(font, sz);
  778. color servercolor = textColorDefault;
  779. if ( m_flServerFrameComputationTime > ( 1/fTickRate ) + 0.0001 )
  780. servercolor = GetColorFromVariance( m_flServerFrameComputationTime, 1/fTickRate, 0.25f, 0.5f, 0.75f);
  781. Q_snprintf( sz, sizeof( sz ), "sv:%5.1f %s%4.1f ms var: %6.3f ms", m_flServerFrameComputationTime*1000.0f,
  782. ( net_graphholdsvframerate.GetBool() ? "~/" : "+-" ),
  783. m_flServerFramerateStdDeviation * 1000.0f, m_flServerFrameStartTimeStdDeviation * 1000.0f );
  784. DrawColoredText( font, x + tickTextWide, y, servercolor, sz );
  785. y += textTall;
  786. // Draw legend
  787. if ( graphvalue >= 3 )
  788. {
  789. int textTall = g_pMatSystemSurface->GetFontTall( m_hFontSmall );
  790. y = saveY - textTall - 5;
  791. int cw, ch;
  792. g_pMatSystemSurface->GetTextSize( m_hFontSmall, L"otherplayersWWW", cw, ch );
  793. if ( x - cw < 0 )
  794. {
  795. x += w + 5;
  796. }
  797. else
  798. {
  799. x -= cw;
  800. }
  801. DrawColoredText( m_hFontSmall, x, y, color(0, 0, 255, 255), "localplayer" );
  802. y -= textTall;
  803. DrawColoredText( m_hFontSmall, x, y, color(0, 255, 0, 255), "otherplayers" );
  804. y -= textTall;
  805. DrawColoredText( m_hFontSmall, x, y, color(255, 0, 0, 255), "entities" );
  806. y -= textTall;
  807. DrawColoredText( m_hFontSmall, x, y, color(255, 255, 0, 255), "sounds" );
  808. y -= textTall;
  809. DrawColoredText( m_hFontSmall, x, y, color(0, 255, 255, 255), "events" );
  810. y -= textTall;
  811. DrawColoredText( m_hFontSmall, x, y, color(255, 0, 255, 255), "tempents" );
  812. y -= textTall;
  813. DrawColoredText( m_hFontSmall, x, y, color(128, 128, 0, 255), "usermessages" );
  814. y -= textTall;
  815. DrawColoredText( m_hFontSmall, x, y, color(0, 128, 128, 255), "entmessages" );
  816. y -= textTall;
  817. DrawColoredText( m_hFontSmall, x, y, color(128, 0, 0, 255), "stringcmds" );
  818. y -= textTall;
  819. DrawColoredText( m_hFontSmall, x, y, color(0, 128, 0, 255), "stringtables" );
  820. y -= textTall;
  821. DrawColoredText( m_hFontSmall, x, y, color(0, 0, 128, 255), "voice" );
  822. y -= textTall;
  823. }
  824. else
  825. {
  826. const CPUFrequencyResults frequency = GetCPUFrequencyResults();
  827. double currentTime = Plat_FloatTime();
  828. const double displayTime = 5.0f; // Display frequency results for this long.
  829. if ( frequency.m_GHz > 0 && frequency.m_timeStamp + displayTime > currentTime )
  830. {
  831. // Optionally print out the CPU frequency monitoring data.
  832. color cpuColor = textColorDefault;
  833. if ( frequency.m_percentage < kCPUMonitoringWarning2 )
  834. cpuColor = textColorWarn3;
  835. else if ( frequency.m_percentage < kCPUMonitoringWarning1 )
  836. cpuColor = textColorWarn2;
  837. // Experimental fading out as data becomes stale. Probably too distracting.
  838. //float age = currentTime - frequency.m_timeStamp;
  839. //cpuColor.a *= ( displayTime - age ) / displayTime;
  840. V_sprintf_safe( sz, "CPU frequency percent: %3.1f%% Min percent: %3.1f%%", frequency.m_percentage, frequency.m_lowestPercentage );
  841. DrawColoredText( font, x, y, cpuColor, sz );
  842. }
  843. }
  844. }
  845. //-----------------------------------------------------------------------------
  846. // Purpose: Determine type of graph to show, or if +graph key is held down, use detailed graph
  847. // Output : int
  848. //-----------------------------------------------------------------------------
  849. int CNetGraphPanel::GraphValue( void )
  850. {
  851. int graphtype;
  852. graphtype = net_graph.GetInt();
  853. ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( 0 );
  854. if ( !graphtype && !( in_graph.GetPerUser().state & 1 ) )
  855. return 0;
  856. // With +graph key, use max area
  857. if ( !graphtype )
  858. {
  859. graphtype = Min( sv_max_allowed_net_graph.GetInt(), 3 );
  860. }
  861. return graphtype;
  862. }
  863. //-----------------------------------------------------------------------------
  864. // Purpose: Figure out x and y position for graph based on net_graphpos
  865. // value.
  866. // Input : *rect -
  867. // width -
  868. // *x -
  869. // *y -
  870. //-----------------------------------------------------------------------------
  871. void CNetGraphPanel::GraphGetXY( vrect_t *rect, int width, int *x, int *y )
  872. {
  873. *x = rect->x + 5;
  874. switch ( net_graphpos.GetInt() )
  875. {
  876. case 0:
  877. break;
  878. case 1:
  879. *x = rect->x + rect->width - 5 - width;
  880. break;
  881. case 2:
  882. *x = rect->x + ( rect->width - 10 - width ) / 2;
  883. break;
  884. default:
  885. *x = rect->x + clamp( XRES( net_graphpos.GetInt() ), 5, rect->width - width - 5 );
  886. }
  887. *y = rect->y+rect->height - LERP_HEIGHT - 5;
  888. }
  889. //-----------------------------------------------------------------------------
  890. // Purpose: drawing stream progess (file download etc) as green bars ( under in/out)
  891. // Input : x -
  892. // y -
  893. // maxmsgbytes -
  894. //-----------------------------------------------------------------------------
  895. void CNetGraphPanel::DrawStreamProgress( int x, int y, int width )
  896. {
  897. vrect_t rcLine;
  898. rcLine.height = 1;
  899. rcLine.x = x;
  900. byte color[3]; color[0] = 0; color[1] = 200; color[2] = 0;
  901. if ( m_StreamTotal[FLOW_INCOMING] > 0 )
  902. {
  903. rcLine.y = y - m_nNetGraphHeight + 15 + 14;
  904. rcLine.width = (m_StreamRecv[FLOW_INCOMING]*width)/m_StreamTotal[FLOW_INCOMING];
  905. DrawLine( &rcLine, color, 255 );
  906. }
  907. if ( m_StreamTotal[FLOW_OUTGOING] > 0 )
  908. {
  909. rcLine.y = y - m_nNetGraphHeight + 2*15 + 14;
  910. rcLine.width = (m_StreamRecv[FLOW_OUTGOING]*width)/m_StreamTotal[FLOW_OUTGOING];
  911. DrawLine( &rcLine, color, 255 );
  912. }
  913. }
  914. //-----------------------------------------------------------------------------
  915. // Purpose: If showing bandwidth data, draw hatches big enough for largest message
  916. // Input : x -
  917. // y -
  918. // maxmsgbytes -
  919. //-----------------------------------------------------------------------------
  920. void CNetGraphPanel::DrawHatches( int x, int y, int maxmsgbytes )
  921. {
  922. int starty;
  923. int ystep;
  924. vrect_t rcHatch;
  925. byte colorminor[3];
  926. byte color[3];
  927. ystep = (int)( 10.0 / net_scale.GetFloat() );
  928. ystep = MAX( ystep, 1 );
  929. rcHatch.y = y;
  930. rcHatch.height = 1;
  931. rcHatch.x = x;
  932. rcHatch.width = 4;
  933. color[0] = 0;
  934. color[1] = 200;
  935. color[2] = 0;
  936. colorminor[0] = 63;
  937. colorminor[1] = 63;
  938. colorminor[2] = 0;
  939. for ( starty = rcHatch.y; rcHatch.y > 0 && ((starty - rcHatch.y)*net_scale.GetFloat() < ( maxmsgbytes + 50 ) ); rcHatch.y -= ystep )
  940. {
  941. if ( !((int)((starty - rcHatch.y)*net_scale.GetFloat() ) % 50 ) )
  942. {
  943. DrawLine( &rcHatch, color, 255 );
  944. }
  945. else if ( ystep > 5 )
  946. {
  947. DrawLine( &rcHatch, colorminor, 200 );
  948. }
  949. }
  950. }
  951. //-----------------------------------------------------------------------------
  952. // Purpose: State what type of server the user is playing on. dedicated, listen, offline.
  953. // Input : x -
  954. // y -
  955. //-----------------------------------------------------------------------------
  956. void CNetGraphPanel::DrawServerType( int xright, int y )
  957. {
  958. char const *psz = "offline";
  959. bool bPlayingDemo = engine->IsPlayingDemo();
  960. CDemoPlaybackParameters_t const *pParams = bPlayingDemo ? engine->GetDemoPlaybackParameters() : NULL;
  961. bool bLiveBroadcast = bPlayingDemo && pParams && pParams->m_bPlayingLiveRemoteBroadcast;
  962. if ( bPlayingDemo && !bLiveBroadcast )
  963. {
  964. psz = "demo";
  965. }
  966. else if ( engine->IsClientLocalToActiveServer() && !bLiveBroadcast )
  967. {
  968. psz = "local";
  969. }
  970. else if ( engine->IsInGame() )
  971. {
  972. INetChannelInfo *pInfo = engine->GetNetChannelInfo();
  973. bool bP2P = ( netadr_t( pInfo ? pInfo->GetAddress() : "127.0.0.1" ).GetPort() == 1 );
  974. if ( engine->IsHLTV() )
  975. {
  976. if ( CSGameRules() && CSGameRules()->IsValveDS() )
  977. psz = bLiveBroadcast ? "Official GOTV+" : "Official GOTV";
  978. else if ( bP2P )
  979. psz = bLiveBroadcast ? "P2P GOTV+" : "P2P GOTV";
  980. else
  981. psz = bLiveBroadcast ? "GOTV+" : "GOTV";
  982. }
  983. else
  984. {
  985. if ( CSGameRules() && CSGameRules()->IsValveDS() )
  986. psz = "Official DS";
  987. else if ( bP2P )
  988. psz = "P2P";
  989. else
  990. psz = "online";
  991. }
  992. }
  993. else if ( engine->IsConnected() )
  994. psz = "loading";
  995. DrawColoredText( GetNetgraphFont(), xright - TextWidth(GetNetgraphFont(), psz) - 1, y, textColorDefault, psz );
  996. }
  997. //-----------------------------------------------------------------------------
  998. // Purpose: Draws bandwidth breakdown data
  999. // Input : *rcFill -
  1000. // bytes -
  1001. // r -
  1002. // g -
  1003. // b -
  1004. // alpha -
  1005. // Output : int
  1006. //-----------------------------------------------------------------------------
  1007. int CNetGraphPanel::DrawDataSegment( vrect_t *rcFill, int bytes, byte r, byte g, byte b, byte alpha )
  1008. {
  1009. int h;
  1010. byte color[3];
  1011. h = bytes / net_scale.GetFloat();
  1012. color[0] = r;
  1013. color[1] = g;
  1014. color[2] = b;
  1015. rcFill->height = h;
  1016. rcFill->y -= h;
  1017. if ( rcFill->y < 2 )
  1018. return 0;
  1019. DrawLine( rcFill, color, alpha );
  1020. return 1;
  1021. }
  1022. //-----------------------------------------------------------------------------
  1023. // Purpose:
  1024. //-----------------------------------------------------------------------------
  1025. void CNetGraphPanel::OnTick( void )
  1026. {
  1027. bool bVisible = ShouldDraw();
  1028. if ( IsVisible() != bVisible )
  1029. {
  1030. SetVisible( bVisible );
  1031. }
  1032. }
  1033. bool CNetGraphPanel::ShouldDraw( void )
  1034. {
  1035. if ( GraphValue() != 0 )
  1036. return true;
  1037. return false;
  1038. }
  1039. void CNetGraphPanel::DrawLargePacketSizes( int x, int w, int graphtype, float warning_threshold )
  1040. {
  1041. vrect_t rcFill = {0,0,0,0};
  1042. int a, i;
  1043. for (a=0 ; a<w ; a++)
  1044. {
  1045. i = (m_IncomingSequence-a) & ( TIMINGS - 1 );
  1046. rcFill.x = x + w -a -1;
  1047. rcFill.width = 1;
  1048. rcFill.y = m_Graph[i].sampleY;
  1049. rcFill.height = m_Graph[i].sampleHeight;
  1050. int nTotalBytes = m_Graph[ i ].msgbytes[ INetChannelInfo::TOTAL ];
  1051. if ( warning_threshold != 0.0f &&
  1052. nTotalBytes > MAX( 300, warning_threshold ) )
  1053. {
  1054. char sz[ 32 ];
  1055. Q_snprintf( sz, sizeof( sz ), "%i", nTotalBytes );
  1056. int len = TextWidth(m_hFont, sz );
  1057. int textx, texty;
  1058. textx = rcFill.x - len / 2;
  1059. texty = MAX( 0, rcFill.y - 11 );
  1060. DrawColoredText( m_hFont, textx, texty, color(255, 255, 255, 255), sz );
  1061. }
  1062. }
  1063. }
  1064. //-----------------------------------------------------------------------------
  1065. // Purpose:
  1066. //-----------------------------------------------------------------------------
  1067. void CNetGraphPanel::Paint()
  1068. {
  1069. VPROF( "CNetGraphPanel::Paint" );
  1070. int graphtype;
  1071. int x, y;
  1072. int w;
  1073. vrect_t vrect;
  1074. int maxmsgbytes = 0;
  1075. float avg_message = 0.0f;
  1076. float warning_threshold = 0.0f;
  1077. if ( ( graphtype = GraphValue() ) == 0 )
  1078. return;
  1079. // Since we divide by scale, make sure it's sensible
  1080. if ( net_scale.GetFloat() <= 0 )
  1081. {
  1082. net_scale.SetValue( 0.1f );
  1083. }
  1084. // Get screen rectangle
  1085. int sw, sh;
  1086. surface()->GetScreenSize( sw, sh );
  1087. if ( IsGameConsole() )
  1088. {
  1089. // shrink for titlesafe
  1090. int insetX = XBOX_MINBORDERSAFE * (float)sw;
  1091. int insetY = XBOX_MINBORDERSAFE * (float)sh;
  1092. vrect.x = insetX;
  1093. vrect.y = insetY;
  1094. vrect.width = sw - 2 * insetX;
  1095. vrect.height = sh - 2 * insetY;
  1096. }
  1097. else
  1098. {
  1099. vrect.x = 0;
  1100. vrect.y = 0;
  1101. vrect.width = sw;
  1102. vrect.height = sh;
  1103. }
  1104. w = MIN( (int)TIMINGS, m_EstimatedWidth );
  1105. if ( vrect.width < w + 10 )
  1106. {
  1107. w = vrect.width - 10;
  1108. }
  1109. // get current client netchannel INetChannelInfo interface
  1110. INetChannelInfo *nci = engine->GetNetChannelInfo();
  1111. if ( nci )
  1112. {
  1113. // update incoming data
  1114. GetFrameData( nci, &maxmsgbytes, &avg_message, &warning_threshold );
  1115. // update outgoing data
  1116. GetCommandInfo( nci, m_Cmdinfo );
  1117. UpdateEstimatedServerFramerate( nci );
  1118. }
  1119. GraphGetXY( &vrect, w, &x, &y );
  1120. if ( graphtype >= 3 )
  1121. {
  1122. PaintLineArt( x, y, w, graphtype, maxmsgbytes );
  1123. DrawLargePacketSizes( x, w, graphtype, warning_threshold );
  1124. }
  1125. // Draw client frame timing info
  1126. DrawTimes( vrect, m_Cmdinfo, x, w, graphtype );
  1127. DrawSvFrameRate( vrect, m_SvFrameRate, x, w, graphtype );
  1128. DrawTextFields( graphtype, x, y, w, m_Graph, m_Cmdinfo );
  1129. }
  1130. //-----------------------------------------------------------------------------
  1131. // Purpose:
  1132. //-----------------------------------------------------------------------------
  1133. void CNetGraphPanel::PaintLineArt( int x, int y, int w, int graphtype, int maxmsgbytes )
  1134. {
  1135. VPROF( "CNetGraphPanel::PaintLineArt" );
  1136. ResetLineSegments();
  1137. int lastvalidh = 0;
  1138. byte color[3];
  1139. int ping;
  1140. byte alpha;
  1141. vrect_t rcFill = {0,0,0,0};
  1142. int pingheight = m_nNetGraphHeight - LERP_HEIGHT - 2;
  1143. if (net_graphmsecs.GetInt() < 50 )
  1144. {
  1145. net_graphmsecs.SetValue( 50 );
  1146. }
  1147. bool bShowLatency = net_graphshowlatency.GetBool() && graphtype >= 3;
  1148. for (int a=0 ; a<w ; a++)
  1149. {
  1150. int i = (m_IncomingSequence-a) & ( TIMINGS - 1 );
  1151. int h = bShowLatency ? m_PacketLatency[i].latency : 0;
  1152. packet_latency_t *pl = &m_PacketLatency[ i ];
  1153. ColorForHeight( pl, color, &ping, &alpha );
  1154. // Skipped
  1155. if ( !ping )
  1156. {
  1157. // Re-use the last latency
  1158. h = lastvalidh;
  1159. }
  1160. else
  1161. {
  1162. h = pingheight * (float)h/net_graphmsecs.GetFloat();
  1163. lastvalidh = h;
  1164. }
  1165. if ( h > pingheight )
  1166. {
  1167. h = pingheight;
  1168. }
  1169. rcFill.x = x + w -a -1;
  1170. rcFill.y = y - h;
  1171. rcFill.width = 1;
  1172. rcFill.height = h;
  1173. if ( ping )
  1174. {
  1175. rcFill.height = pl->choked ? 2 : 1;
  1176. }
  1177. if ( !ping )
  1178. {
  1179. DrawLine2(&rcFill, color, color, alpha, 31 );
  1180. }
  1181. else
  1182. {
  1183. DrawLine(&rcFill, color, alpha );
  1184. }
  1185. rcFill.y = y;
  1186. rcFill.height = 1;
  1187. color[0] = 0;
  1188. color[1] = 255;
  1189. color[2] = 0;
  1190. DrawLine( &rcFill, color, 160 );
  1191. if ( graphtype < 3 )
  1192. continue;
  1193. // Draw a separator.
  1194. rcFill.y = y - m_nNetGraphHeight - 1;
  1195. rcFill.height = 1;
  1196. color[0] = 255;
  1197. color[1] = 255;
  1198. color[2] = 255;
  1199. DrawLine(&rcFill, color, 255 );
  1200. // Move up for begining of data
  1201. rcFill.y -= 1;
  1202. // Packet didn't have any real data...
  1203. if ( m_PacketLatency[i].latency > 9995 )
  1204. continue;
  1205. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::LOCALPLAYER], 0, 0, 255 ) )
  1206. continue;
  1207. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::OTHERPLAYERS], 0, 255, 0 ) )
  1208. continue;
  1209. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::ENTITIES], 255, 0, 0 ) )
  1210. continue;
  1211. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::SOUNDS], 255, 255, 0) )
  1212. continue;
  1213. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::EVENTS], 0, 255, 255 ) )
  1214. continue;
  1215. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::TEMPENTS], 255, 0, 255 ) )
  1216. continue;
  1217. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::USERMESSAGES], 128, 128, 0 ) )
  1218. continue;
  1219. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::ENTMESSAGES], 0, 128, 128 ) )
  1220. continue;
  1221. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::STRINGCMD], 128, 0, 0) )
  1222. continue;
  1223. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::STRINGTABLE], 0, 128, 0) )
  1224. continue;
  1225. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::VOICE], 0, 0, 128 ) )
  1226. continue;
  1227. if ( !DrawDataSegment( &rcFill, m_Graph[ i ].msgbytes[INetChannelInfo::PAINTMAP], 64, 0, 0 ) )
  1228. continue;
  1229. // Final data chunk is total size, don't use solid line routine for this
  1230. h = m_Graph[i].msgbytes[INetChannelInfo::TOTAL] / net_scale.GetFloat();
  1231. color[ 0 ] = color[ 1 ] = color[ 2 ] = 240;
  1232. rcFill.height = 1;
  1233. rcFill.y = y - m_nNetGraphHeight - 1 - h;
  1234. if ( rcFill.y < 2 )
  1235. continue;
  1236. DrawLine(&rcFill, color, 128 );
  1237. // Cache off height
  1238. m_Graph[i].sampleY = rcFill.y;
  1239. m_Graph[i].sampleHeight = rcFill.height;
  1240. }
  1241. if ( graphtype >= 3 )
  1242. {
  1243. // Draw hatches for first one:
  1244. // on the far right side
  1245. DrawHatches( x, y - m_nNetGraphHeight - 1, maxmsgbytes );
  1246. DrawStreamProgress( x, y, w );
  1247. }
  1248. DrawLineSegments();
  1249. }
  1250. //-----------------------------------------------------------------------------
  1251. // Purpose:
  1252. //-----------------------------------------------------------------------------
  1253. void CNetGraphPanel::ResetLineSegments()
  1254. {
  1255. m_Rects.RemoveAll();
  1256. }
  1257. //-----------------------------------------------------------------------------
  1258. // Purpose:
  1259. //-----------------------------------------------------------------------------
  1260. void CNetGraphPanel::DrawLineSegments()
  1261. {
  1262. int c = m_Rects.Count();
  1263. if ( c <= 0 )
  1264. return;
  1265. int start = 0;
  1266. while ( start < c )
  1267. {
  1268. int consume = MIN( 5000, c - start );
  1269. CMatRenderContextPtr pRenderContext( materials );
  1270. IMesh* m_pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_WhiteMaterial );
  1271. CMeshBuilder meshBuilder;
  1272. meshBuilder.Begin( m_pMesh, MATERIAL_LINES, c );
  1273. int i;
  1274. for ( i = start ; i < start + consume; i++ )
  1275. {
  1276. CLineSegment *seg = &m_Rects[ i ];
  1277. meshBuilder.Color4ubv( seg->color );
  1278. meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
  1279. meshBuilder.Position3f( seg->x1, seg->y1, 0 );
  1280. meshBuilder.AdvanceVertex();
  1281. meshBuilder.Color4ubv( seg->color2 );
  1282. meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
  1283. meshBuilder.Position3f( seg->x2, seg->y2, 0 );
  1284. meshBuilder.AdvanceVertex();
  1285. }
  1286. meshBuilder.End();
  1287. m_pMesh->Draw();
  1288. start += consume;
  1289. }
  1290. }
  1291. //-----------------------------------------------------------------------------
  1292. // Purpose: Draws a colored, filled rectangle
  1293. // Input : *rect -
  1294. // *color -
  1295. // alpha -
  1296. //-----------------------------------------------------------------------------
  1297. void CNetGraphPanel::DrawLine( vrect_t *rect, unsigned char *color, unsigned char alpha )
  1298. {
  1299. DrawLine2( rect, color, color, alpha, alpha );
  1300. }
  1301. //-----------------------------------------------------------------------------
  1302. // Purpose: Draws a colored, filled rectangle
  1303. // Input : *rect -
  1304. // *color -
  1305. // alpha -
  1306. //-----------------------------------------------------------------------------
  1307. void CNetGraphPanel::DrawLine2( vrect_t *rect, unsigned char *color, unsigned char *color2, unsigned char alpha, unsigned char alpha2 )
  1308. {
  1309. VPROF( "CNetGraphPanel::DrawLine2" );
  1310. int idx = m_Rects.AddToTail();
  1311. CLineSegment *seg = &m_Rects[ idx ];
  1312. seg->color[0] = color[0];
  1313. seg->color[1] = color[1];
  1314. seg->color[2] = color[2];
  1315. seg->color[3] = alpha;
  1316. seg->color2[0] = color2[0];
  1317. seg->color2[1] = color2[1];
  1318. seg->color2[2] = color2[2];
  1319. seg->color2[3] = alpha2;
  1320. if ( rect->width == 1 )
  1321. {
  1322. seg->x1 = rect->x;
  1323. seg->y1 = rect->y;
  1324. seg->x2 = rect->x;
  1325. seg->y2 = rect->y + rect->height;
  1326. }
  1327. else if ( rect->height == 1 )
  1328. {
  1329. seg->x1 = rect->x;
  1330. seg->y1 = rect->y;
  1331. seg->x2 = rect->x + rect->width;
  1332. seg->y2 = rect->y;
  1333. }
  1334. else
  1335. {
  1336. Assert( 0 );
  1337. m_Rects.Remove( idx );
  1338. }
  1339. }
  1340. void CNetGraphPanel::UpdateEstimatedServerFramerate( INetChannelInfo *netchannel )
  1341. {
  1342. netchannel->GetRemoteFramerate( &m_flServerFrameComputationTime, &m_flServerFramerateStdDeviation, &m_flServerFrameStartTimeStdDeviation );
  1343. m_SvFrameRate[ m_nServerFramerateSample % TIMINGS ].flSvFrameTime = m_flServerFrameComputationTime;
  1344. m_SvFrameRate[ m_nServerFramerateSample % TIMINGS ].flSvFrameRateStdDev = m_flServerFramerateStdDeviation;
  1345. m_SvFrameRate[ m_nServerFramerateSample % TIMINGS ].flSvFrameStartTimeStdDev = m_flServerFrameStartTimeStdDeviation;
  1346. ++ m_nServerFramerateSample;
  1347. if ( net_graphholdsvframerate.GetBool() )
  1348. {
  1349. for ( int j = 0; j < TIMINGS; ++ j )
  1350. {
  1351. if ( m_SvFrameRate[j].flSvFrameTime > m_flServerFrameComputationTime )
  1352. m_flServerFrameComputationTime = m_SvFrameRate[j].flSvFrameTime;
  1353. if ( m_SvFrameRate[j].flSvFrameRateStdDev > m_flServerFramerateStdDeviation )
  1354. m_flServerFramerateStdDeviation = m_SvFrameRate[j].flSvFrameRateStdDev;
  1355. if ( m_SvFrameRate[ j ].flSvFrameStartTimeStdDev > m_flServerFrameStartTimeStdDeviation )
  1356. m_flServerFrameStartTimeStdDeviation = m_SvFrameRate[ j ].flSvFrameStartTimeStdDev;
  1357. }
  1358. }
  1359. }
  1360. CNetGraphPanel::color CNetGraphPanel::GetColorFromVariance( float fValue, float fBase, float fVariance1, float fVariance2, float fVariance3 )
  1361. {
  1362. if ( fBase == 0.0f )
  1363. return textColorDefault;
  1364. float fDelta = fabsf(fBase - fValue);
  1365. float fDeltaFraction = fDelta / fBase;
  1366. if ( fDeltaFraction > fVariance3 )
  1367. return textColorWarn3;
  1368. if ( fDeltaFraction > fVariance2 )
  1369. return textColorWarn2;
  1370. if ( fDeltaFraction > fVariance1 )
  1371. return textColorWarn1;
  1372. return textColorDefault;
  1373. }
  1374. class CNetGraphPanelInterface : public INetGraphPanel
  1375. {
  1376. private:
  1377. CNetGraphPanel *netGraphPanel;
  1378. public:
  1379. CNetGraphPanelInterface( void )
  1380. {
  1381. netGraphPanel = NULL;
  1382. }
  1383. void Create( VPANEL parent )
  1384. {
  1385. netGraphPanel = new CNetGraphPanel( parent );
  1386. }
  1387. void Destroy( void )
  1388. {
  1389. if ( netGraphPanel )
  1390. {
  1391. netGraphPanel->SetParent( (Panel *)NULL );
  1392. delete netGraphPanel;
  1393. }
  1394. }
  1395. };
  1396. static CNetGraphPanelInterface g_NetGraphPanel;
  1397. INetGraphPanel *netgraphpanel = ( INetGraphPanel * )&g_NetGraphPanel;