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.

536 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "quakedef.h"
  7. #include <assert.h>
  8. #include "engine_launcher_api.h"
  9. #include "iengine.h"
  10. #include "ivideomode.h"
  11. #include "igame.h"
  12. #include "vmodes.h"
  13. #include "modes.h"
  14. #include "sys.h"
  15. #include "host.h"
  16. #include "keys.h"
  17. #include "cdll_int.h"
  18. #include "host_state.h"
  19. #include "cdll_engine_int.h"
  20. #include "sys_dll.h"
  21. #include "tier0/vprof.h"
  22. #include "profile.h"
  23. #include "gl_matsysiface.h"
  24. #include "vprof_engine.h"
  25. #include "server.h"
  26. #include "cl_demo.h"
  27. #include "toolframework/itoolframework.h"
  28. #include "toolframework/itoolsystem.h"
  29. #include "inputsystem/iinputsystem.h"
  30. #include "gl_cvars.h"
  31. #include "filesystem_engine.h"
  32. #include "tier0/cpumonitoring.h"
  33. #ifndef SWDS
  34. #include "vgui_baseui_interface.h"
  35. #endif
  36. #include "tier0/etwprof.h"
  37. // memdbgon must be the last include file in a .cpp file!!!
  38. #include "tier0/memdbgon.h"
  39. //-----------------------------------------------------------------------------
  40. // Forward declarations
  41. //-----------------------------------------------------------------------------
  42. void Sys_ShutdownGame( void );
  43. int Sys_InitGame( CreateInterfaceFn appSystemFactory,
  44. char const* pBaseDir, void *pwnd, int bIsDedicated );
  45. // Sleep time when not focus. Set to 0 to not sleep even if app doesn't have focus.
  46. ConVar engine_no_focus_sleep( "engine_no_focus_sleep", "50", FCVAR_ARCHIVE );
  47. // sleep time when not focus
  48. #define NOT_FOCUS_SLEEP 50
  49. #define DEFAULT_FPS_MAX 300
  50. #define DEFAULT_FPS_MAX_S "300"
  51. static int s_nDesiredFPSMax = DEFAULT_FPS_MAX;
  52. static bool s_bFPSMaxDrivenByPowerSavings = false;
  53. //-----------------------------------------------------------------------------
  54. // ConVars and ConCommands
  55. //-----------------------------------------------------------------------------
  56. static void fps_max_callback( IConVar *var, const char *pOldValue, float flOldValue )
  57. {
  58. // Only update s_nDesiredFPSMax when not driven by the mat_powersavingsmode ConVar (see below)
  59. if ( !s_bFPSMaxDrivenByPowerSavings )
  60. {
  61. s_nDesiredFPSMax = ( (ConVar *)var)->GetInt();
  62. }
  63. }
  64. ConVar fps_max( "fps_max", DEFAULT_FPS_MAX_S, FCVAR_NOT_CONNECTED, "Frame rate limiter, cannot be set while connected to a server.", fps_max_callback );
  65. // When set, this ConVar (typically driven from the advanced video settings) will drive fps_max (see above) to
  66. // half of the refresh rate, if the user hasn't otherwise set fps_max (via console, commandline etc)
  67. static void mat_powersavingsmode_callback( IConVar *var, const char *pOldValue, float flOldValue )
  68. {
  69. s_bFPSMaxDrivenByPowerSavings = true;
  70. int nRefresh = s_nDesiredFPSMax;
  71. if ( ( (ConVar *)var)->GetBool() )
  72. {
  73. MaterialVideoMode_t mode;
  74. materials->GetDisplayMode( mode );
  75. nRefresh = MAX( 30, ( mode.m_RefreshRate + 1 ) >> 1 ); // Half of display refresh rate (min of 30Hz)
  76. }
  77. fps_max.SetValue( nRefresh );
  78. s_bFPSMaxDrivenByPowerSavings = false;
  79. }
  80. static ConVar mat_powersavingsmode( "mat_powersavingsmode", "0", FCVAR_ARCHIVE, "Power Savings Mode", mat_powersavingsmode_callback );
  81. #ifndef _RETAIL
  82. static ConVar async_serialize( "async_serialize", "0", 0, "Force async reads to serialize for profiling" );
  83. #define ShouldSerializeAsync() async_serialize.GetBool()
  84. #else
  85. #define ShouldSerializeAsync() false
  86. #endif
  87. extern ConVar host_timer_spin_ms;
  88. extern float host_nexttick;
  89. extern IVEngineClient *engineClient;
  90. #ifdef WIN32
  91. static void cpu_frequency_monitoring_callback( IConVar *var, const char *pOldValue, float flOldValue )
  92. {
  93. // Set the specified interval for CPU frequency monitoring
  94. SetCPUMonitoringInterval( (unsigned)( ( (ConVar *)var)->GetFloat() * 1000 ) );
  95. }
  96. ConVar cpu_frequency_monitoring( "cpu_frequency_monitoring", "0", 0, "Set CPU frequency monitoring interval in seconds. Zero means disabled.", true, 0.0f, true, 10.0f, cpu_frequency_monitoring_callback );
  97. #endif
  98. //-----------------------------------------------------------------------------
  99. // Purpose:
  100. //-----------------------------------------------------------------------------
  101. class CEngine : public IEngine
  102. {
  103. public:
  104. CEngine( void );
  105. virtual ~CEngine( void );
  106. bool Load( bool dedicated, const char *basedir );
  107. virtual void Unload( void );
  108. virtual EngineState_t GetState( void );
  109. virtual void SetNextState( EngineState_t iNextState );
  110. void Frame( void );
  111. float GetFrameTime( void );
  112. float GetCurTime( void );
  113. bool TrapKey_Event( ButtonCode_t key, bool down );
  114. void TrapMouse_Event( int buttons, bool down );
  115. void StartTrapMode( void );
  116. bool IsTrapping( void );
  117. bool CheckDoneTrapping( ButtonCode_t& key );
  118. int GetQuitting( void );
  119. void SetQuitting( int quittype );
  120. private:
  121. bool FilterTime( float t );
  122. int m_nQuitting;
  123. EngineState_t m_nDLLState;
  124. EngineState_t m_nNextDLLState;
  125. double m_flCurrentTime;
  126. double m_flFrameTime;
  127. double m_flPreviousTime;
  128. float m_flFilteredTime;
  129. float m_flMinFrameTime; // Expected duration of a frame, or zero if it is unlimited.
  130. float m_flLastRemainder; // 'Unused' time on the last render loop.
  131. bool m_bCatchupTime;
  132. };
  133. static CEngine g_Engine;
  134. IEngine *eng = ( IEngine * )&g_Engine;
  135. //IEngineAPI *engine = NULL;
  136. //-----------------------------------------------------------------------------
  137. // Purpose: Constructor
  138. //-----------------------------------------------------------------------------
  139. CEngine::CEngine( void )
  140. {
  141. m_nDLLState = DLL_INACTIVE;
  142. m_nNextDLLState = DLL_INACTIVE;
  143. m_flCurrentTime = 0.0;
  144. m_flFrameTime = 0.0f;
  145. m_flPreviousTime = 0.0;
  146. m_flFilteredTime = 0.0f;
  147. m_flMinFrameTime = 0.0f;
  148. m_flLastRemainder = 0.0f;
  149. m_bCatchupTime = false;
  150. m_nQuitting = QUIT_NOTQUITTING;
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. CEngine::~CEngine( void )
  156. {
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Purpose:
  160. //-----------------------------------------------------------------------------
  161. void CEngine::Unload( void )
  162. {
  163. Sys_ShutdownGame();
  164. m_nDLLState = DLL_INACTIVE;
  165. m_nNextDLLState = DLL_INACTIVE;
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose:
  169. // Output : Returns true on success, false on failure.
  170. //-----------------------------------------------------------------------------
  171. bool CEngine::Load( bool bDedicated, const char *rootdir )
  172. {
  173. bool success = false;
  174. // Activate engine
  175. // NOTE: We must bypass the 'next state' block here for initialization to work properly.
  176. m_nDLLState = m_nNextDLLState = InEditMode() ? DLL_PAUSED : DLL_ACTIVE;
  177. if ( Sys_InitGame(
  178. g_AppSystemFactory,
  179. rootdir,
  180. game->GetMainWindowAddress(),
  181. bDedicated ) )
  182. {
  183. success = true;
  184. UpdateMaterialSystemConfig();
  185. }
  186. return success;
  187. }
  188. //-----------------------------------------------------------------------------
  189. // Purpose:
  190. // Input : dt -
  191. // Output : Returns true on success, false on failure.
  192. //-----------------------------------------------------------------------------
  193. bool CEngine::FilterTime( float dt )
  194. {
  195. if ( sv.IsDedicated() && !g_bDedicatedServerBenchmarkMode )
  196. {
  197. m_flMinFrameTime = host_nexttick;
  198. return ( dt >= host_nexttick );
  199. }
  200. m_flMinFrameTime = 0.0f;
  201. // Dedicated's tic_rate regulates server frame rate. Don't apply fps filter here.
  202. // Only do this restriction on the client. Prevents clients from accomplishing certain
  203. // hacks by pausing their client for a period of time.
  204. if ( IsPC() && !sv.IsDedicated() && !CanCheat() && fps_max.GetFloat() < 30 )
  205. {
  206. // Don't do anything if fps_max=0 (which means it's unlimited).
  207. if ( fps_max.GetFloat() != 0.0f )
  208. {
  209. Warning( "sv_cheats is 0 and fps_max is being limited to a minimum of 30 (or set to 0).\n" );
  210. fps_max.SetValue( 30.0f );
  211. }
  212. }
  213. float fps = fps_max.GetFloat();
  214. if ( fps > 0.0f )
  215. {
  216. // Limit fps to withing tolerable range
  217. // fps = max( MIN_FPS, fps ); // red herring - since we're only checking if dt < 1/fps, clamping against MIN_FPS has no effect
  218. fps = min( MAX_FPS, (double)fps );
  219. float minframetime = 1.0 / fps;
  220. m_flMinFrameTime = minframetime;
  221. if (
  222. #if !defined(SWDS)
  223. !demoplayer->IsPlayingTimeDemo() &&
  224. #endif
  225. !g_bDedicatedServerBenchmarkMode &&
  226. dt < minframetime )
  227. {
  228. // framerate is too high
  229. return false;
  230. }
  231. }
  232. return true;
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Purpose:
  236. // Output : int
  237. //-----------------------------------------------------------------------------
  238. void CEngine::Frame( void )
  239. {
  240. // yield the CPU for a little while when paused, minimized, or not the focus
  241. // FIXME: Move this to main windows message pump?
  242. if ( IsPC() && !game->IsActiveApp() && !sv.IsDedicated() && engine_no_focus_sleep.GetInt() > 0 )
  243. {
  244. VPROF_BUDGET( "Sleep", VPROF_BUDGETGROUP_SLEEPING );
  245. #if defined( RAD_TELEMETRY_ENABLED )
  246. if( !g_Telemetry.Level )
  247. #endif
  248. g_pInputSystem->SleepUntilInput( engine_no_focus_sleep.GetInt() );
  249. }
  250. if ( m_flPreviousTime == 0 )
  251. {
  252. (void) FilterTime( 0.0f );
  253. m_flPreviousTime = Sys_FloatTime() - m_flMinFrameTime;
  254. }
  255. // Watch for data from the CPU frequency monitoring system and print it to the console.
  256. const CPUFrequencyResults frequency = GetCPUFrequencyResults();
  257. static double s_lastFrequencyTimestamp;
  258. if ( frequency.m_timeStamp > s_lastFrequencyTimestamp )
  259. {
  260. s_lastFrequencyTimestamp = frequency.m_timeStamp;
  261. Msg( "~CPU Freq: %1.3f GHz Percent of requested: %3.1f%% Minimum percent seen: %3.1f%%\n",
  262. frequency.m_GHz, frequency.m_percentage, frequency.m_lowestPercentage );
  263. }
  264. // Loop until it is time for our frame. Don't return early because pumping messages
  265. // and processing console input is expensive (0.1 ms for each call to ProcessConsoleInput).
  266. for (;;)
  267. {
  268. // Get current time
  269. m_flCurrentTime = Sys_FloatTime();
  270. // Determine dt since we last ticked
  271. m_flFrameTime = m_flCurrentTime - m_flPreviousTime;
  272. // This should never happen...
  273. Assert( m_flFrameTime >= 0.0f );
  274. if ( m_flFrameTime < 0.0f )
  275. {
  276. // ... but if the clock ever went backwards due to a bug,
  277. // we'd have no idea how much time has elapsed, so just
  278. // catch up to the next scheduled server tick.
  279. m_flFrameTime = host_nexttick;
  280. }
  281. if ( FilterTime( m_flFrameTime ) )
  282. {
  283. // Time to render our frame.
  284. break;
  285. }
  286. if ( IsPC() && ( !sv.IsDedicated() || host_timer_spin_ms.GetFloat() != 0 ) )
  287. {
  288. // ThreadSleep may be imprecise. On non-dedicated servers, we busy-sleep
  289. // for the last one or two milliseconds to ensure very tight timing.
  290. float fBusyWaitMS = IsWindows() ? 2.25f : 1.5f;
  291. if ( sv.IsDedicated() )
  292. {
  293. fBusyWaitMS = host_timer_spin_ms.GetFloat();
  294. fBusyWaitMS = MAX( fBusyWaitMS, 0.5f );
  295. }
  296. // If we are meeting our frame rate then go idle for a while
  297. // to avoid wasting power and to let other threads/processes run.
  298. // Calculate how long we need to wait.
  299. int nSleepMS = (int)( ( m_flMinFrameTime - m_flFrameTime ) * 1000 - fBusyWaitMS );
  300. if ( nSleepMS > 0 )
  301. {
  302. ThreadSleep( nSleepMS );
  303. }
  304. else
  305. {
  306. // On x86, busy-wait using PAUSE instruction which encourages
  307. // power savings by idling for ~10 cycles (also yielding to
  308. // the other logical hyperthread core if the CPU supports it)
  309. for (int i = 2000; i >= 0; --i)
  310. {
  311. #if defined(POSIX)
  312. __asm( "pause" ); __asm( "pause" ); __asm( "pause" ); __asm( "pause" );
  313. #elif defined(IS_WINDOWS_PC)
  314. _asm { pause }; _asm { pause }; _asm { pause }; _asm { pause };
  315. #endif
  316. }
  317. }
  318. // Go back to the top of the loop and see if it is time yet.
  319. }
  320. else
  321. {
  322. int nSleepMicrosecs = (int) ceilf( clamp( ( m_flMinFrameTime - m_flFrameTime ) * 1000000.f, 1.f, 1000000.f ) );
  323. #ifdef POSIX
  324. usleep( nSleepMicrosecs );
  325. #else
  326. ThreadSleep( (nSleepMicrosecs + 999) / 1000 );
  327. #endif
  328. }
  329. }
  330. if ( ShouldSerializeAsync() )
  331. {
  332. static ConVar *pSyncReportConVar = g_pCVar->FindVar( "fs_report_sync_opens" );
  333. bool bReportingSyncOpens = ( pSyncReportConVar && pSyncReportConVar->GetInt() );
  334. int reportLevel = 0;
  335. if ( bReportingSyncOpens )
  336. {
  337. reportLevel = pSyncReportConVar->GetInt();
  338. pSyncReportConVar->SetValue( 0 );
  339. }
  340. g_pFileSystem->AsyncFinishAll();
  341. if ( bReportingSyncOpens )
  342. {
  343. pSyncReportConVar->SetValue( reportLevel );
  344. }
  345. }
  346. #ifdef VPROF_ENABLED
  347. PreUpdateProfile( m_flFrameTime );
  348. #endif
  349. // Reset swallowed time...
  350. m_flFilteredTime = 0.0f;
  351. #ifndef SWDS
  352. if ( !sv.IsDedicated() )
  353. {
  354. ClientDLL_FrameStageNotify( FRAME_START );
  355. ETWRenderFrameMark( false );
  356. }
  357. #endif
  358. #ifdef VPROF_ENABLED
  359. PostUpdateProfile();
  360. #endif
  361. TelemetryTick();
  362. { // profile scope
  363. VPROF_BUDGET( "CEngine::Frame", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED );
  364. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  365. #ifdef RAD_TELEMETRY_ENABLED
  366. TmU64 time0 = tmFastTime();
  367. #endif
  368. switch( m_nDLLState )
  369. {
  370. case DLL_PAUSED: // paused, in hammer
  371. case DLL_INACTIVE: // no dll
  372. break;
  373. case DLL_ACTIVE: // engine is focused
  374. case DLL_CLOSE: // closing down dll
  375. case DLL_RESTART: // engine is shutting down but will restart right away
  376. // Run the engine frame
  377. HostState_Frame( m_flFrameTime );
  378. break;
  379. }
  380. // Has the state changed?
  381. if ( m_nNextDLLState != m_nDLLState )
  382. {
  383. m_nDLLState = m_nNextDLLState;
  384. // Do special things if we change to particular states
  385. switch( m_nDLLState )
  386. {
  387. case DLL_CLOSE:
  388. SetQuitting( QUIT_TODESKTOP );
  389. break;
  390. case DLL_RESTART:
  391. SetQuitting( QUIT_RESTART );
  392. break;
  393. }
  394. }
  395. #ifdef RAD_TELEMETRY_ENABLED
  396. float time = ( tmFastTime() - time0 ) * g_Telemetry.flRDTSCToMilliSeconds;
  397. if( time > 0.5f )
  398. {
  399. tmPlot( TELEMETRY_LEVEL0, TMPT_TIME_MS, 0, time, "CEngine::Frame" );
  400. }
  401. #endif
  402. } // profile scope
  403. // Remember old time
  404. m_flPreviousTime = m_flCurrentTime;
  405. #if defined( VPROF_ENABLED ) && defined( _X360 )
  406. UpdateVXConsoleProfile();
  407. #endif
  408. }
  409. //-----------------------------------------------------------------------------
  410. // Purpose:
  411. //-----------------------------------------------------------------------------
  412. CEngine::EngineState_t CEngine::GetState( void )
  413. {
  414. return m_nDLLState;
  415. }
  416. //-----------------------------------------------------------------------------
  417. // Purpose:
  418. //-----------------------------------------------------------------------------
  419. void CEngine::SetNextState( EngineState_t iNextState )
  420. {
  421. m_nNextDLLState = iNextState;
  422. }
  423. //-----------------------------------------------------------------------------
  424. // Purpose:
  425. //-----------------------------------------------------------------------------
  426. float CEngine::GetFrameTime( void )
  427. {
  428. return m_flFrameTime;
  429. }
  430. //-----------------------------------------------------------------------------
  431. // Purpose:
  432. //-----------------------------------------------------------------------------
  433. float CEngine::GetCurTime( void )
  434. {
  435. return m_flCurrentTime;
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose: Flag that we are in the process of quiting
  439. //-----------------------------------------------------------------------------
  440. void CEngine::SetQuitting( int quittype )
  441. {
  442. m_nQuitting = quittype;
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose: Check whether we are ready to exit
  446. //-----------------------------------------------------------------------------
  447. int CEngine::GetQuitting( void )
  448. {
  449. return m_nQuitting;
  450. }