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 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();
}
}

View file

@ -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

View file

@ -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.

View file

@ -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;