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.

1271 lines
35 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: VProf engine integration
  4. //
  5. //===========================================================================//
  6. #include "tier0/platform.h"
  7. #include "sys.h"
  8. #include "vprof_engine.h"
  9. #include "sv_main.h"
  10. #include "iengine.h"
  11. #include "basetypes.h"
  12. #include "convar.h"
  13. #include "cmd.h"
  14. #include "tier1/strtools.h"
  15. #include "con_nprint.h"
  16. #include "tier0/vprof.h"
  17. #include "materialsystem/imaterialsystem.h"
  18. #ifndef DEDICATED
  19. #include "vgui_baseui_interface.h"
  20. #include "vgui_vprofpanel.h"
  21. #endif
  22. #include "utlvector.h"
  23. #include "sv_remoteaccess.h"
  24. #include "ivprofexport.h"
  25. #include "vprof_record.h"
  26. #include "filesystem_engine.h"
  27. #include "tier1/utlstring.h"
  28. #include "tier1/utlvector.h"
  29. #include "debugoverlay.h"
  30. #include "fmtstr.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. #ifdef VPROF_ENABLED
  34. void VProfExport_StartOrStop();
  35. static ConVar vprof_dump_spikes( "vprof_dump_spikes","0", 0, "Framerate at which vprof will begin to dump spikes to the console. 0 = disabled, negative to reset after dump" );
  36. static ConVar vprof_dump_spikes_terse( "vprof_dump_spikes_terse","0", 0, "Whether to use most terse output" );
  37. static ConVar vprof_dump_spikes_hierarchy( "vprof_dump_spikes_hiearchy","0", 0, "Set to 1 to get a hierarchy report whith vprof_dump_spikes" );
  38. static ConVar vprof_dump_spikes_node( "vprof_dump_spikes_node","", 0, "Node to start report from when doing a dump spikes" );
  39. static ConVar vprof_dump_spikes_budget_group( "vprof_dump_spikes_budget_group","", 0, "Budget gtNode to start report from when doing a dump spikes" );
  40. static ConVar vprof_dump_oninterval( "vprof_dump_oninterval", "0", 0, "Interval (in seconds) at which vprof will batch up data and dump it to the console." );
  41. static void (*g_pfnDeferredOp)();
  42. static void ExecuteDeferredOp()
  43. {
  44. if ( g_pfnDeferredOp )
  45. {
  46. (*g_pfnDeferredOp)();
  47. g_pfnDeferredOp = NULL;
  48. }
  49. }
  50. unsigned g_VProfTargetThread = ThreadGetCurrentId();
  51. const double MAX_SPIKE_REPORT = 1.0;
  52. const int MAX_SPIKE_REPORT_FRAMES = 10;
  53. static double LastSpikeTime = 0;
  54. static int LastSpikeFrame = 0;
  55. static ConVar vprof_counters( "vprof_counters", "0", 0 );
  56. static ConVar vprof_counters_show_minmax( "vprof_counters_show_minmax", "0", 0 );
  57. extern bool con_debuglog;
  58. extern ConVar con_logfile;
  59. static bool g_fVprofOnByUI;
  60. static bool g_bVProfNoVSyncOff = false;
  61. class ConsoleLogger
  62. {
  63. public:
  64. ConsoleLogger( void )
  65. {
  66. #ifndef _X360
  67. #if !defined( DEDICATED )
  68. m_condebugEnabled = con_debuglog;
  69. #else
  70. m_condebugEnabled = false;
  71. #endif
  72. if ( !m_condebugEnabled )
  73. {
  74. g_pFileSystem->CreateDirHierarchy( "vprof" );
  75. while ( 1 )
  76. {
  77. ++m_index;
  78. const char *fname = va( "vprof/vprof%d.txt", m_index );
  79. if ( g_pFileSystem->FileExists( fname ) )
  80. {
  81. continue;
  82. }
  83. #if !defined( DEDICATED )
  84. con_logfile.SetValue( fname );
  85. #endif
  86. break;
  87. }
  88. }
  89. #endif
  90. }
  91. ~ConsoleLogger()
  92. {
  93. #ifndef _X360
  94. if ( !m_condebugEnabled )
  95. {
  96. #if !defined( DEDICATED )
  97. con_logfile.SetValue( "" );
  98. #endif
  99. }
  100. #endif
  101. }
  102. private:
  103. static int m_index;
  104. bool m_condebugEnabled;
  105. };
  106. int ConsoleLogger::m_index = 0;
  107. static float s_flIntervalStartTime = 0.0f;
  108. static bool g_bDumpCounters = false;
  109. CON_COMMAND(vprof_dump_counters, "Dump vprof counters to the console" )
  110. {
  111. g_bDumpCounters = true;
  112. }
  113. void PreUpdateProfile( float filteredtime )
  114. {
  115. Assert( g_VProfCurrentProfile.AtRoot() );
  116. ExecuteDeferredOp();
  117. VProfExport_StartOrStop();
  118. VProfRecord_StartOrStop();
  119. if ( g_VProfCurrentProfile.GetTargetThreadId() != g_VProfTargetThread )
  120. {
  121. g_VProfCurrentProfile.SetTargetThreadId( g_VProfTargetThread );
  122. }
  123. // Check to see if it is time to dump the data and restart collection.
  124. if ( g_VProfCurrentProfile.IsEnabled() && ( vprof_dump_oninterval.GetFloat() != 0.0f ) )
  125. {
  126. float flCurrentTime = eng->GetCurTime();
  127. float flIntervalTime = vprof_dump_oninterval.GetFloat();
  128. g_VProfCurrentProfile.MarkFrame();
  129. if ( ( s_flIntervalStartTime + flIntervalTime ) < flCurrentTime )
  130. {
  131. // Dump the current profile.
  132. g_VProfCurrentProfile.OutputReport( VPRT_SUMMARY | VPRT_LIST_BY_TIME | VPRT_LIST_BY_AVG_TIME | VPRT_LIST_BY_TIME_LESS_CHILDREN | VPRT_LIST_TOP_ITEMS_ONLY );
  133. // Stop the current profile.
  134. g_VProfCurrentProfile.Stop();
  135. // Reset and restart the current profile.
  136. g_VProfCurrentProfile.Reset();
  137. g_VProfCurrentProfile.Start();
  138. s_flIntervalStartTime = flCurrentTime;
  139. }
  140. }
  141. if( g_VProfCurrentProfile.IsEnabled() && vprof_dump_spikes.GetFloat() )
  142. {
  143. float spikeThreash = fabsf( vprof_dump_spikes.GetFloat() );
  144. g_VProfCurrentProfile.MarkFrame();
  145. bool bSuppressRestart = false;
  146. if ( g_VProfSignalSpike || eng->GetFrameTime() > ( 1.f / spikeThreash ) )
  147. {
  148. if( g_VProfSignalSpike || ( Sys_FloatTime() - LastSpikeTime > MAX_SPIKE_REPORT && g_ServerGlobalVariables.framecount > LastSpikeFrame + MAX_SPIKE_REPORT_FRAMES ) )
  149. {
  150. ConsoleLogger consoleLog;
  151. Msg( "******** Spike on frame %d at time %.3f ", g_ServerGlobalVariables.framecount, Plat_FloatTime() );
  152. if ( vprof_dump_spikes_hierarchy.GetBool() )
  153. {
  154. g_VProfCurrentProfile.OutputReport( VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY,
  155. ( vprof_dump_spikes_node.GetString()[0] ) ? vprof_dump_spikes_node.GetString() : NULL,
  156. ( vprof_dump_spikes_budget_group.GetString()[0] ) ? g_VProfCurrentProfile.BudgetGroupNameToBudgetGroupID( vprof_dump_spikes_budget_group.GetString() ) : -1 );
  157. }
  158. else
  159. {
  160. int flags;
  161. if ( !vprof_dump_spikes_terse.GetBool() )
  162. {
  163. flags = VPRT_SUMMARY | VPRT_LIST_BY_TIME | VPRT_LIST_BY_AVG_TIME | VPRT_LIST_BY_TIME_LESS_CHILDREN | VPRT_LIST_TOP_ITEMS_ONLY;
  164. }
  165. else
  166. {
  167. flags = VPRT_LIST_BY_TIME | VPRT_LIST_TOP_ITEMS_ONLY;
  168. }
  169. g_VProfCurrentProfile.OutputReport( flags,
  170. ( vprof_dump_spikes_node.GetString()[0] ) ? vprof_dump_spikes_node.GetString() : NULL,
  171. ( vprof_dump_spikes_budget_group.GetString()[0] ) ? g_VProfCurrentProfile.BudgetGroupNameToBudgetGroupID( vprof_dump_spikes_budget_group.GetString() ) : -1 );
  172. }
  173. LastSpikeTime = Sys_FloatTime();
  174. LastSpikeFrame = g_ServerGlobalVariables.framecount;
  175. if ( vprof_dump_spikes.GetFloat() < 0.0 )
  176. {
  177. vprof_dump_spikes.SetValue( 0.0f );
  178. // g_VProfCurrentProfile.Stop();
  179. g_fVprofOnByUI = false;
  180. bSuppressRestart = true;
  181. }
  182. }
  183. g_VProfSignalSpike = false;
  184. }
  185. int iStartDepth = 0;
  186. do
  187. {
  188. g_VProfCurrentProfile.Stop();
  189. iStartDepth++;
  190. } while( g_VProfCurrentProfile.IsEnabled() );
  191. if (!bSuppressRestart)
  192. {
  193. g_VProfCurrentProfile.Reset();
  194. while ( iStartDepth-- )
  195. {
  196. g_VProfCurrentProfile.Start();
  197. }
  198. }
  199. Assert( g_VProfCurrentProfile.AtRoot() );
  200. Assert( g_VProfCurrentProfile.IsEnabled() );
  201. }
  202. int nCounterType = vprof_counters.GetInt();
  203. if ( nCounterType || g_bDumpCounters )
  204. {
  205. int i;
  206. int n = g_VProfCurrentProfile.GetNumCounters();
  207. int nprintIndex = 0;
  208. int static nCycle = 0;
  209. for ( i = 0; i < n; i++ )
  210. {
  211. if ( g_VProfCurrentProfile.GetCounterGroup( i ) != ( nCounterType - 1 ) )
  212. continue;
  213. const char *pName;
  214. int val;
  215. pName = g_VProfCurrentProfile.GetCounterNameAndValue( i, val );
  216. if ( g_bDumpCounters )
  217. {
  218. Msg("VPROF: %s = %d\n", pName, val );
  219. }
  220. if ( !vprof_counters_show_minmax.GetBool() )
  221. {
  222. if ( IsPC() )
  223. {
  224. Con_NPrintf( nprintIndex, "%s = %d\n", pName, val );
  225. }
  226. else if ( IsGameConsole() )
  227. {
  228. #ifndef DEDICATED
  229. CDebugOverlay::AddScreenTextOverlay( 0.05f, 0.05f, nprintIndex, 0.001f, 255, 255, 255, 255, CFmtStr( "%s = %d", pName, val ) );
  230. #endif
  231. }
  232. }
  233. else
  234. {
  235. static CUtlVector<int> history[30];
  236. history[nCycle].EnsureCount( n );
  237. history[nCycle][i] = val;
  238. int valMin = val;
  239. int valMax = val;
  240. for (int j = 0; j < 30; j++)
  241. {
  242. history[j].EnsureCount( n );
  243. valMin = MIN( valMin, history[j][i] );
  244. valMax = MAX( valMax, history[j][i] );
  245. }
  246. if ( IsPC() )
  247. {
  248. Con_NPrintf( nprintIndex, "%s = %6d (%6d:%6d)\n", pName, val, valMin, valMax );
  249. }
  250. else if ( IsGameConsole() )
  251. {
  252. #ifndef DEDICATED
  253. CDebugOverlay::AddScreenTextOverlay( 0.05f, 0.05f, nprintIndex, 0.001f, 255, 255, 255, 255, CFmtStr( "%s = %6d (%6d:%6d)", pName, val, valMin, valMax ) );
  254. #endif
  255. }
  256. }
  257. nprintIndex++;
  258. }
  259. nCycle = (nCycle + 1) % 30;
  260. }
  261. g_VProfCurrentProfile.ResetCounters( COUNTER_GROUP_DEFAULT );
  262. g_VProfCurrentProfile.ResetCounters( COUNTER_GROUP_TEXTURE_PER_FRAME );
  263. g_VProfCurrentProfile.ResetCounters( COUNTER_GROUP_GRAPHICS_PER_FRAME );
  264. // This MUST come before GetVProfPanel()->UpdateProfile(), because UpdateProfile uses the data we snapshot here.
  265. VProfExport_SnapshotVProfHistory();
  266. #ifdef VPROF_ENABLED
  267. VProfRecord_Snapshot();
  268. #endif
  269. #ifndef DEDICATED
  270. // Update the vgui panel
  271. if ( GetVProfPanel() )
  272. GetVProfPanel()->UpdateProfile( filteredtime );
  273. #endif
  274. g_bDumpCounters = false;
  275. }
  276. void PostUpdateProfile()
  277. {
  278. if ( g_VProfCurrentProfile.IsEnabled() && !vprof_dump_spikes.GetFloat() && !vprof_dump_oninterval.GetFloat() )
  279. {
  280. g_VProfCurrentProfile.MarkFrame();
  281. }
  282. }
  283. #if defined( VPROF_VXCONSOLE_EXISTS )
  284. void UpdateVXConsoleProfile()
  285. {
  286. g_VProfCurrentProfile.VXProfileUpdate();
  287. }
  288. #endif
  289. static bool g_fVprofCacheMissOnByUI = false;
  290. static char g_szDefferedArg1[128];
  291. static char g_szDefferedArg2[128];
  292. #define DEFERRED_CON_COMMAND( cmd, help ) \
  293. static void cmd##_Impl(); \
  294. CON_COMMAND(cmd, help) \
  295. { \
  296. g_pfnDeferredOp = cmd##_Impl; \
  297. Q_strncpy( g_szDefferedArg1, args[1], sizeof(g_szDefferedArg1) ); \
  298. Q_strncpy( g_szDefferedArg2, args[2], sizeof(g_szDefferedArg2) ); \
  299. } \
  300. static void cmd##_Impl()
  301. CON_COMMAND_F( spike,"generates a fake spike", FCVAR_CHEAT )
  302. {
  303. Sys_Sleep(1000);
  304. }
  305. CON_COMMAND( vprof_vtune_group, "enable vtune for a particular vprof group (\"disable\" to disable)" )
  306. {
  307. if( args.ArgC() != 2 )
  308. {
  309. Warning( "vprof_vtune_group groupName (disable to turn off)\n" );
  310. return;
  311. }
  312. const char *pArg = args[ 1 ];
  313. if( Q_stricmp( pArg, "disable" ) == 0 )
  314. {
  315. g_VProfCurrentProfile.DisableVTuneGroup();
  316. }
  317. else
  318. {
  319. g_VProfCurrentProfile.EnableVTuneGroup( args[ 1 ] );
  320. }
  321. }
  322. CON_COMMAND( vprof_dump_groupnames, "Write the names of all of the vprof groups to the console." )
  323. {
  324. int n = g_VProfCurrentProfile.GetNumBudgetGroups();
  325. int i;
  326. for( i = 0; i < n; i++ )
  327. {
  328. Msg( "group %d: \"%s\"\n", i, g_VProfCurrentProfile.GetBudgetGroupName( i ) );
  329. }
  330. }
  331. DEFERRED_CON_COMMAND( vprof_cachemiss, "Toggle VProf cache miss checking" )
  332. {
  333. if ( !g_fVprofCacheMissOnByUI )
  334. {
  335. Msg("VProf cache miss enabled.\n");
  336. g_VProfCurrentProfile.PMEEnable( true );
  337. g_fVprofCacheMissOnByUI = true;
  338. }
  339. else
  340. {
  341. Msg("VProf cache miss disabled.\n");
  342. g_VProfCurrentProfile.PMEEnable( false );
  343. g_fVprofCacheMissOnByUI = false;
  344. }
  345. }
  346. DEFERRED_CON_COMMAND( vprof_cachemiss_on, "Turn on VProf cache miss checking" )
  347. {
  348. if ( !g_fVprofCacheMissOnByUI )
  349. {
  350. Msg("VProf cache miss enabled.\n");
  351. g_VProfCurrentProfile.PMEEnable( true );
  352. g_fVprofCacheMissOnByUI = true;
  353. }
  354. }
  355. DEFERRED_CON_COMMAND( vprof_cachemiss_off, "Turn off VProf cache miss checking" )
  356. {
  357. if ( g_fVprofCacheMissOnByUI )
  358. {
  359. Msg("VProf cache miss disabled.\n");
  360. g_VProfCurrentProfile.PMEEnable( false );
  361. g_fVprofCacheMissOnByUI = false;
  362. }
  363. }
  364. DEFERRED_CON_COMMAND( vprof, "Toggle VProf profiler" )
  365. {
  366. if ( !g_fVprofOnByUI )
  367. {
  368. Msg("VProf enabled.\n");
  369. g_VProfCurrentProfile.Start();
  370. g_fVprofOnByUI = true;
  371. }
  372. else
  373. {
  374. Msg("VProf disabled.\n");
  375. g_VProfCurrentProfile.Stop();
  376. g_fVprofOnByUI = false;
  377. }
  378. }
  379. #ifdef _X360
  380. DEFERRED_CON_COMMAND( vprof_360_novsync_off, "Leaves vsync on when vxconsole brings up showbudget." )
  381. {
  382. g_bVProfNoVSyncOff = !g_bVProfNoVSyncOff;
  383. Msg("VProf novsync auto setting %s.\n", g_bVProfNoVSyncOff ? "disabled" : "enabled" );
  384. }
  385. DEFERRED_CON_COMMAND( vprof_360_show_time, "Shows time in vprof" )
  386. {
  387. g_VProfCurrentProfile.VXConsoleReportMode( CVProfile::VXCONSOLE_REPORT_TIME );
  388. }
  389. DEFERRED_CON_COMMAND( vprof_360_show_cachemiss, "Shows cachemisses in vprof" )
  390. {
  391. if ( !g_fVprofCacheMissOnByUI )
  392. {
  393. Msg("VProf cache miss enabled.\n");
  394. g_VProfCurrentProfile.PMEEnable( true );
  395. g_fVprofCacheMissOnByUI = true;
  396. }
  397. g_VProfCurrentProfile.VXConsoleReportMode( CVProfile::VXCONSOLE_REPORT_L2CACHE_MISSES );
  398. }
  399. DEFERRED_CON_COMMAND( vprof_360_show_loadhitstore, "Shows load-hit-stores in vprof" )
  400. {
  401. if ( !g_fVprofCacheMissOnByUI )
  402. {
  403. Msg("VProf cache miss enabled.\n");
  404. g_VProfCurrentProfile.PMEEnable( true );
  405. g_fVprofCacheMissOnByUI = true;
  406. }
  407. g_VProfCurrentProfile.VXConsoleReportMode( CVProfile::VXCONSOLE_REPORT_LOAD_HIT_STORE );
  408. }
  409. DEFERRED_CON_COMMAND( vprof_360_time_scale, "Scale used when displaying time (0 = use default)" )
  410. {
  411. float flScale = atof(g_szDefferedArg1);
  412. if ( flScale <= 0.0f )
  413. {
  414. flScale = 1000.0f;
  415. }
  416. g_VProfCurrentProfile.VXConsoleReportScale( CVProfile::VXCONSOLE_REPORT_TIME, flScale );
  417. }
  418. DEFERRED_CON_COMMAND( vprof_360_cachemiss_scale, "Scale used when displaying cachemisses (0 = use default)" )
  419. {
  420. float flScale = atof(g_szDefferedArg1);
  421. if ( flScale <= 0.0f )
  422. {
  423. flScale = 1.0f;
  424. }
  425. g_VProfCurrentProfile.VXConsoleReportScale( CVProfile::VXCONSOLE_REPORT_L2CACHE_MISSES, flScale );
  426. }
  427. DEFERRED_CON_COMMAND( vprof_360_loadhitstore_scale, "Scale used when displaying load-hit-stores (0 = use default)" )
  428. {
  429. float flScale = atof(g_szDefferedArg1);
  430. if ( flScale <= 0.0f )
  431. {
  432. flScale = 0.1f;
  433. }
  434. g_VProfCurrentProfile.VXConsoleReportScale( CVProfile::VXCONSOLE_REPORT_LOAD_HIT_STORE, flScale );
  435. }
  436. #endif // 360
  437. DEFERRED_CON_COMMAND( vprof_on, "Turn on VProf profiler" )
  438. {
  439. if ( !g_fVprofOnByUI )
  440. {
  441. Msg("VProf enabled.\n");
  442. g_VProfCurrentProfile.Start();
  443. g_fVprofOnByUI = true;
  444. if ( IsX360() && !g_bVProfNoVSyncOff )
  445. {
  446. ConVarRef mat_vsyncref( "mat_vsync" );
  447. if ( mat_vsyncref.GetBool() )
  448. {
  449. Warning( "Disabling vsync (via mat_vsync) to increase profiling accuracy.\n" );
  450. mat_vsyncref.SetValue( false );
  451. }
  452. }
  453. }
  454. }
  455. CON_COMMAND( budget_toggle_group, "Turn a budget group on/off" )
  456. {
  457. if( args.ArgC() != 2 )
  458. {
  459. return;
  460. }
  461. int budgetGroup = g_VProfCurrentProfile.BudgetGroupNameToBudgetGroupIDNoCreate( args[1] );
  462. if ( budgetGroup == -1 )
  463. {
  464. return;
  465. }
  466. g_VProfCurrentProfile.HideBudgetGroup( budgetGroup, !(g_VProfCurrentProfile.GetBudgetGroupFlags( budgetGroup ) & BUDGETFLAG_HIDDEN) );
  467. }
  468. DEFERRED_CON_COMMAND( vprof_off, "Turn off VProf profiler" )
  469. {
  470. if ( g_fVprofOnByUI )
  471. {
  472. Msg("VProf disabled.\n");
  473. g_VProfCurrentProfile.Stop();
  474. g_fVprofOnByUI = false;
  475. // alien swarm has special behavior for certain testing scenarios:
  476. // generate a report after turning off vprof
  477. if ( g_szDefferedArg1[0] && stricmp("infested",g_szDefferedArg1) == 0 )
  478. {
  479. ConsoleLogger consoleLog;
  480. g_VProfCurrentProfile.OutputReport( VPRT_FULL & ~VPRT_HIERARCHY, NULL );
  481. }
  482. #if defined( _X360 )
  483. // disable all updating
  484. g_VProfCurrentProfile.VXEnableUpdateMode( 0xFFFFFFFF, false );
  485. #endif
  486. }
  487. }
  488. DEFERRED_CON_COMMAND( vprof_reset, "Reset the stats in VProf profiler" )
  489. {
  490. Msg("VProf reset.\n");
  491. g_VProfCurrentProfile.Reset();
  492. #ifndef DEDICATED
  493. if ( GetVProfPanel() )
  494. {
  495. GetVProfPanel()->Reset();
  496. }
  497. #endif
  498. }
  499. DEFERRED_CON_COMMAND(vprof_reset_peaks, "Reset just the peak time in VProf profiler")
  500. {
  501. Msg("VProf peaks reset.\n");
  502. g_VProfCurrentProfile.ResetPeaks();
  503. }
  504. DEFERRED_CON_COMMAND(vprof_generate_report, "Generate a report to the console.")
  505. {
  506. g_VProfCurrentProfile.Pause();
  507. ConsoleLogger consoleLog;
  508. g_VProfCurrentProfile.OutputReport( VPRT_FULL & ~VPRT_HIERARCHY, (g_szDefferedArg1[0]) ? g_szDefferedArg1 : NULL );
  509. g_VProfCurrentProfile.Resume();
  510. }
  511. DEFERRED_CON_COMMAND(vprof_generate_report_budget, "Generate a report to the console based on budget group.")
  512. {
  513. if ( !g_szDefferedArg1[0] )
  514. {
  515. return;
  516. }
  517. g_VProfCurrentProfile.Pause();
  518. ConsoleLogger consoleLog;
  519. g_VProfCurrentProfile.OutputReport( VPRT_FULL & ~VPRT_HIERARCHY, NULL, g_VProfCurrentProfile.BudgetGroupNameToBudgetGroupID( g_szDefferedArg1 ) );
  520. g_VProfCurrentProfile.Resume();
  521. }
  522. DEFERRED_CON_COMMAND(vprof_generate_report_hierarchy, "Generate a report to the console.")
  523. {
  524. g_VProfCurrentProfile.Pause();
  525. ConsoleLogger consoleLog;
  526. g_VProfCurrentProfile.OutputReport( VPRT_HIERARCHY, (g_szDefferedArg1[0]) ? g_szDefferedArg1 : NULL );
  527. g_VProfCurrentProfile.Resume();
  528. }
  529. DEFERRED_CON_COMMAND(vprof_generate_report_hierarchy_per_frame_and_count_only, "Generate a minimal hiearchical report to the console.")
  530. {
  531. g_VProfCurrentProfile.Pause();
  532. ConsoleLogger consoleLog;
  533. g_VProfCurrentProfile.OutputReport( VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY );
  534. g_VProfCurrentProfile.Resume();
  535. }
  536. DEFERRED_CON_COMMAND(vprof_generate_report_AI, "Generate a report to the console.")
  537. {
  538. // This is an unfortunate artifact of deferred commands not supporting arguments
  539. g_VProfCurrentProfile.Pause();
  540. ConsoleLogger consoleLog;
  541. g_VProfCurrentProfile.OutputReport( (VPRT_FULL & ~VPRT_HIERARCHY), "NPCs" );
  542. g_VProfCurrentProfile.Resume();
  543. }
  544. DEFERRED_CON_COMMAND(vprof_generate_report_AI_only, "Generate a report to the console.")
  545. {
  546. // This is an unfortunate artifact of deferred commands not supporting arguments
  547. g_VProfCurrentProfile.Pause();
  548. ConsoleLogger consoleLog;
  549. g_VProfCurrentProfile.OutputReport( (VPRT_FULL & ~VPRT_HIERARCHY), "NPCs", g_VProfCurrentProfile.BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_NPCS ) );
  550. g_VProfCurrentProfile.Resume();
  551. }
  552. DEFERRED_CON_COMMAND(vprof_generate_report_map_load, "Generate a report to the console.")
  553. {
  554. // This is an unfortunate artifact of deferred commands not supporting arguments
  555. g_VProfCurrentProfile.Pause();
  556. ConsoleLogger consoleLog;
  557. g_VProfCurrentProfile.OutputReport( VPRT_FULL, "Host_NewGame" );
  558. g_VProfCurrentProfile.Resume();
  559. }
  560. #ifdef VPROF_VXCONSOLE_EXISTS
  561. CON_COMMAND( vx_vprof_update, "" )
  562. {
  563. if ( args.ArgC() < 2 )
  564. return;
  565. const char *pArg = args[1];
  566. if ( !Q_stricmp( pArg, "cpu" ) )
  567. {
  568. g_VProfCurrentProfile.VXEnableUpdateMode( VPROF_UPDATE_BUDGET, true );
  569. }
  570. else if ( !Q_stricmp( pArg, "texture" ) )
  571. {
  572. g_VProfCurrentProfile.VXEnableUpdateMode( VPROF_UPDATE_TEXTURE_GLOBAL, true );
  573. g_VProfCurrentProfile.VXEnableUpdateMode( VPROF_UPDATE_TEXTURE_PERFRAME, false );
  574. }
  575. else if ( !Q_stricmp( pArg, "texture_frame" ) )
  576. {
  577. g_VProfCurrentProfile.VXEnableUpdateMode( VPROF_UPDATE_TEXTURE_PERFRAME, true );
  578. g_VProfCurrentProfile.VXEnableUpdateMode( VPROF_UPDATE_TEXTURE_GLOBAL, false );
  579. }
  580. }
  581. CON_COMMAND( vx_vprof_nodeslist, "" )
  582. {
  583. g_VProfCurrentProfile.VXSendNodes();
  584. }
  585. #endif
  586. #ifdef _X360
  587. DEFERRED_CON_COMMAND( vprof_360_enable_counters, "Enable 360 L2 and LHS counters for a node" )
  588. {
  589. g_VProfCurrentProfile.Pause();
  590. if ( g_VProfCurrentProfile.PMCEnableL2Upon(g_szDefferedArg1 ) )
  591. {
  592. g_VProfCurrentProfile.DumpEnabledPMCNodes();
  593. // Msg("PMC enabled for only node %s\n", g_szDefferedArg1);
  594. }
  595. else
  596. {
  597. Warning( "Node not found.\n" );
  598. }
  599. g_VProfCurrentProfile.Resume();
  600. }
  601. DEFERRED_CON_COMMAND( vprof_360_enable_counters_recursive, "Enable 360 L2 and LHS counters for a node and all subnodes" )
  602. {
  603. g_VProfCurrentProfile.Pause();
  604. if ( g_VProfCurrentProfile.PMCEnableL2Upon( g_szDefferedArg1, true ) )
  605. {
  606. g_VProfCurrentProfile.DumpEnabledPMCNodes();
  607. // Msg("PMC enabled for only node %s\n", g_szDefferedArg1);
  608. }
  609. else
  610. {
  611. Warning( "Node not found.\n" );
  612. }
  613. g_VProfCurrentProfile.Resume();
  614. }
  615. DEFERRED_CON_COMMAND( vprof_360_disable_counters, "Disable 360 L2 and LHS counters for a node. Specify 'all' to mean all nodes." )
  616. {
  617. g_VProfCurrentProfile.Pause();
  618. if ( stricmp( g_szDefferedArg1, "all" ) == 0 )
  619. {
  620. g_VProfCurrentProfile.PMCDisableAllNodes();
  621. }
  622. else
  623. {
  624. if ( g_VProfCurrentProfile.PMCDisableL2Upon( g_szDefferedArg1, false ) )
  625. {
  626. g_VProfCurrentProfile.DumpEnabledPMCNodes();
  627. // Msg("PMC enabled for only node %s\n", g_szDefferedArg1);
  628. }
  629. else
  630. {
  631. Warning( "Node not found.\n" );
  632. }
  633. }
  634. g_VProfCurrentProfile.Resume();
  635. }
  636. DEFERRED_CON_COMMAND( vprof_360_disable_counters_recursive, "Disable 360 L2 and LHS counters for a node and all children." )
  637. {
  638. g_VProfCurrentProfile.Pause();
  639. if ( g_VProfCurrentProfile.PMCDisableL2Upon( g_szDefferedArg1, true) )
  640. {
  641. g_VProfCurrentProfile.DumpEnabledPMCNodes();
  642. // Msg("PMC enabled for only node %s\n", g_szDefferedArg1);
  643. }
  644. else
  645. {
  646. Warning( "Node not found.\n" );
  647. }
  648. g_VProfCurrentProfile.Resume();
  649. }
  650. DEFERRED_CON_COMMAND( vprof_360_report_counters, "Report L2/LHS info for specified node" )
  651. {
  652. g_VProfCurrentProfile.Pause();
  653. ConsoleLogger consoleLog;
  654. CVProfNode *pNode = g_VProfCurrentProfile.FindNode( g_VProfCurrentProfile.GetRoot(), g_szDefferedArg1 );
  655. if ( pNode )
  656. {
  657. Msg( "NODE %s\n\tL2 misses: %d\n\tLHS misses: %d\n", g_szDefferedArg1, pNode->GetL2CacheMisses(), pNode->GetLoadHitStores() );
  658. }
  659. else
  660. {
  661. Warning( "Node %s not found.", g_szDefferedArg1 );
  662. }
  663. g_VProfCurrentProfile.Resume();
  664. }
  665. DEFERRED_CON_COMMAND( vprof_360_cpu_trace_enable, "Usage: vprof_360_cpu_trace_enable <\"node\">. Enable CPU tracing during scope of node. Do this before calling vprof_360_cpu_trace_go." )
  666. {
  667. CVProfNode *RESTRICT upon = g_VProfCurrentProfile.CPUTraceEnableForNode( g_szDefferedArg1 );
  668. if ( upon )
  669. {
  670. Msg( "%s will be traced from start to end. Make sure vprof is enabled, and enter \nvprof_360_cpu_trace_go <filename> to engage!\n", upon->GetName() );
  671. }
  672. else
  673. {
  674. Warning( "Missing node %s. Run VProf to instance the node and wrap in \"double-quotes\". \n", g_szDefferedArg1 );
  675. }
  676. }
  677. DEFERRED_CON_COMMAND( vprof_360_cpu_trace_disable, "Disable CPU tracing on all nodes." )
  678. {
  679. g_VProfCurrentProfile.CPUTraceDisableAllNodes();
  680. g_VProfCurrentProfile.SetCPUTraceEnabled( CVProfile::kDisabled );
  681. }
  682. DEFERRED_CON_COMMAND( vprof_360_cpu_trace_go, "Usage: vprof_360_cpu_trace_go <filename>. Will record one CPU trace of the node specified in vprof_360_cpu_trace_enable, dumping it to e:/filename.pix2." )
  683. {
  684. if ( !g_fVprofOnByUI )
  685. {
  686. Msg( "VProf enabled.\n" );
  687. g_VProfCurrentProfile.Start();
  688. g_fVprofOnByUI = true;
  689. ConVarRef mat_vsyncref( "mat_vsync" );
  690. if ( mat_vsyncref.GetBool() )
  691. {
  692. Warning( "Disabling vsync (via mat_vsync) to increase profiling accuracy.\n" );
  693. mat_vsyncref.SetValue( false );
  694. }
  695. }
  696. if ( g_VProfCurrentProfile.CPUTraceGetEnabledNode() == NULL || g_VProfCurrentProfile.CPUTraceGetEnabledNode() == g_VProfCurrentProfile.GetRoot() )
  697. {
  698. Msg( "Defaulting PIX trace node to CEngine::Frame\n" );
  699. g_VProfCurrentProfile.CPUTraceEnableForNode( "CEngine::Frame" );
  700. }
  701. if ( g_VProfCurrentProfile.CPUTraceGetEnabledNode() != NULL )
  702. {
  703. if ( !g_szDefferedArg1[0] )
  704. {
  705. SYSTEMTIME systemTime;
  706. GetLocalTime( &systemTime );
  707. V_snprintf( g_szDefferedArg1, ARRAYSIZE(g_szDefferedArg1), "vprof_%d_%d_%d_%d_%d_%d", systemTime.wMonth, systemTime.wDay, systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds );
  708. }
  709. const char *filename = g_VProfCurrentProfile.SetCPUTraceFilename( g_szDefferedArg1 );
  710. g_VProfCurrentProfile.SetCPUTraceEnabled( CVProfile::kFirstHitNode );
  711. Msg( "Trace will be written to %s\n", filename );
  712. }
  713. else
  714. {
  715. Warning( "Usage: vprof_360_cpu_trace_enable <\"node\">.\n" );
  716. }
  717. }
  718. DEFERRED_CON_COMMAND( vprof_360_cpu_trace_go_repeat, "Usage: vprof_360_cpu_trace_go_repeat <filename>. For each time the node specified in vprof_360_cpu_trace_enable is hit during the next frame, dump a CPU trace to e:/filenameXXXX.pix2." )
  719. {
  720. if ( g_VProfCurrentProfile.CPUTraceGetEnabledNode() != NULL )
  721. {
  722. const char *filename = g_VProfCurrentProfile.SetCPUTraceFilename( g_szDefferedArg1 );
  723. g_VProfCurrentProfile.SetCPUTraceEnabled( CVProfile::kAllNodesInFrame_WaitingForMark );
  724. Msg( "Trace will be written to %s%.4d ... \n", filename, g_VProfCurrentProfile.GetMultiTraceIndex() );
  725. }
  726. else
  727. {
  728. Warning( "Usage: vprof_360_cpu_trace_enable <\"node\">.\n" );
  729. }
  730. }
  731. DEFERRED_CON_COMMAND( vprof_360_cpu_trace_go_multiframe, "Usage: vprof_360_cpu_trace_go_multiframe <framecount> <filename>. For each time the node specified in vprof_360_cpu_trace_enable is hit during the next frame, dump a CPU trace to e:/filenameXXXX.pix2." )
  732. {
  733. int nNumFrames = Q_atoi( g_szDefferedArg1 );
  734. if ( nNumFrames >= 1 && nNumFrames < 1000 )
  735. {
  736. if ( g_VProfCurrentProfile.CPUTraceGetEnabledNode() != NULL )
  737. {
  738. const char *filename = g_VProfCurrentProfile.SetCPUTraceFilename( g_szDefferedArg2 );
  739. g_VProfCurrentProfile.SetCPUTraceEnabled( CVProfile::kAllNodesInFrame_WaitingForMarkMultiFrame, true, nNumFrames );
  740. Msg( "Trace will be written to %s%.4d ... \n", filename, g_VProfCurrentProfile.GetMultiTraceIndex() );
  741. return;
  742. }
  743. }
  744. Warning( "Usage: vprof_360_cpu_trace_go_multiframe <framecount> <filename>.\n" );
  745. }
  746. DEFERRED_CON_COMMAND( vx_vprof_trace, "" )
  747. {
  748. CVProfNode *RESTRICT pNode = g_VProfCurrentProfile.CPUTraceEnableForNode( g_szDefferedArg1 );
  749. if ( !pNode )
  750. {
  751. Warning( "vx_vprof_trace: Missing Node %s\n", g_szDefferedArg1 );
  752. return;
  753. }
  754. vprof_on_Impl();
  755. g_VProfCurrentProfile.SetCPUTraceFilename( "capture" );
  756. g_VProfCurrentProfile.SetCPUTraceEnabled( CVProfile::kFirstHitNode, true );
  757. }
  758. #endif
  759. // ------------------------------------------------------------------------------------------------------------------------------------ //
  760. // Exports for the dedicated server UI.
  761. // ------------------------------------------------------------------------------------------------------------------------------------ //
  762. class CVProfExport : public IVProfExport
  763. {
  764. public:
  765. CVProfExport()
  766. {
  767. m_nListeners = 0;
  768. m_bStart = m_bStop = false;
  769. m_BudgetFlagsFilter = 0;
  770. }
  771. inline CVProfile* GetActiveVProfile()
  772. {
  773. return g_pVProfileForDisplay;
  774. }
  775. inline bool CanShowBudgetGroup( int iGroup )
  776. {
  777. return ( GetActiveVProfile()->GetBudgetGroupFlags( iGroup ) & m_BudgetFlagsFilter ) != 0;
  778. }
  779. virtual void AddListener()
  780. {
  781. ++m_nListeners;
  782. if ( m_nListeners == 1 )
  783. m_bStart = true; // Defer the command till vprof is ready.
  784. }
  785. virtual void RemoveListener()
  786. {
  787. --m_nListeners;
  788. if ( m_nListeners == 0 )
  789. m_bStop = true; // Defer the command till vprof is ready.
  790. }
  791. virtual void SetBudgetFlagsFilter( int filter )
  792. {
  793. m_BudgetFlagsFilter = filter;
  794. }
  795. virtual int GetNumBudgetGroups()
  796. {
  797. int nTotalGroups = MIN( m_Times.Count(), GetActiveVProfile()->GetNumBudgetGroups() );
  798. int nRet = 0;
  799. for ( int i=0; i < nTotalGroups; i++ )
  800. {
  801. if ( CanShowBudgetGroup( i ) )
  802. ++nRet;
  803. }
  804. return nRet;
  805. }
  806. virtual void GetBudgetGroupInfos( CExportedBudgetGroupInfo *pInfos )
  807. {
  808. int iOut = 0;
  809. int nTotalGroups = MIN( m_Times.Count(), GetActiveVProfile()->GetNumBudgetGroups() );
  810. for ( int i=0; i < nTotalGroups; i++ )
  811. {
  812. if ( CanShowBudgetGroup( i ) )
  813. {
  814. pInfos[iOut].m_pName = GetActiveVProfile()->GetBudgetGroupName( i );
  815. int red, green, blue, alpha;
  816. GetActiveVProfile()->GetBudgetGroupColor( i, red, green, blue, alpha );
  817. pInfos[iOut].m_Color = Color( red, green, blue, alpha );
  818. pInfos[iOut].m_BudgetFlags = GetActiveVProfile()->GetBudgetGroupFlags( i );
  819. ++iOut;
  820. }
  821. }
  822. }
  823. virtual void GetBudgetGroupTimes( float times[IVProfExport::MAX_BUDGETGROUP_TIMES] )
  824. {
  825. int nTotalGroups = MIN( m_Times.Count(), GetActiveVProfile()->GetNumBudgetGroups() );
  826. int nGroups = MIN( nTotalGroups, IVProfExport::MAX_BUDGETGROUP_TIMES );
  827. memset( times, 0, sizeof( times[0] ) * nGroups );
  828. int iOut = 0;
  829. for ( int i=0; i < nTotalGroups; i++ )
  830. {
  831. if ( CanShowBudgetGroup( i ) )
  832. {
  833. times[iOut] = m_Times[i];
  834. ++iOut;
  835. }
  836. }
  837. }
  838. void GetAllBudgetGroupTimes( float *pTimes )
  839. {
  840. int nTotalGroups = GetActiveVProfile()->GetNumBudgetGroups();
  841. for ( int i=0; i < nTotalGroups; i++ )
  842. {
  843. pTimes[i] = CanShowBudgetGroup( i ) ? m_Times[i] : 0.0f;
  844. }
  845. }
  846. virtual void PauseProfile()
  847. {
  848. if ( materials )
  849. materials->Flush();
  850. g_VProfCurrentProfile.Pause();
  851. }
  852. virtual void ResumeProfile()
  853. {
  854. if ( materials )
  855. materials->Flush();
  856. g_VProfCurrentProfile.Resume();
  857. }
  858. public:
  859. void StartOrStop()
  860. {
  861. if ( m_bStart )
  862. {
  863. g_VProfCurrentProfile.Start();
  864. m_bStart = false;
  865. }
  866. if ( m_bStop )
  867. {
  868. g_VProfCurrentProfile.Stop();
  869. m_bStop = false;
  870. }
  871. }
  872. void CalculateBudgetGroupTimes_Recursive( CVProfNode *pNode )
  873. {
  874. // If this node's info is filtered out, then put it in its parent's budget group.
  875. CVProfNode *pTestNode = pNode;
  876. while ( pTestNode != GetActiveVProfile()->GetRoot() &&
  877. ( !CanShowBudgetGroup( pTestNode->GetBudgetGroupID() ) ||
  878. ( GetActiveVProfile()->GetBudgetGroupFlags( pTestNode->GetBudgetGroupID() ) & BUDGETFLAG_HIDDEN ) != 0 ) )
  879. {
  880. pTestNode = pTestNode->GetParent();
  881. }
  882. int groupID = pTestNode->GetBudgetGroupID();
  883. double nodeTime = pNode->GetPrevTimeLessChildren();
  884. if ( groupID >= 0 && groupID < MIN( m_Times.Count(), IVProfExport::MAX_BUDGETGROUP_TIMES ) )
  885. {
  886. m_Times[groupID] += nodeTime;
  887. }
  888. else
  889. {
  890. Assert( false );
  891. }
  892. if( pNode->GetSibling() )
  893. {
  894. CalculateBudgetGroupTimes_Recursive( pNode->GetSibling() );
  895. }
  896. if( pNode->GetChild() )
  897. {
  898. CalculateBudgetGroupTimes_Recursive( pNode->GetChild() );
  899. }
  900. if ( !VProfRecord_IsPlayingBack() )
  901. {
  902. pNode->ClearPrevTime();
  903. }
  904. }
  905. void SnapshotVProfHistory()
  906. {
  907. // Don't do the work if there are no listeners.
  908. if ( !GetActiveVProfile()->IsEnabled() )
  909. return;
  910. if ( m_Times.Count() < GetActiveVProfile()->GetNumBudgetGroups() )
  911. {
  912. m_Times.SetSize( GetActiveVProfile()->GetNumBudgetGroups() );
  913. }
  914. memset( m_Times.Base(), 0, sizeof( m_Times[0] ) * GetActiveVProfile()->GetNumBudgetGroups() );
  915. CVProfNode *pNode = GetActiveVProfile()->GetRoot();
  916. if( pNode && pNode->GetChild() )
  917. {
  918. CalculateBudgetGroupTimes_Recursive( pNode->GetChild() );
  919. }
  920. }
  921. private:
  922. CUtlVector<float> m_Times; // Times from the most recent snapshot.
  923. int m_nListeners;
  924. int m_BudgetFlagsFilter; // We can only capture one type of filtered data at a time.
  925. bool m_bStart;
  926. bool m_bStop;
  927. };
  928. CVProfExport g_VProfExport;
  929. IVProfExport *g_pVProfExport = &g_VProfExport;
  930. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVProfExport, IVProfExport, VPROF_EXPORT_INTERFACE_VERSION, g_VProfExport );
  931. void VProfExport_SnapshotVProfHistory()
  932. {
  933. g_VProfExport.SnapshotVProfHistory();
  934. }
  935. void VProfExport_StartOrStop()
  936. {
  937. g_VProfExport.StartOrStop();
  938. }
  939. // Used by rpt
  940. void VProfExport_Pause()
  941. {
  942. g_VProfExport.PauseProfile();
  943. }
  944. void VProfExport_Resume()
  945. {
  946. g_VProfExport.ResumeProfile();
  947. }
  948. //-----------------------------------------------------------------------------
  949. // Used to point the budget panel at remote data
  950. //-----------------------------------------------------------------------------
  951. void OverrideVProfExport( IVProfExport *pExport )
  952. {
  953. if ( g_pVProfExport == &g_VProfExport )
  954. {
  955. g_pVProfExport = pExport;
  956. }
  957. }
  958. void ResetVProfExport( IVProfExport *pExport )
  959. {
  960. if ( g_pVProfExport == pExport )
  961. {
  962. g_pVProfExport = &g_VProfExport;
  963. }
  964. }
  965. //-----------------------------------------------------------------------------
  966. // Listener to vprof data
  967. //-----------------------------------------------------------------------------
  968. struct VProfListenInfo_t
  969. {
  970. ra_listener_id m_nListenerId;
  971. float m_flLastSentVProfDataTime;
  972. CUtlVector< CUtlString > m_SentGroups;
  973. VProfListenInfo_t() : m_flLastSentVProfDataTime( 0.0f ) {}
  974. VProfListenInfo_t( ra_listener_id nListenerId ) : m_nListenerId( nListenerId ), m_flLastSentVProfDataTime( 0.0f ) {}
  975. bool operator==( const VProfListenInfo_t& src ) const { return src.m_nListenerId == m_nListenerId; }
  976. private:
  977. VProfListenInfo_t( const VProfListenInfo_t& src );
  978. };
  979. static CUtlVector<VProfListenInfo_t> s_VProfListeners;
  980. //-----------------------------------------------------------------------------
  981. // Purpose: serialize and send data to remote listeners
  982. //-----------------------------------------------------------------------------
  983. static int FindSentGroupIndex( VProfListenInfo_t &info, const char *pGroupName )
  984. {
  985. int nCount = info.m_SentGroups.Count();
  986. for ( int i = 0; i < nCount; ++i )
  987. {
  988. if ( !Q_strcmp( pGroupName, info.m_SentGroups[i].Get() ) )
  989. return i;
  990. }
  991. return -1;
  992. }
  993. //-----------------------------------------------------------------------------
  994. // Purpose: serialize and send data to remote listeners
  995. //-----------------------------------------------------------------------------
  996. void WriteRemoteVProfGroupData( VProfListenInfo_t &info )
  997. {
  998. if ( IsX360() )
  999. return;
  1000. int nGroupCount = g_pVProfileForDisplay->GetNumBudgetGroups();
  1001. int nInitialCount = info.m_SentGroups.Count();
  1002. // Build list of unsent groups to send
  1003. int nSendCount = 0;
  1004. int *pIndex = (int*)stackalloc( nGroupCount * sizeof(int) );
  1005. for ( int i = 0; i < nGroupCount; ++i )
  1006. {
  1007. const char *pName = g_pVProfileForDisplay->GetBudgetGroupName( i );
  1008. if ( FindSentGroupIndex( info, pName ) >= 0 )
  1009. continue;
  1010. int j = info.m_SentGroups.AddToTail();
  1011. info.m_SentGroups[j] = pName;
  1012. pIndex[nSendCount++] = i;
  1013. }
  1014. if ( nSendCount == 0 )
  1015. return;
  1016. CUtlBuffer buf( 1024, 1024 );
  1017. buf.PutInt( nInitialCount );
  1018. buf.PutInt( nSendCount );
  1019. for ( int i=0; i < nSendCount; i++ )
  1020. {
  1021. int nIndex = pIndex[i];
  1022. int red, green, blue, alpha;
  1023. g_pVProfileForDisplay->GetBudgetGroupColor( nIndex, red, green, blue, alpha );
  1024. buf.PutUnsignedChar( (unsigned char)red );
  1025. buf.PutUnsignedChar( (unsigned char)green );
  1026. buf.PutUnsignedChar( (unsigned char)blue );
  1027. buf.PutUnsignedChar( (unsigned char)alpha );
  1028. const char *pName = g_pVProfileForDisplay->GetBudgetGroupName( nIndex );
  1029. buf.PutString( pName );
  1030. }
  1031. g_ServerRemoteAccess.SendVProfData( info.m_nListenerId, true, buf.Base(), buf.TellMaxPut() );
  1032. }
  1033. static ConVar rpt_vprof_time( "rpt_vprof_time","0.25", FCVAR_HIDDEN | FCVAR_DONTRECORD, "" );
  1034. void WriteRemoteVProfData()
  1035. {
  1036. if ( IsX360() )
  1037. return;
  1038. // Throttle sending too much data
  1039. float flMaxDelta = rpt_vprof_time.GetFloat();
  1040. float flTime = Plat_FloatTime();
  1041. bool bShouldSend = false;
  1042. int nListenerCount = s_VProfListeners.Count();
  1043. for( int i = 0; i < nListenerCount; i++ )
  1044. {
  1045. if ( flTime - s_VProfListeners[i].m_flLastSentVProfDataTime >= flMaxDelta )
  1046. {
  1047. bShouldSend = true;
  1048. break;
  1049. }
  1050. }
  1051. if ( !bShouldSend )
  1052. return;
  1053. int nGroupCount = g_pVProfileForDisplay->GetNumBudgetGroups();
  1054. int nBufSize = nGroupCount * sizeof(float);
  1055. float *pTimes = (float*)stackalloc( nBufSize );
  1056. g_VProfExport.GetAllBudgetGroupTimes( pTimes );
  1057. for( int i = 0; i < nListenerCount; i++ )
  1058. {
  1059. if ( flTime - s_VProfListeners[i].m_flLastSentVProfDataTime < flMaxDelta )
  1060. continue;
  1061. WriteRemoteVProfGroupData( s_VProfListeners[i] );
  1062. s_VProfListeners[i].m_flLastSentVProfDataTime = flTime;
  1063. // Re-order send times to match send group order
  1064. int nSentSize = s_VProfListeners[i].m_SentGroups.Count() * sizeof(float);
  1065. float *pSentTimes = (float*)stackalloc( nSentSize );
  1066. memset( pSentTimes, 0, nSentSize );
  1067. for ( int j = 0; j < nGroupCount; ++j )
  1068. {
  1069. int nIndex = FindSentGroupIndex( s_VProfListeners[i], g_pVProfileForDisplay->GetBudgetGroupName( j ) );
  1070. Assert( nIndex >= 0 );
  1071. pSentTimes[ nIndex ] = pTimes[j];
  1072. }
  1073. g_ServerRemoteAccess.SendVProfData( s_VProfListeners[i].m_nListenerId, false, pSentTimes, nSentSize );
  1074. }
  1075. }
  1076. //-----------------------------------------------------------------------------
  1077. // Purpose: add a new endpoint to send data to
  1078. //-----------------------------------------------------------------------------
  1079. void RegisterVProfDataListener( ra_listener_id listenerID )
  1080. {
  1081. RemoveVProfDataListener( listenerID );
  1082. int nIndex = s_VProfListeners.AddToTail( );
  1083. s_VProfListeners[nIndex].m_nListenerId = listenerID;
  1084. g_VProfExport.AddListener();
  1085. WriteRemoteVProfGroupData( s_VProfListeners[nIndex] );
  1086. }
  1087. //-----------------------------------------------------------------------------
  1088. // Purpose: remove an endpoint we are sending data to
  1089. //-----------------------------------------------------------------------------
  1090. void RemoveVProfDataListener( ra_listener_id listenerID )
  1091. {
  1092. VProfListenInfo_t findInfo( listenerID );
  1093. if ( s_VProfListeners.FindAndRemove( findInfo ) )
  1094. {
  1095. g_VProfExport.RemoveListener();
  1096. }
  1097. }
  1098. #endif