Team Fortress 2 Source Code as on 22/4/2020
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.

1201 lines
33 KiB

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