mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2025-03-23 11:21:01 +00:00
I_GetFrameTime to try and improve frame pace
(It doesn't feel that much better though.)
This commit is contained in:
parent
3da9852d2a
commit
f482218913
3 changed files with 115 additions and 59 deletions
10
src/d_main.c
10
src/d_main.c
|
@ -702,8 +702,8 @@ void D_SRB2Loop(void)
|
|||
boolean interp = false;
|
||||
boolean doDisplay = false;
|
||||
|
||||
precise_t frameTime = 0;
|
||||
int frameElapsed = 0;
|
||||
double frameTime = 0.0;
|
||||
double frameElapsed = 0.0;
|
||||
|
||||
if (dedicated)
|
||||
server = true;
|
||||
|
@ -755,8 +755,8 @@ void D_SRB2Loop(void)
|
|||
|
||||
for (;;)
|
||||
{
|
||||
frameTime = I_GetPreciseTime();
|
||||
frameElapsed = 0;
|
||||
frameTime = I_GetFrameTime();
|
||||
frameElapsed = 0.0;
|
||||
|
||||
if (lastwipetic)
|
||||
{
|
||||
|
@ -903,7 +903,7 @@ void D_SRB2Loop(void)
|
|||
SCR_CalculateFPS();
|
||||
|
||||
// Fully completed frame made, handle frame cap delay.
|
||||
frameElapsed = I_PreciseToMicros(I_GetPreciseTime() - frameTime);
|
||||
frameElapsed = I_GetFrameTime() - frameTime;
|
||||
|
||||
if (!singletics)
|
||||
{
|
||||
|
|
|
@ -42,11 +42,11 @@ extern UINT8 keyboard_started;
|
|||
*/
|
||||
UINT32 I_GetFreeMem(UINT32 *total);
|
||||
|
||||
/** \brief Called by D_SRB2Loop, returns current time in tics.
|
||||
/** \brief Called by D_SRB2Loop, returns current time in game tics.
|
||||
*/
|
||||
tic_t I_GetTime(void);
|
||||
|
||||
/** \brief Get the current time in tics including fractions.
|
||||
/** \brief Get the current time in game tics, including fractions.
|
||||
*/
|
||||
float I_GetTimeFrac(void);
|
||||
|
||||
|
@ -58,6 +58,10 @@ precise_t I_GetPreciseTime(void);
|
|||
*/
|
||||
int I_PreciseToMicros(precise_t d);
|
||||
|
||||
/** \brief Get the current time in rendering tics, including fractions.
|
||||
*/
|
||||
double I_GetFrameTime(void);
|
||||
|
||||
/** \brief Sleeps by the value of cv_sleep
|
||||
|
||||
\return void
|
||||
|
@ -68,7 +72,7 @@ void I_Sleep(void);
|
|||
|
||||
\return void
|
||||
*/
|
||||
boolean I_FrameCapSleep(const int elapsed);
|
||||
boolean I_FrameCapSleep(const double elapsed);
|
||||
|
||||
/** \brief Get events
|
||||
|
||||
|
|
|
@ -2187,6 +2187,58 @@ int I_PreciseToMicros(precise_t d)
|
|||
return (int)(UINT64)(d / (timer_frequency / 1000000.0));
|
||||
}
|
||||
|
||||
//
|
||||
// I_GetFrameTime
|
||||
// returns time in 1/fpscap second tics
|
||||
//
|
||||
|
||||
static UINT32 frame_rate;
|
||||
|
||||
static double frame_frequency;
|
||||
static UINT64 frame_epoch;
|
||||
static double elapsed_frames;
|
||||
|
||||
static void I_InitFrameTime(const UINT64 now, const UINT32 cap)
|
||||
{
|
||||
frame_rate = cap;
|
||||
frame_epoch = now;
|
||||
|
||||
//elapsed_frames = 0.0;
|
||||
|
||||
if (frame_rate == 0)
|
||||
{
|
||||
// Shouldn't be used, but just in case...?
|
||||
frame_frequency = 1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
frame_frequency = timer_frequency / (double)frame_rate;
|
||||
}
|
||||
|
||||
double I_GetFrameTime(void)
|
||||
{
|
||||
const UINT64 now = SDL_GetPerformanceCounter();
|
||||
const UINT32 cap = R_GetFramerateCap();
|
||||
|
||||
if (cap != frame_rate)
|
||||
{
|
||||
I_InitFrameTime(now, cap);
|
||||
}
|
||||
|
||||
if (frame_rate == 0)
|
||||
{
|
||||
// Always advance a frame.
|
||||
elapsed_frames += 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
elapsed_frames += (now - frame_epoch) / frame_frequency;
|
||||
}
|
||||
|
||||
frame_epoch = now; // moving epoch
|
||||
return elapsed_frames;
|
||||
}
|
||||
|
||||
//
|
||||
// I_StartupTimer
|
||||
//
|
||||
|
@ -2197,6 +2249,9 @@ void I_StartupTimer(void)
|
|||
|
||||
tic_frequency = timer_frequency / (double)NEWTICRATE;
|
||||
elapsed_tics = 0.0;
|
||||
|
||||
I_InitFrameTime(tic_epoch, R_GetFramerateCap());
|
||||
elapsed_frames = 0.0;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -2213,69 +2268,66 @@ void I_Sleep(void)
|
|||
// I_FrameCapSleep
|
||||
// Sleeps for a variable amount of time, depending on how much time the last frame took.
|
||||
//
|
||||
boolean I_FrameCapSleep(const int elapsed)
|
||||
boolean I_FrameCapSleep(const double 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.
|
||||
// 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;
|
||||
|
||||
const UINT32 capFrames = R_GetFramerateCap();
|
||||
int capMicros = 0;
|
||||
double capMS = 0.0;
|
||||
double elapsedMS = 0.0;
|
||||
double waitMS = 0.0;
|
||||
|
||||
if (capFrames == 0)
|
||||
if (frame_rate == 0)
|
||||
{
|
||||
// We don't want to cap.
|
||||
return false;
|
||||
}
|
||||
|
||||
capMicros = 1000000 / capFrames;
|
||||
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.
|
||||
|
||||
if (elapsed < capMicros)
|
||||
if (waitMS <= 0.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)
|
||||
{
|
||||
precise_t sleepStart = I_GetPreciseTime();
|
||||
precise_t sleepEnd = sleepStart;
|
||||
int sleepElasped = 0;
|
||||
|
||||
if (wait > delayGranularity && cv_sleep.value != -1)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
wait -= sleepElasped;
|
||||
}
|
||||
|
||||
// We took our nap.
|
||||
return true;
|
||||
// Too small of a wait, don't delay.
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're lagging behind.
|
||||
return false;
|
||||
while (waitMS > 0.0)
|
||||
{
|
||||
double sleepStart = I_GetFrameTime();
|
||||
double sleepEnd = sleepStart;
|
||||
double sleepElaspedMS = 0.0;
|
||||
|
||||
if (waitMS > delayGranularity && cv_sleep.value != -1)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
// We took our nap.
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef NEWSIGNALHANDLER
|
||||
|
|
Loading…
Reference in a new issue