Lots of FPS stuff

- Disabled VSync, due to the numerous problems it has.
- Instead, added an FPS cap.
- Frame interpolation is now tied to fpscap != 35.
- By default, the FPS cap is set to the monitor's refresh rate.
- Rewrote the FPS counter.

(This also consolidates several more commits ahead of this
fixing various issues. -eid)
This commit is contained in:
Sally Coolatta 2022-03-26 23:48:08 -04:00 committed by Eidolon
parent 41997c4549
commit c186d6402b
17 changed files with 342 additions and 147 deletions

View file

@ -5233,7 +5233,7 @@ boolean TryRunTics(tic_t realtics)
if (demoplayback)
{
neededtic = gametic + (realtics * cv_playbackspeed.value);
neededtic = gametic + realtics;
// start a game after a demo
maketic += realtics;
firstticstosend = maketic;

View file

@ -477,7 +477,7 @@ static void D_Display(void)
if (!automapactive && !dedicated && cv_renderview.value)
{
R_ApplyLevelInterpolators(cv_frameinterpolation.value == 1 ? rendertimefrac : FRACUNIT);
R_ApplyLevelInterpolators(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT);
PS_START_TIMING(ps_rendercalltime);
if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
{
@ -687,6 +687,29 @@ 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
// =========================================================================
@ -698,6 +721,8 @@ void D_SRB2Loop(void)
tic_t oldentertics = 0, entertic = 0, realtics = 0, rendertimeout = INFTICS;
static lumpnum_t gstartuplumpnum;
boolean ticked;
boolean interp;
boolean doDisplay = false;
if (dedicated)
server = true;
@ -762,14 +787,25 @@ void D_SRB2Loop(void)
refreshdirmenu = 0; // not sure where to put this, here as good as any?
if (demoplayback && gamestate == GS_LEVEL)
{
// Nicer place to put this.
realtics = realtics * cv_playbackspeed.value;
}
#ifdef DEBUGFILE
if (!realtics)
if (debugload)
debugload--;
#endif
if (!realtics && !singletics && cv_frameinterpolation.value != 1)
interp = R_UsingFrameInterpolation();
doDisplay = false;
ticked = false;
if (!realtics && !singletics && !interp)
{
// Non-interp sleep
I_Sleep();
continue;
}
@ -778,85 +814,115 @@ void D_SRB2Loop(void)
HW3S_BeginFrameUpdate();
#endif
// don't skip more than 10 frames at a time
// (fadein / fadeout cause massive frame skip!)
if (realtics > 8)
realtics = 1;
// process tics (but maybe not if realtic == 0)
ticked = TryRunTics(realtics);
if (cv_frameinterpolation.value == 1 && !(paused || P_AutoPause()))
if (realtics > 0 || singletics)
{
static float tictime;
// don't skip more than 10 frames at a time
// (fadein / fadeout cause massive frame skip!)
if (realtics > 8)
realtics = 1;
// process tics (but maybe not if realtic == 0)
ticked = TryRunTics(realtics);
if (lastdraw || singletics || gametic > rendergametic)
{
rendergametic = gametic;
rendertimeout = entertic+TICRATE/17;
doDisplay = true;
}
else if (rendertimeout < entertic) // in case the server hang or netsplit
{
// Lagless camera! Yay!
if (gamestate == GS_LEVEL && netgame)
{
// Evaluate the chase cam once for every local realtic
// This might actually be better suited inside G_Ticker or TryRunTics
for (tic_t chasecamtics = 0; chasecamtics < realtics; chasecamtics++)
{
if (splitscreen && camera2.chase)
P_MoveChaseCamera(&players[secondarydisplayplayer], &camera2, false);
if (camera.chase)
P_MoveChaseCamera(&players[displayplayer], &camera, false);
}
R_UpdateViewInterpolation();
}
doDisplay = true;
}
}
if (interp)
{
static float tictime = 0.0f;
static float prevtime = 0.0f;
float entertime = I_GetTimeFrac();
fixed_t entertimefrac;
fixed_t entertimefrac = FRACUNIT;
if (ticked)
{
tictime = entertime;
}
entertimefrac = FLOAT_TO_FIXED(entertime - tictime);
// 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 (D_CheckFrameCap())
{
continue;
}
// renderdeltatics is a bit awkard to evaluate, since the system time interface is whole tic-based
renderdeltatics = realtics * FRACUNIT;
if (entertimefrac > rendertimefrac)
renderdeltatics += entertimefrac - rendertimefrac;
else
renderdeltatics -= rendertimefrac - entertimefrac;
if (!(paused || P_AutoPause()))
{
#if 0
CONS_Printf("prevtime = %f\n", prevtime);
CONS_Printf("entertime = %f\n", entertime);
CONS_Printf("tictime = %f\n", tictime);
CONS_Printf("entertime - prevtime = %f\n", entertime - prevtime);
CONS_Printf("entertime - tictime = %f\n", entertime - tictime);
CONS_Printf("========\n");
#endif
rendertimefrac = entertimefrac;
if (entertime - prevtime >= 1.0f)
{
// Lagged for more frames than a gametic...
// No need for interpolation.
entertimefrac = FRACUNIT;
}
else
{
entertimefrac = min(FRACUNIT, FLOAT_TO_FIXED(entertime - tictime));
}
// renderdeltatics is a bit awkard to evaluate, since the system time interface is whole tic-based
renderdeltatics = realtics * FRACUNIT;
if (entertimefrac > rendertimefrac)
renderdeltatics += entertimefrac - rendertimefrac;
else
renderdeltatics -= rendertimefrac - entertimefrac;
rendertimefrac = entertimefrac;
}
prevtime = entertime;
}
else
{
rendertimefrac = FRACUNIT;
renderdeltatics = realtics * FRACUNIT;
rendertimefrac = FRACUNIT;
}
if (cv_frameinterpolation.value == 1)
if (interp || doDisplay)
{
D_Display();
}
if (lastdraw || singletics || gametic > rendergametic)
{
rendergametic = gametic;
rendertimeout = entertic+TICRATE/17;
// Update display, next frame, with current state.
// (Only display if not already done for frame interp)
cv_frameinterpolation.value == 0 ? D_Display() : 0;
if (moviemode)
M_SaveFrame();
if (takescreenshot) // Only take screenshots after drawing.
M_DoScreenShot();
}
else if (rendertimeout < entertic) // in case the server hang or netsplit
{
// Lagless camera! Yay!
if (gamestate == GS_LEVEL && netgame)
{
// Evaluate the chase cam once for every local realtic
// This might actually be better suited inside G_Ticker or TryRunTics
for (tic_t chasecamtics = 0; chasecamtics < realtics; chasecamtics++)
{
if (splitscreen && camera2.chase)
P_MoveChaseCamera(&players[secondarydisplayplayer], &camera2, false);
if (camera.chase)
P_MoveChaseCamera(&players[displayplayer], &camera, false);
}
R_UpdateViewInterpolation();
}
// (Only display if not already done for frame interp)
cv_frameinterpolation.value == 0 ? D_Display() : 0;
if (moviemode)
M_SaveFrame();
if (takescreenshot) // Only take screenshots after drawing.
M_DoScreenShot();
}
if (moviemode)
M_SaveFrame();
if (takescreenshot) // Only take screenshots after drawing.
M_DoScreenShot();
// consoleplayer -> displayplayer (hear sounds from viewpoint)
S_UpdateSounds(); // move positional sounds
@ -867,6 +933,11 @@ 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 game logic when its here.
SCR_CalculateFPS();
}
}

View file

@ -191,7 +191,7 @@ static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"},
static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}};
static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}};
static CV_PossibleValue_t sleeping_cons_t[] = {{-1, "MIN"}, {1000/TICRATE, "MAX"}, {0, NULL}};
static CV_PossibleValue_t sleeping_cons_t[] = {{0, "MIN"}, {1000/TICRATE, "MAX"}, {0, NULL}};
static CV_PossibleValue_t competitionboxes_cons_t[] = {{0, "Normal"}, {1, "Mystery"}, //{2, "Teleport"},
{3, "None"}, {0, NULL}};

