From 80cb9994d52147c8da25d44fe3f1040b1046f787 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 25 Apr 2022 02:12:27 -0400 Subject: [PATCH] Move I_FinishUpdate to D_SRB2Loop to sync screen updates with FPS cap, use timestamps in I_FrameCapSleep to simplify the code --- src/d_main.c | 59 +++++++++++++++++++++++----------------------- src/i_system.h | 2 +- src/sdl/i_system.c | 53 +++++++++++++++-------------------------- src/sdl/i_video.c | 2 +- 4 files changed, 51 insertions(+), 65 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 0931944fe..9cfd694e7 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -297,17 +297,17 @@ gamestate_t wipegamestate = GS_LEVEL; INT16 wipetypepre = -1; INT16 wipetypepost = -1; -static void D_Display(void) +static boolean D_Display(void) { boolean forcerefresh = false; static boolean wipe = false; INT32 wipedefindex = 0; if (dedicated) - return; + return false; if (nodrawers) - return; // for comparative timing/profiling + return false; // for comparative timing/profiling // Lactozilla: Switching renderers works by checking // if the game has to do it right when the frame @@ -681,10 +681,10 @@ static void D_Display(void) M_DrawPerfStats(); } - PS_START_TIMING(ps_swaptime); - I_FinishUpdate(); // page flip or blit buffer - PS_STOP_TIMING(ps_swaptime); + return true; // Do I_FinishUpdate in the main loop } + + return false; } // ========================================================================= @@ -701,9 +701,9 @@ void D_SRB2Loop(void) boolean ticked = false; boolean interp = false; boolean doDisplay = false; + boolean screenUpdate = false; - double frameTime = 0.0; - double frameElapsed = 0.0; + double frameEnd = 0.0; if (dedicated) server = true; @@ -755,9 +755,6 @@ void D_SRB2Loop(void) for (;;) { - frameTime = I_GetFrameTime(); - frameElapsed = 0.0; - if (lastwipetic) { oldentertics = lastwipetic; @@ -769,8 +766,6 @@ void D_SRB2Loop(void) realtics = entertic - oldentertics; oldentertics = entertic; - refreshdirmenu = 0; // not sure where to put this, here as good as any? - if (demoplayback && gamestate == GS_LEVEL) { // Nicer place to put this. @@ -784,13 +779,15 @@ void D_SRB2Loop(void) #endif interp = R_UsingFrameInterpolation(); - doDisplay = false; + doDisplay = screenUpdate = false; ticked = false; #ifdef HW3SOUND HW3S_BeginFrameUpdate(); #endif + refreshdirmenu = 0; // not sure where to put this, here as good as any? + if (realtics > 0 || singletics) { // don't skip more than 10 frames at a time @@ -879,14 +876,9 @@ void D_SRB2Loop(void) if (interp || doDisplay) { - D_Display(); + screenUpdate = D_Display(); } - if (moviemode) - M_SaveFrame(); - if (takescreenshot) // Only take screenshots after drawing. - M_DoScreenShot(); - // consoleplayer -> displayplayer (hear sounds from viewpoint) S_UpdateSounds(); // move positional sounds S_UpdateClosedCaptions(); @@ -897,18 +889,27 @@ void D_SRB2Loop(void) 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_GetFrameTime() - frameTime; - + // Fully completed frame made. + frameEnd = I_GetFrameTime(); if (!singletics) { - I_FrameCapSleep(frameElapsed); + I_FrameCapSleep(frameEnd); } + + // I_FinishUpdate is now here instead of D_Display, + // because it synchronizes it more closely with the frame counter. + if (screenUpdate == true) + { + PS_START_TIMING(ps_swaptime); + I_FinishUpdate(); // page flip or blit buffer + PS_STOP_TIMING(ps_swaptime); + } + + // Only take screenshots after drawing. + if (moviemode) + M_SaveFrame(); + if (takescreenshot) + M_DoScreenShot(); } } diff --git a/src/i_system.h b/src/i_system.h index a6f4ea70f..f607ec79c 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -72,7 +72,7 @@ void I_Sleep(void); \return void */ -boolean I_FrameCapSleep(const double elapsed); +boolean I_FrameCapSleep(const double frameStart); /** \brief Get events diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 21b262207..2e1bf0c17 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -2222,6 +2222,7 @@ double I_GetFrameTime(void) if (cap != frame_rate) { + // Maybe do this in a OnChange function for cv_fpscap? I_InitFrameTime(now, cap); } @@ -2260,23 +2261,24 @@ void I_StartupTimer(void) // void I_Sleep(void) { - if (cv_sleep.value != -1) + if (cv_sleep.value > 0) SDL_Delay(cv_sleep.value); } // // I_FrameCapSleep -// Sleeps for a variable amount of time, depending on how much time the last frame took. +// Sleeps for a variable amount of time, depending on how much time the frame took. // -boolean I_FrameCapSleep(const double elapsed) +boolean I_FrameCapSleep(const double t) { // SDL_Delay(1) gives me a range of around 1.95ms to 2.05ms. // Has a bit extra to be totally safe. const double delayGranularity = 2.1; + double frameMS = 0.0; - double capMS = 0.0; - double elapsedMS = 0.0; - double waitMS = 0.0; + double curTime = 0.0; + double destTime = 0.0; + double sleepTime = 0.0; if (frame_rate == 0) { @@ -2284,46 +2286,29 @@ boolean I_FrameCapSleep(const double elapsed) return false; } - capMS = 1000.0 / frame_rate; // Time of 1 frame, in milliseconds - elapsedMS = elapsed * capMS; // Convert elapsed from frame time to milliseconds. - waitMS = (capMS - elapsedMS); // How many MS to delay by. + curTime = I_GetFrameTime(); + destTime = floor(t) + 1.0; - if (waitMS <= 0.0) + if (curTime >= destTime) { - // Too small of a wait, don't delay. + // We're already behind schedule. return false; } - while (waitMS > 0.0) - { - double sleepStart = I_GetFrameTime(); - double sleepEnd = sleepStart; - double sleepElaspedMS = 0.0; + frameMS = frame_rate * 0.001; // 1ms as frame time + sleepTime = destTime - (delayGranularity * frameMS); - if (waitMS > delayGranularity && cv_sleep.value != -1) + while (curTime < destTime) + { + if (curTime < sleepTime && cv_sleep.value <= 0) { // Wait 1ms at a time (on default settings) // until we're close enough. SDL_Delay(cv_sleep.value); - - sleepEnd = I_GetFrameTime(); - sleepElaspedMS = (sleepEnd - sleepStart) * capMS; - } - else - { - // When we have an extremely fine wait, - // we do this to spin-lock the remaining time. - - while (sleepElaspedMS < waitMS) - { - sleepEnd = I_GetFrameTime(); - sleepElaspedMS = (sleepEnd - sleepStart) * capMS; - } - - break; } - waitMS -= sleepElaspedMS; + // This part will spin-lock the rest. + curTime = I_GetFrameTime(); } // We took our nap. diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index eedb60e09..8d1ea62d4 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1214,7 +1214,7 @@ void I_FinishUpdate(void) if (rendermode == render_none) return; //Alam: No software or OpenGl surface - //SCR_CalculateFPS(); // Moved to main loop + SCR_CalculateFPS(); if (I_SkipFrame()) return;