Skip to content

Commit 2bbdda8

Browse files
committed
perf: fps_max adjustments
backport fps_max 49 limit from CSGO add a new frame limiter method which sleeps until a certain accuracy threshold then, it will tightly loop while waiting for our yield to reach the end time this ensures that we always arrive at the exact time that the FPS limit expects before, this was sensitive to 2 issues, one being that the pause instruction has different cycle counts on different CPUs and the other being that the frame limit was not being handled efficiently within the busy wait period
1 parent 002e01f commit 2bbdda8

File tree

1 file changed

+18
-16
lines changed

1 file changed

+18
-16
lines changed

src/engine/sys_engine.cpp

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
#include "vgui_baseui_interface.h"
3636
#endif
3737
#include "tier0/etwprof.h"
38+
#ifdef IS_WINDOWS_PC
39+
#include <windows.h>
40+
#endif
3841

3942
// memdbgon must be the last include file in a .cpp file!!!
4043
#include "tier0/memdbgon.h"
@@ -244,13 +247,13 @@ bool CEngine::FilterTime( float dt )
244247
// Dedicated's tic_rate regulates server frame rate. Don't apply fps filter here.
245248
// Only do this restriction on the client. Prevents clients from accomplishing certain
246249
// hacks by pausing their client for a period of time.
247-
if ( IsPC() && !sv.IsDedicated() && !CanCheat() && fps_max.GetFloat() < 30 )
250+
if ( IsPC() && !sv.IsDedicated() && !CanCheat() && fps_max.GetFloat() < 49 )
248251
{
249252
// Don't do anything if fps_max=0 (which means it's unlimited).
250253
if ( fps_max.GetFloat() != 0.0f )
251254
{
252-
Warning( "sv_cheats is 0 and fps_max is being limited to a minimum of 30 (or set to 0).\n" );
253-
fps_max.SetValue( 30.0f );
255+
Warning( "sv_cheats is 0 and fps_max is being limited to a minimum of 49 (or set to 0).\n" );
256+
fps_max.SetValue( 49.0f );
254257
}
255258
}
256259

@@ -343,7 +346,9 @@ void CEngine::Frame( void )
343346
{
344347
// ThreadSleep may be imprecise. On non-dedicated servers, we busy-sleep
345348
// for the last one or two milliseconds to ensure very tight timing.
346-
float fBusyWaitMS = IsWindows() ? 2.25f : 1.5f;
349+
float fBusyWaitMS = IsWindows() ? 2.0f : 1.5f;
350+
float fWaitTime = m_flMinFrameTime - m_flFrameTime;
351+
float fWaitEnd = m_flCurrentTime + fWaitTime;
347352
if ( sv.IsDedicated() )
348353
{
349354
fBusyWaitMS = host_timer_spin_ms.GetFloat();
@@ -354,23 +359,20 @@ void CEngine::Frame( void )
354359
// to avoid wasting power and to let other threads/processes run.
355360
// Calculate how long we need to wait.
356361
int nSleepMS = (int)( ( m_flMinFrameTime - m_flFrameTime ) * 1000 - fBusyWaitMS );
357-
if ( nSleepMS > 0 )
362+
if ( nSleepMS > fBusyWaitMS )
358363
{
359364
ThreadSleep( nSleepMS );
360365
}
361-
else
366+
367+
while ( Plat_FloatTime() < fWaitEnd )
362368
{
363-
// On x86, busy-wait using PAUSE instruction which encourages
364-
// power savings by idling for ~10 cycles (also yielding to
365-
// the other logical hyperthread core if the CPU supports it)
366-
for (int i = 2000; i >= 0; --i)
367-
{
368-
#if defined(POSIX)
369-
__asm( "pause" ); __asm( "pause" ); __asm( "pause" ); __asm( "pause" );
370-
#elif defined(IS_WINDOWS_PC)
371-
_asm { pause }; _asm { pause }; _asm { pause }; _asm { pause };
369+
ThreadPause();
370+
// Yield the CPU to other threads.
371+
#ifdef IS_WINDOWS_PC
372+
SwitchToThread();
373+
#elif defined( POSIX )
374+
sched_yield();
372375
#endif
373-
}
374376
}
375377

376378
// Go back to the top of the loop and see if it is time yet.

0 commit comments

Comments
 (0)