View file

@ -3648,7 +3648,7 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
// uncapped/interpolation
interpmobjstate_t interp = {0};
if (cv_frameinterpolation.value == 1 && !paused)
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolateMobjState(thing, rendertimefrac, &interp);
}
@ -5089,7 +5089,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
dispoffset = thing->info->dispoffset;
if (cv_frameinterpolation.value == 1 && !paused)
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolateMobjState(thing, rendertimefrac, &interp);
}
@ -5502,7 +5502,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
interpmobjstate_t interp = {0};
// do interpolation
if (cv_frameinterpolation.value == 1 && !paused)
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolatePrecipMobjState(thing, rendertimefrac, &interp);
}

View file

@ -1344,7 +1344,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
float finalscale;
interpmobjstate_t interp;
if (cv_frameinterpolation.value == 1 && !paused)
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolateMobjState(spr->mobj, rendertimefrac, &interp);
}

View file

@ -56,7 +56,7 @@ precise_t I_GetPreciseTime(void);
/** \brief Converts a precise_t to microseconds and casts it to a 32 bit integer.
*/
int I_PreciseToMicros(precise_t);
int I_PreciseToMicros(precise_t d);
/** \brief The I_Sleep function
@ -64,6 +64,8 @@ int I_PreciseToMicros(precise_t);
*/
void I_Sleep(void);
boolean I_CheckFrameCap(precise_t start, precise_t end);
/** \brief Get events
Called by D_SRB2Loop,

View file

@ -151,4 +151,6 @@ void I_BeginRead(void);
*/
void I_EndRead(void);
UINT32 I_GetRefreshRate(void);
#endif

