From b18e53417af84f4f30f972213e0dade3ad640942 Mon Sep 17 00:00:00 2001 From: Sally Coolatta <tehrealsalt@gmail.com> Date: Tue, 19 Apr 2022 12:06:17 -0400 Subject: [PATCH] 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. --- src/d_main.c | 75 +++++++++++++------------------------------- src/i_system.h | 8 +++-- src/screen.c | 9 +++--- src/sdl/i_system.c | 77 +++++++++++++++++++++++++++------------------- 4 files changed, 79 insertions(+), 90 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 470ec4fb72..f29fdd2249 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -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 // ========================================================================= @@ -720,10 +697,13 @@ void D_SRB2Loop(void) { tic_t oldentertics = 0, entertic = 0, realtics = 0, rendertimeout = INFTICS; static lumpnum_t gstartuplumpnum; - boolean ticked; - boolean interp; + + boolean ticked = false; + boolean interp = false; boolean doDisplay = false; - boolean frameCap = false; + + precise_t frameTime = 0; + int frameElapsed = 0; if (dedicated) server = true; @@ -775,6 +755,9 @@ void D_SRB2Loop(void) for (;;) { + frameTime = I_GetPreciseTime(); + frameElapsed = 0; + if (lastwipetic) { oldentertics = lastwipetic; @@ -804,19 +787,6 @@ void D_SRB2Loop(void) doDisplay = 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 HW3S_BeginFrameUpdate(); #endif @@ -872,15 +842,6 @@ void D_SRB2Loop(void) 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 0 @@ -912,11 +873,6 @@ void D_SRB2Loop(void) } else { - if (frameCap) - { - continue; - } - renderdeltatics = realtics * FRACUNIT; rendertimefrac = FRACUNIT; } @@ -940,6 +896,19 @@ void D_SRB2Loop(void) #endif 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); + } } } diff --git a/src/i_system.h b/src/i_system.h index fefe0a7c12..f813135cef 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -58,13 +58,17 @@ precise_t I_GetPreciseTime(void); */ int I_PreciseToMicros(precise_t d); -/** \brief The I_Sleep function +/** \brief Sleeps by the value of cv_sleep \return 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 diff --git a/src/screen.c b/src/screen.c index b51b5472f8..6ad36273b7 100644 --- a/src/screen.c +++ b/src/screen.c @@ -509,7 +509,7 @@ void SCR_DisplayTicRate(void) INT32 ticcntcolor = 0; const INT32 h = vid.height-(8*vid.dupy); UINT32 cap = R_GetFramerateCap(); - double fps = ceil(averageFPS); + double fps = round(averageFPS); if (gamestate == GS_NULL) return; @@ -525,10 +525,11 @@ void SCR_DisplayTicRate(void) ticcntcolor = V_GREENMAP; } - 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 { if (cap > 0) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index b3db8b455c..428186f307 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -2167,6 +2167,10 @@ float I_GetTimeFrac(void) return elapsed_tics; } +// +// I_GetPreciseTime +// returns time in precise_t +// precise_t I_GetPreciseTime(void) { return SDL_GetPerformanceCounter(); @@ -2184,7 +2188,7 @@ int I_PreciseToMicros(precise_t d) } // -//I_StartupTimer +// I_StartupTimer // void I_StartupTimer(void) { @@ -2195,71 +2199,82 @@ void I_StartupTimer(void) elapsed_tics = 0.0; } +// +// I_Sleep +// Sleeps by the value of cv_sleep +// void I_Sleep(void) { if (cv_sleep.value != -1) 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(); int capMicros = 0; - int elapsed; - if (capFrames == 0) { // We don't want to cap. return false; } - elapsed = I_PreciseToMicros(end - start); capMicros = 1000000 / capFrames; if (elapsed < capMicros) { - // Experimental variable delay code. - if (cv_sleep.value > 0) + const INT64 error = capMicros / 40; + // 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. - INT64 wait = (capMicros - elapsed); + precise_t sleepStart = I_GetPreciseTime(); + precise_t sleepEnd = sleepStart; + int sleepElasped = 0; - while (wait > 0) + if (wait > delayGranularity && cv_sleep.value != -1) { - precise_t sleepStart = I_GetPreciseTime(); - precise_t sleepEnd = sleepStart; - int sleepElasped = 0; + // Wait 1ms at a time (on default settings) + // until we're close enough. + 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(); 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) - { - sleepEnd = I_GetPreciseTime(); - sleepElasped = I_PreciseToMicros(sleepEnd - sleepStart); - } - } - - wait -= sleepElasped; + break; } + + wait -= sleepElasped; } + // We took our nap. return true; } - // Waited enough to draw again. + // We're lagging behind. return false; } -- GitLab