Move I_FinishUpdate to D_SRB2Loop to sync screen updates with FPS cap, use timestamps in I_FrameCapSleep to simplify the code

This commit is contained in:
Sally Coolatta 2022-04-25 02:12:27 -04:00 committed by Eidolon
parent f482218913
commit 80cb9994d5
4 changed files with 51 additions and 65 deletions

View file

@ -297,17 +297,17 @@ gamestate_t wipegamestate = GS_LEVEL;
INT16 wipetypepre = -1; INT16 wipetypepre = -1;
INT16 wipetypepost = -1; INT16 wipetypepost = -1;
static void D_Display(void) static boolean D_Display(void)
{ {
boolean forcerefresh = false; boolean forcerefresh = false;
static boolean wipe = false; static boolean wipe = false;
INT32 wipedefindex = 0; INT32 wipedefindex = 0;
if (dedicated) if (dedicated)
return; return false;
if (nodrawers) if (nodrawers)
return; // for comparative timing/profiling return false; // for comparative timing/profiling
// Lactozilla: Switching renderers works by checking // Lactozilla: Switching renderers works by checking
// if the game has to do it right when the frame // if the game has to do it right when the frame
@ -681,10 +681,10 @@ static void D_Display(void)
M_DrawPerfStats(); M_DrawPerfStats();
} }
PS_START_TIMING(ps_swaptime); return true; // Do I_FinishUpdate in the main loop
I_FinishUpdate(); // page flip or blit buffer
PS_STOP_TIMING(ps_swaptime);
} }
return false;
} }
// ========================================================================= // =========================================================================
@ -701,9 +701,9 @@ void D_SRB2Loop(void)
boolean ticked = false; boolean ticked = false;
boolean interp = false; boolean interp = false;
boolean doDisplay = false; boolean doDisplay = false;
boolean screenUpdate = false;
double frameTime = 0.0; double frameEnd = 0.0;
double frameElapsed = 0.0;
if (dedicated) if (dedicated)
server = true; server = true;
@ -755,9 +755,6 @@ void D_SRB2Loop(void)
for (;;) for (;;)
{ {
frameTime = I_GetFrameTime();
frameElapsed = 0.0;
if (lastwipetic) if (lastwipetic)
{ {
oldentertics = lastwipetic; oldentertics = lastwipetic;
@ -769,8 +766,6 @@ void D_SRB2Loop(void)
realtics = entertic - oldentertics; realtics = entertic - oldentertics;
oldentertics = entertic; oldentertics = entertic;
refreshdirmenu = 0; // not sure where to put this, here as good as any?
if (demoplayback && gamestate == GS_LEVEL) if (demoplayback && gamestate == GS_LEVEL)
{ {
// Nicer place to put this. // Nicer place to put this.
@ -784,13 +779,15 @@ void D_SRB2Loop(void)
#endif #endif
interp = R_UsingFrameInterpolation(); interp = R_UsingFrameInterpolation();
doDisplay = false; doDisplay = screenUpdate = false;
ticked = false; ticked = false;
#ifdef HW3SOUND #ifdef HW3SOUND
HW3S_BeginFrameUpdate(); HW3S_BeginFrameUpdate();
#endif #endif
refreshdirmenu = 0; // not sure where to put this, here as good as any?
if (realtics > 0 || singletics) if (realtics > 0 || singletics)
{ {
// don't skip more than 10 frames at a time // don't skip more than 10 frames at a time
@ -879,14 +876,9 @@ void D_SRB2Loop(void)
if (interp || doDisplay) 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) // consoleplayer -> displayplayer (hear sounds from viewpoint)
S_UpdateSounds(); // move positional sounds S_UpdateSounds(); // move positional sounds
S_UpdateClosedCaptions(); S_UpdateClosedCaptions();
@ -897,18 +889,27 @@ void D_SRB2Loop(void)
LUA_Step(); LUA_Step();
// Moved to here from I_FinishUpdate. // Fully completed frame made.
// It doesn't track fades properly anymore by being here (might be easy fix), frameEnd = I_GetFrameTime();
// 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;
if (!singletics) 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();
} }
} }

View file

@ -72,7 +72,7 @@ void I_Sleep(void);
\return void \return void
*/ */
boolean I_FrameCapSleep(const double elapsed); boolean I_FrameCapSleep(const double frameStart);
/** \brief Get events /** \brief Get events

View file

@ -2222,6 +2222,7 @@ double I_GetFrameTime(void)
if (cap != frame_rate) if (cap != frame_rate)
{ {
// Maybe do this in a OnChange function for cv_fpscap?
I_InitFrameTime(now, cap); I_InitFrameTime(now, cap);
} }
@ -2260,23 +2261,24 @@ void I_StartupTimer(void)
// //
void I_Sleep(void) void I_Sleep(void)
{ {
if (cv_sleep.value != -1) if (cv_sleep.value > 0)
SDL_Delay(cv_sleep.value); SDL_Delay(cv_sleep.value);
} }
// //
// I_FrameCapSleep // 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. // SDL_Delay(1) gives me a range of around 1.95ms to 2.05ms.
// Has a bit extra to be totally safe. // Has a bit extra to be totally safe.
const double delayGranularity = 2.1; const double delayGranularity = 2.1;
double frameMS = 0.0;
double capMS = 0.0; double curTime = 0.0;
double elapsedMS = 0.0; double destTime = 0.0;
double waitMS = 0.0; double sleepTime = 0.0;
if (frame_rate == 0) if (frame_rate == 0)
{ {
@ -2284,46 +2286,29 @@ boolean I_FrameCapSleep(const double elapsed)
return false; return false;
} }
capMS = 1000.0 / frame_rate; // Time of 1 frame, in milliseconds curTime = I_GetFrameTime();
elapsedMS = elapsed * capMS; // Convert elapsed from frame time to milliseconds. destTime = floor(t) + 1.0;
waitMS = (capMS - elapsedMS); // How many MS to delay by.
if (waitMS <= 0.0) if (curTime >= destTime)
{ {
// Too small of a wait, don't delay. // We're already behind schedule.
return false; return false;
} }
while (waitMS > 0.0) frameMS = frame_rate * 0.001; // 1ms as frame time
{ sleepTime = destTime - (delayGranularity * frameMS);
double sleepStart = I_GetFrameTime();
double sleepEnd = sleepStart;
double sleepElaspedMS = 0.0;
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) // Wait 1ms at a time (on default settings)
// until we're close enough. // until we're close enough.
SDL_Delay(cv_sleep.value); 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; // This part will spin-lock the rest.
} curTime = I_GetFrameTime();
waitMS -= sleepElaspedMS;
} }
// We took our nap. // We took our nap.

View file

@ -1214,7 +1214,7 @@ void I_FinishUpdate(void)
if (rendermode == render_none) if (rendermode == render_none)
return; //Alam: No software or OpenGl surface return; //Alam: No software or OpenGl surface
//SCR_CalculateFPS(); // Moved to main loop SCR_CalculateFPS();
if (I_SkipFrame()) if (I_SkipFrame())
return; return;