View file

@ -22,6 +22,7 @@
#include "d_main.h"
#include "d_netcmd.h"
#include "console.h"
#include "r_fps.h"
#include "r_local.h"
#include "hu_stuff.h"
#include "g_game.h"
@ -1380,16 +1381,14 @@ static menuitem_t OP_VideoOptionsMenu[] =
{IT_HEADER, NULL, "Diagnostic", NULL, 184},
{IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 190},
{IT_STRING | IT_CVAR, NULL, "Clear Before Redraw", &cv_homremoval, 195},
{IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 200},
{IT_STRING | IT_CVAR, NULL, "FPS Cap", &cv_fpscap, 195},
{IT_STRING | IT_CVAR, NULL, "Clear Before Redraw", &cv_homremoval, 200},
{IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 205},
#ifdef HWRENDER
{IT_HEADER, NULL, "Renderer", NULL, 208},
{IT_CALL | IT_STRING, NULL, "OpenGL Options...", M_OpenGLOptionsMenu, 214},
{IT_HEADER, NULL, "Renderer", NULL, 213},
{IT_CALL | IT_STRING, NULL, "OpenGL Options...", M_OpenGLOptionsMenu, 219},
#endif
{IT_HEADER, NULL, "Experimental", NULL, 222},
{IT_STRING | IT_CVAR, NULL, "Frame Interpolation", &cv_frameinterpolation, 228},
};
static menuitem_t OP_VideoModeMenu[] =

View file

