Handle the sleep at the end of D_SRB2Loop instead of the start

Simplifies logic in the other parts of the loop, and fixes problems with it frequently waiting too long.
This commit is contained in:
Sally Coolatta 2022-04-19 12:06:17 -04:00 committed by Eidolon
parent 685190fe40
commit b18e53417a
4 changed files with 79 additions and 90 deletions

View file

@ -687,29 +687,6 @@ static void D_Display(void)
} }
} }
static boolean D_CheckFrameCap(void)
{
static boolean init = false;
static precise_t startCap = 0;
precise_t endCap = 0;
endCap = I_GetPreciseTime();
if (init == false)
{
startCap = endCap;
init = true;
}
else if (I_CheckFrameCap(startCap, endCap))
{
// Framerate should be capped.
return true;
}
startCap = endCap;
return false;
}
// ========================================================================= // =========================================================================
// D_SRB2Loop // D_SRB2Loop
// ========================================================================= // =========================================================================
@ -720,10 +697,13 @@ void D_SRB2Loop(void)
{ {
tic_t oldentertics = 0, entertic = 0, realtics = 0, rendertimeout = INFTICS; tic_t oldentertics = 0, entertic = 0, realtics = 0, rendertimeout = INFTICS;
static lumpnum_t gstartuplumpnum; static lumpnum_t gstartuplumpnum;
boolean ticked;
boolean interp; boolean ticked = false;
boolean interp = false;
boolean doDisplay = false; boolean doDisplay = false;
boolean frameCap = false;
precise_t frameTime = 0;
int frameElapsed = 0;
if (dedicated) if (dedicated)
server = true; server = true;
@ -775,6 +755,9 @@ void D_SRB2Loop(void)
for (;;) for (;;)
{ {
frameTime = I_GetPreciseTime();
frameElapsed = 0;
if (lastwipetic) if (lastwipetic)
{ {
oldentertics = lastwipetic; oldentertics = lastwipetic;
@ -804,19 +787,6 @@ void D_SRB2Loop(void)
doDisplay = false; doDisplay = false;
ticked = false; ticked = false;
frameCap = D_CheckFrameCap();
// Moved to here from I_FinishUpdate.
// It doesn't track fades properly anymore by being here (might be easy fix),
// but it's a little more accurate for actual rendering when its here.
SCR_CalculateFPS();
if (!realtics && !singletics)
{
if (frameCap)
continue;
}
#ifdef HW3SOUND #ifdef HW3SOUND
HW3S_BeginFrameUpdate(); HW3S_BeginFrameUpdate();
#endif #endif
@ -872,15 +842,6 @@ void D_SRB2Loop(void)
tictime = entertime; tictime = entertime;
} }
// Handle interp sleep / framerate cap here.
// TryRunTics needs ran if possible to prevent lagged map changes,
// (and if that runs, the code above needs to also run)
// so this is done here after TryRunTics.
if (frameCap)
{
continue;
}
if (!(paused || P_AutoPause())) if (!(paused || P_AutoPause()))
{ {
#if 0 #if 0
@ -912,11 +873,6 @@ void D_SRB2Loop(void)
} }
else else
{ {
if (frameCap)
{
continue;
}
renderdeltatics = realtics * FRACUNIT; renderdeltatics = realtics * FRACUNIT;
rendertimefrac = FRACUNIT; rendertimefrac = FRACUNIT;
} }
@ -940,6 +896,19 @@ void D_SRB2Loop(void)
#endif #endif
LUA_Step(); LUA_Step();
// Moved to here from I_FinishUpdate.
// It doesn't track fades properly anymore by being here (might be easy fix),
// but it's a little more accurate for actual rendering when its here.
SCR_CalculateFPS();
// Fully completed frame made, handle frame cap delay.
frameElapsed = I_PreciseToMicros(I_GetPreciseTime() - frameTime);
if (!singletics)
{
I_FrameCapSleep(frameElapsed);
}
} }
} }

View file

