When vsync is enabled, cap the desired rfps.

With vsync enabled the render times of consecutive frames can diverge.
The first frame arrives right at the next display frame and is rendered
without waiting time. The next frame has to wait 20ms. The leads to some
problems with the move prediction if the client is asynchronous. Fix
this by capping the desired frame rate at the display refresh rate. Also
make sure that the network framerate is never higher then the renderer
framerate.

With the commit the timing is always correct:
* With no limit as much frames as possible are rendered. In this case
  rfps > nfps and everything's good.
* With vsync enabled rfps > nfps or rfps == nfps is given. Also rfps
  will never exceed the display refresh rate.
* On slow hardware either rfps > nfps or an implicit rfpc == nfps is
  given.
This commit is contained in:
Yamagi Burmeister 2016-08-14 16:35:48 +02:00
parent 0ba7614739
commit 4d9d555d8e
2 changed files with 58 additions and 3 deletions

View file

@ -43,6 +43,8 @@
#ifdef SDL2
#include <SDL2/SDL.h>
#include <SDL2/SDL_video.h>
#else // SDL1.2
#include <SDL/SDL.h>
#endif //SDL2
@ -66,6 +68,7 @@ SDL_Surface* window = NULL;
#endif
qboolean have_stencil = false;
qboolean vsync_active;
#ifdef X11GAMMA
XRRCrtcGamma** gammaRamps = NULL;
@ -602,6 +605,7 @@ GLimp_InitGraphics(qboolean fullscreen)
/* Set vsync */
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, gl_swapinterval->value ? 1 : 0);
vsync_active = gl_swapinterval->value ? true : false;
#endif
while (1)
@ -661,6 +665,7 @@ GLimp_InitGraphics(qboolean fullscreen)
/* Set vsync - TODO: -1 could be set for "late swap tearing" */
SDL_GL_SetSwapInterval(gl_swapinterval->value ? 1 : 0);
vsync_active = gl_swapinterval->value ? true : false;
#endif
/* Initialize the stencil buffer */
@ -750,6 +755,32 @@ void GLimp_GrabInput(qboolean grab)
#endif
}
/*
* Returns the VSync state.
*/
qboolean GLimp_VsyncEnabled(void)
{
return vsync_active;
}
/*
* Returns the current display refresh rate.
*/
int GLimp_GetRefreshRate(void)
{
int i;
int refresh = 0;
SDL_DisplayMode mode;
for (i = 0; i < SDL_GetNumVideoDisplays(); ++i)
{
SDL_GetCurrentDisplayMode(i, &mode);
refresh = refresh < mode.refresh_rate ? mode.refresh_rate : refresh;
}
return refresh;
}
/*
* Shuts the SDL render backend down
*/

View file

@ -707,9 +707,15 @@ CL_UpdateWindowedMouse(void)
}
}
qboolean GLimp_VsyncEnabled(void);
int GLimp_GetRefreshRate(void);
void
CL_Frame(int msec)
{
int nfps;
int rfps;
static int lasttimecalled;
static int packetdelta = 1000;
@ -725,6 +731,24 @@ CL_Frame(int msec)
return;
}
// Target render frame rate
if (GLimp_VsyncEnabled())
{
rfps = GLimp_GetRefreshRate();
if (rfps > gl_maxfps->value)
{
rfps = (int)gl_maxfps->value;
}
}
else
{
rfps = (int)gl_maxfps->value;
}
// The network framerate must not be higher then the render framerate
nfps = (cl_maxfps->value > rfps) ? rfps : cl_maxfps->value;
// Adjust deltas
packetdelta += msec;
renderdelta += msec;
@ -764,7 +788,7 @@ CL_Frame(int msec)
if (cl_async->value)
{
// Network frames
if (packetdelta < (1000.0f / cl_maxfps->value))
if (packetdelta < (1000.0f / nfps))
{
packetframe = false;
}
@ -774,7 +798,7 @@ CL_Frame(int msec)
}
// Render frames
if (renderdelta < (1000.0f / gl_maxfps->value))
if (renderdelta < (1000.0f / rfps))
{
renderframe = false;
}
@ -788,7 +812,7 @@ CL_Frame(int msec)
else
{
// Cap frames at gl_maxfps
if (renderdelta < (1000.0f / gl_maxfps->value))
if (renderdelta < (1000.0f / rfps))
{
renderframe = false;
packetframe = false;