@ -21,10 +21,52 @@
#include "p_spec.h"
#include "r_state.h"
#include "z_zone.h"
#include "console.h" // con_startup_loadprogress
#ifdef HWRENDER
#include "hardware/hw_main.h" // for cv_glshearing
#endif
static CV_PossibleValue_t fpscap_cons_t[] = {
{-1, "Match refresh rate"},
{0, "Unlimited"},
#ifdef DEVELOP
// Lower values are actually pretty useful for debugging interp problems!
{1, "One Singular Frame"},
{10, "10"},
{20, "20"},
{25, "25"},
{30, "30"},
#endif
{35, "35"},
{50, "50"},
{60, "60"},
{70, "70"},
{75, "75"},
{90, "90"},
{100, "100"},
{120, "120"},
{144, "144"},
{200, "200"},
{240, "240"},
{0, NULL}
};
consvar_t cv_fpscap = CVAR_INIT ("fpscap", "Match refresh rate", CV_SAVE, fpscap_cons_t, NULL);
UINT32 R_GetFramerateCap(void)
{
if (cv_fpscap.value < 0)
{
return I_GetRefreshRate();
}
return cv_fpscap.value;
}
boolean R_UsingFrameInterpolation(void)
{
return (R_GetFramerateCap() != TICRATE); // maybe use ">" instead?
}
static viewvars_t p1view_old;
static viewvars_t p1view_new;
static viewvars_t p2view_old;
@ -179,7 +221,7 @@ void R_SetViewContext(enum viewcontext_e _viewcontext)
fixed_t R_InterpolateFixed(fixed_t from, fixed_t to)
{
if (cv_frameinterpolation.value == 0)
if (!R_UsingFrameInterpolation())
{
return to;
}
@ -189,7 +231,7 @@ fixed_t R_InterpolateFixed(fixed_t from, fixed_t to)
angle_t R_InterpolateAngle(angle_t from, angle_t to)
{
if (cv_frameinterpolation.value == 0)
if (!R_UsingFrameInterpolation())
{
return to;
}

View file

@ -19,6 +19,11 @@
#include "p_local.h"
#include "r_state.h"
extern consvar_t cv_fpscap;
UINT32 R_GetFramerateCap(void);
boolean R_UsingFrameInterpolation(void);
enum viewcontext_e
{
VIEWCONTEXT_PLAYER1 = 0,

View file

@ -168,9 +168,6 @@ consvar_t cv_drawdist_precip = CVAR_INIT ("drawdist_precip", "1024", CV_SAVE, dr
//consvar_t cv_precipdensity = CVAR_INIT ("precipdensity", "Moderate", CV_SAVE, precipdensity_cons_t, NULL);
consvar_t cv_fov = CVAR_INIT ("fov", "90", CV_FLOAT|CV_CALL, fov_cons_t, Fov_OnChange);
// Frame interpolation/uncapped
consvar_t cv_frameinterpolation = {"frameinterpolation", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
// Okay, whoever said homremoval causes a performance hit should be shot.
consvar_t cv_homremoval = CVAR_INIT ("homremoval", "No", CV_SAVE, homremoval_cons_t, NULL);
@ -1199,7 +1196,7 @@ void R_SetupFrame(player_t *player)
// newview->sin = FINESINE(viewangle>>ANGLETOFINESHIFT);
// newview->cos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
R_InterpolateView(cv_frameinterpolation.value == 1 ? rendertimefrac : FRACUNIT);
R_InterpolateView(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT);
}
void R_SkyboxFrame(player_t *player)
@ -1343,7 +1340,7 @@ void R_SkyboxFrame(player_t *player)
// newview->sin = FINESINE(viewangle>>ANGLETOFINESHIFT);
// newview->cos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
R_InterpolateView(cv_frameinterpolation.value == 1 ? rendertimefrac : FRACUNIT);
R_InterpolateView(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT);
}
boolean R_ViewpointHasChasecam(player_t *player)
@ -1627,5 +1624,5 @@ void R_RegisterEngineStuff(void)
CV_RegisterVar(&cv_movebob);
// Frame interpolation/uncapped
CV_RegisterVar(&cv_frameinterpolation);
CV_RegisterVar(&cv_fpscap);
}

View file

@ -119,9 +119,6 @@ extern consvar_t cv_fov;
extern consvar_t cv_skybox;
extern consvar_t cv_tailspickup;
// Frame interpolation (uncapped framerate)
extern consvar_t cv_frameinterpolation;
// Called by startup code.
void R_Init(void);

View file

@ -1148,7 +1148,7 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope)
// for frame interpolation
interpmobjstate_t interp = {0};
if (cv_frameinterpolation.value == 1 && !paused)
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolateMobjState(thing, rendertimefrac, &interp);
}
@ -1281,7 +1281,7 @@ static void R_SkewShadowSprite(
// for frame interpolation
interpmobjstate_t interp = {0};
if (cv_frameinterpolation.value == 1 && !paused)
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolateMobjState(thing, rendertimefrac, &interp);
}
@ -1324,7 +1324,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
if (abs(groundz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes
if (cv_frameinterpolation.value == 1 && !paused)
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolateMobjState(thing, rendertimefrac, &interp);
}
@ -1530,7 +1530,7 @@ static void R_ProjectSprite(mobj_t *thing)
interpmobjstate_t interp = {0};
// do interpolation
if (cv_frameinterpolation.value == 1 && !paused)
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolateMobjState(thing, rendertimefrac, &interp);
}
@ -1850,7 +1850,7 @@ static void R_ProjectSprite(mobj_t *thing)
fixed_t linkscale;
thing = thing->tracer;
if (cv_frameinterpolation.value == 1 && !paused)
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolateMobjState(thing, rendertimefrac, &interp);
}
@ -2210,7 +2210,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
interpmobjstate_t interp = {0};
// do interpolation
if (cv_frameinterpolation.value == 1 && !paused)
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolatePrecipMobjState(thing, rendertimefrac, &interp);
}