@ -58,13 +58,17 @@ precise_t I_GetPreciseTime(void);
*/ */
int I_PreciseToMicros(precise_t d); int I_PreciseToMicros(precise_t d);
/** \brief The I_Sleep function /** \brief Sleeps by the value of cv_sleep
\return void \return void
*/ */
void I_Sleep(void); void I_Sleep(void);
boolean I_CheckFrameCap(precise_t start, precise_t end); /** \brief Sleeps for a variable amount of time, depending on how much time the last frame took.
\return void
*/
boolean I_FrameCapSleep(const int elapsed);
/** \brief Get events /** \brief Get events

View file

@ -509,7 +509,7 @@ void SCR_DisplayTicRate(void)
INT32 ticcntcolor = 0; INT32 ticcntcolor = 0;
const INT32 h = vid.height-(8*vid.dupy); const INT32 h = vid.height-(8*vid.dupy);
UINT32 cap = R_GetFramerateCap(); UINT32 cap = R_GetFramerateCap();
double fps = ceil(averageFPS); double fps = round(averageFPS);
if (gamestate == GS_NULL) if (gamestate == GS_NULL)
return; return;
@ -525,10 +525,11 @@ void SCR_DisplayTicRate(void)
ticcntcolor = V_GREENMAP; ticcntcolor = V_GREENMAP;
} }
if (cv_ticrate.value == 2) // compact counter if (cv_ticrate.value == 2) // compact counter
V_DrawString(vid.width-(32*vid.dupx), h, {
ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%04.0f", fps)); V_DrawRightAlignedString(vid.width, h,
ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%04.2f", averageFPS)); // use averageFPS directly
}
else if (cv_ticrate.value == 1) // full counter else if (cv_ticrate.value == 1) // full counter
{ {
if (cap > 0) if (cap > 0)

View file

@ -2167,6 +2167,10 @@ float I_GetTimeFrac(void)
return elapsed_tics; return elapsed_tics;
} }
//
// I_GetPreciseTime
// returns time in precise_t
//
precise_t I_GetPreciseTime(void) precise_t I_GetPreciseTime(void)
{ {
return SDL_GetPerformanceCounter(); return SDL_GetPerformanceCounter();
@ -2184,7 +2188,7 @@ int I_PreciseToMicros(precise_t d)
} }
// //
//I_StartupTimer // I_StartupTimer
// //
void I_StartupTimer(void) void I_StartupTimer(void)
{ {
@ -2195,71 +2199,82 @@ void I_StartupTimer(void)
elapsed_tics = 0.0; elapsed_tics = 0.0;
} }
//
// I_Sleep
// Sleeps by the value of cv_sleep
//
void I_Sleep(void) void I_Sleep(void)
{ {
if (cv_sleep.value != -1) if (cv_sleep.value != -1)
SDL_Delay(cv_sleep.value); SDL_Delay(cv_sleep.value);
} }
boolean I_CheckFrameCap(precise_t start, precise_t end) //
// I_FrameCapSleep
// Sleeps for a variable amount of time, depending on how much time the last frame took.
//
boolean I_FrameCapSleep(const int elapsed)
{ {
const INT64 delayGranularity = 2000;
// I picked 2ms as it's what GZDoom uses before it stops trying to sleep,
// but maybe other values might work better.
const UINT32 capFrames = R_GetFramerateCap(); const UINT32 capFrames = R_GetFramerateCap();
int capMicros = 0; int capMicros = 0;
int elapsed;
if (capFrames == 0) if (capFrames == 0)
{ {
// We don't want to cap. // We don't want to cap.
return false; return false;
} }
elapsed = I_PreciseToMicros(end - start);
capMicros = 1000000 / capFrames; capMicros = 1000000 / capFrames;
if (elapsed < capMicros) if (elapsed < capMicros)
{ {
// Experimental variable delay code. const INT64 error = capMicros / 40;
if (cv_sleep.value > 0) // 2.5% ... How much we might expect the framerate to flucuate.
// No exact logic behind this number, simply tried stuff until the framerate
// reached the cap 300 more often and only overshot it occasionally.
INT64 wait = (capMicros - elapsed) - error;
while (wait > 0)
{ {
const INT64 delayGranularity = 2000; // 2ms, I picked this as it's what GZDoom uses before it stops trying to sleep. precise_t sleepStart = I_GetPreciseTime();
INT64 wait = (capMicros - elapsed); precise_t sleepEnd = sleepStart;
int sleepElasped = 0;
while (wait > 0) if (wait > delayGranularity && cv_sleep.value != -1)
{ {
precise_t sleepStart = I_GetPreciseTime(); // Wait 1ms at a time (on default settings)
precise_t sleepEnd = sleepStart; // until we're close enough.
int sleepElasped = 0; SDL_Delay(cv_sleep.value);
if (wait > delayGranularity) sleepEnd = I_GetPreciseTime();
sleepElasped = I_PreciseToMicros(sleepEnd - sleepStart);
}
else
{
// When we have an extremely fine wait,
// we do this to spin-lock the remaining time.
while (sleepElasped < wait)
{ {
// Wait 1ms at a time (on default settings)
// until we're close enough.
SDL_Delay(cv_sleep.value);
sleepEnd = I_GetPreciseTime(); sleepEnd = I_GetPreciseTime();
sleepElasped = I_PreciseToMicros(sleepEnd - sleepStart); sleepElasped = I_PreciseToMicros(sleepEnd - sleepStart);
} }
else
{
// When we have an extremely fine wait,
// we do this to spin-lock the remaining time.
while (sleepElasped < wait) break;
{
sleepEnd = I_GetPreciseTime();
sleepElasped = I_PreciseToMicros(sleepEnd - sleepStart);
}
}
wait -= sleepElasped;
} }
wait -= sleepElasped;
} }
// We took our nap.
return true; return true;
} }
// Waited enough to draw again. // We're lagging behind.
return false; return false;
} }