View file

@ -33,12 +33,15 @@
#include "s_sound.h" // ditto
#include "g_game.h" // ditto
#include "p_local.h" // P_AutoPause()
#ifdef HWRENDER
#include "hardware/hw_main.h"
#include "hardware/hw_light.h"
#include "hardware/hw_model.h"
#endif
// SRB2Kart
#include "r_fps.h" // R_GetFramerateCap
#if defined (USEASM) && !defined (NORUSEASM)//&& (!defined (_MSC_VER) || (_MSC_VER <= 1200))
#define RUSEASM //MSC.NET can't patch itself
@ -67,6 +70,7 @@ static CV_PossibleValue_t scr_depth_cons_t[] = {{8, "8 bits"}, {16, "16 bits"},
consvar_t cv_scr_width = CVAR_INIT ("scr_width", "1280", CV_SAVE, CV_Unsigned, NULL);
consvar_t cv_scr_height = CVAR_INIT ("scr_height", "800", CV_SAVE, CV_Unsigned, NULL);
consvar_t cv_scr_depth = CVAR_INIT ("scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL);
consvar_t cv_renderview = CVAR_INIT ("renderview", "On", 0, CV_OnOff, NULL);
CV_PossibleValue_t cv_renderer_t[] = {
@ -447,86 +451,82 @@ boolean SCR_IsAspectCorrect(INT32 width, INT32 height)
);
}
// XMOD FPS display
// moved out of os-specific code for consistency
static boolean fpsgraph[TICRATE];
static tic_t lasttic;
static tic_t totaltics;
double averageFPS = 0.0f;
static UINT32 fpstime = 0;
static UINT32 lastupdatetime = 0;
#define FPS_SAMPLE_RATE (50000) // How often to update FPS samples, in microseconds
#define NUM_FPS_SAMPLES 16 // Number of samples to store
#define FPSUPDATERATE 1/20 // What fraction of a second to update at. The fraction will not simplify to 0, trust me.
#define FPSMAXSAMPLES 16
static double fps_samples[NUM_FPS_SAMPLES];
static UINT32 fpssamples[FPSMAXSAMPLES];
static UINT32 fpssampleslen = 0;
static UINT32 fpssum = 0;
double aproxfps = 0.0f;
void SCR_CalcAproxFps(void)
void SCR_CalculateFPS(void)
{
tic_t i = 0;
tic_t ontic = I_GetTime();
static boolean init = false;
totaltics = 0;
static precise_t startTime = 0;
precise_t endTime = 0;
// Update FPS time
if (I_PreciseToMicros(fpstime - lastupdatetime) > 1000000 * FPSUPDATERATE)
static precise_t updateTime = 0;
int updateElapsed = 0;
int i;
endTime = I_GetPreciseTime();
if (init == false)
{
if (fpssampleslen == FPSMAXSAMPLES)
{
fpssum -= fpssamples[0];
for (i = 1; i < fpssampleslen; i++)
fpssamples[i-1] = fpssamples[i];
}
else
fpssampleslen++;
fpssamples[fpssampleslen-1] = I_GetPreciseTime() - fpstime;
fpssum += fpssamples[fpssampleslen-1];
aproxfps = 1000000 / (I_PreciseToMicros(fpssum) / (double)fpssampleslen);
lastupdatetime = I_GetPreciseTime();
startTime = updateTime = endTime;
init = true;
return;
}
fpstime = I_GetPreciseTime();
updateElapsed = I_PreciseToMicros(endTime - updateTime);
// Update ticrate time
for (i = lasttic + 1; i < TICRATE+lasttic && i < ontic; ++i)
fpsgraph[i % TICRATE] = false;
if (updateElapsed >= FPS_SAMPLE_RATE)
{
static int sampleIndex = 0;
int frameElapsed = I_PreciseToMicros(endTime - startTime);
fpsgraph[ontic % TICRATE] = true;
fps_samples[sampleIndex] = frameElapsed / 1000.0f;
for (i = 0;i < TICRATE;++i)
if (fpsgraph[i])
++totaltics;
sampleIndex++;
if (sampleIndex >= NUM_FPS_SAMPLES)
sampleIndex = 0;
lasttic = ontic;
averageFPS = 0.0f;
for (i = 0; i < NUM_FPS_SAMPLES; i++)
{
averageFPS += fps_samples[i];
}
averageFPS = 1000.0f / (averageFPS / NUM_FPS_SAMPLES);
updateTime = endTime;
}
startTime = endTime;
}
void SCR_DisplayTicRate(void)
{
INT32 ticcntcolor = 0;
const INT32 h = vid.height-(8*vid.dupy);
UINT32 cap = R_GetFramerateCap();
double fps = ceil(averageFPS);
if (gamestate == GS_NULL)
return;
if (totaltics <= TICRATE/2) ticcntcolor = V_REDMAP;
else if (totaltics == TICRATE) ticcntcolor = V_GREENMAP;
if (totaltics <= cap/2) ticcntcolor = V_REDMAP;
else if (totaltics >= cap) ticcntcolor = V_GREENMAP;
if (cv_ticrate.value == 2) // compact counter
V_DrawString(vid.width-(24*vid.dupx), h,
ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%03.0f", aproxfps));
V_DrawString(vid.width-(32*vid.dupx), h,
ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%04.0f", fps));
else if (cv_ticrate.value == 1) // full counter
{
V_DrawString(vid.width-(88*vid.dupx), h,
V_DrawString(vid.width-(104*vid.dupx), h,
V_YELLOWMAP|V_NOSCALESTART|V_USERHUDTRANS, "FPS:");
V_DrawString(vid.width-(56*vid.dupx), h,
ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%03.0f/%03u", aproxfps, TICRATE));
V_DrawString(vid.width-(72*vid.dupx), h,
ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%4.0f/%4u", fps, cap));
}
}

View file

@ -181,7 +181,8 @@ extern boolean R_SSE2;
extern viddef_t vid;
extern INT32 setmodeneeded; // mode number to set if needed, or 0
extern UINT8 setrenderneeded;
extern double aproxfps;
extern double averageFPS;
void SCR_ChangeRenderer(void);
@ -212,7 +213,7 @@ void SCR_CheckDefaultMode(void);
// Set the mode number which is saved in the config
void SCR_SetDefaultMode(void);
void SCR_CalcAproxFps(void);
void SCR_CalculateFPS(void);
FUNCMATH boolean SCR_IsAspectCorrect(INT32 width, INT32 height);

View file

@ -186,6 +186,7 @@ static char returnWadPath[256];
#include "../m_argv.h"
#include "../r_main.h" // Frame interpolation/uncapped
#include "../r_fps.h"
#ifdef MAC_ALERT
#include "macosx/mac_alert.h"
@ -2200,6 +2201,57 @@ void I_Sleep(void)
SDL_Delay(cv_sleep.value);
}
boolean I_CheckFrameCap(precise_t start, precise_t end)
{
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)
{
// Wait to draw the next frame.
UINT32 wait = ((capMicros - elapsed) / 1000);
if (cv_sleep.value > 1)
{
// 1 is the default, and in non-interpolated mode is just the bare minimum wait.
// Since we're already adding some wait with an FPS cap, only apply when it's above 1.
wait += cv_sleep.value - 1;
}
// If the wait's greater than our granularity value,
// we'll just burn the couple extra cycles in the main loop
// in order to get to the next frame.
// This makes us get to the exact FPS cap more often.
// Higher values have more wasted CPU cycles, but the in-game frame performance is better.
// 10ms is the average clock tick of most OS scheduling.
// 15ms is a little more than that, for leniency on slow machines. (This helps mine reach a stable 60, at least!)
// (https://www.libsdl.org/release/SDL-1.2.15/docs/html/sdldelay.html)
#define DELAY_GRANULARITY 15
if (wait >= DELAY_GRANULARITY)
{
SDL_Delay(wait);
}
#undef DELAY_GRANULARITY
return true;
}
// Waited enough to draw again.
return false;
}
#ifdef NEWSIGNALHANDLER
static void newsignalhandler_Warn(const char *pr)
{

View file

@ -1214,7 +1214,7 @@ void I_FinishUpdate(void)
if (rendermode == render_none)
return; //Alam: No software or OpenGl surface
SCR_CalcAproxFps();
//SCR_CalculateFPS(); // Moved to main loop
if (I_SkipFrame())
return;
@ -1475,8 +1475,15 @@ static SDL_bool Impl_CreateContext(void)
int flags = 0; // Use this to set SDL_RENDERER_* flags now
if (usesdl2soft)
flags |= SDL_RENDERER_SOFTWARE;
#if 0
// This shit is BROKEN.
// - The version of SDL we're using cannot toggle VSync at runtime. We'll need a new SDL version implemented to have this work properly.
// - cv_vidwait is initialized before config is loaded, so it's forced to default value at runtime, and forced off when switching. The config loading code would need restructured.
// - With both this & frame interpolation on, I_FinishUpdate takes x10 longer. At this point, it is simpler to use a standard FPS cap.
// So you can probably guess why I'm kinda over this, I'm just disabling it.
else if (cv_vidwait.value)
flags |= SDL_RENDERER_PRESENTVSYNC;
#endif
if (!renderer)
renderer = SDL_CreateRenderer(window, -1, flags);
@ -1961,3 +1968,23 @@ void I_GetCursorPosition(INT32 *x, INT32 *y)
{
SDL_GetMouseState(x, y);
}
UINT32 I_GetRefreshRate(void)
{
int index = SDL_GetWindowDisplayIndex(window);
SDL_DisplayMode m;
if (SDL_WasInit(SDL_INIT_VIDEO) == 0)
{
// Video not init yet.
return 0;
}
if (SDL_GetDesktopDisplayMode(index, &m) != 0)
{
// Error has occurred.
return 0;
}
return m.refresh_rate;
}