Move timing from CL_Frame() to Qcommon_Frame().

This allows us to implement the global timing without an artificial
brake slowing the game unnecessary down. This is only partial working,
more changes and fixes are coming.
This commit is contained in:
Yamagi Burmeister 2017-09-06 10:24:25 +02:00
parent a0aa1c87c7
commit c32f4b0e4a
4 changed files with 171 additions and 125 deletions

View file

@ -111,12 +111,6 @@ Sys_Milliseconds(void)
return curtime;
}
void
Sys_Sleep(int msec)
{
usleep((unsigned int)1000 * msec);
}
void
Sys_Mkdir(char *path)
{

View file

@ -44,14 +44,12 @@ cvar_t *cl_noskins;
cvar_t *cl_footsteps;
cvar_t *cl_timeout;
cvar_t *cl_predict;
cvar_t *cl_maxfps;
cvar_t *cl_drawfps;
cvar_t *cl_gun;
cvar_t *cl_add_particles;
cvar_t *cl_add_lights;
cvar_t *cl_add_entities;
cvar_t *cl_add_blend;
cvar_t *cl_async;
cvar_t *cl_shownet;
cvar_t *cl_showmiss;
@ -83,8 +81,6 @@ cvar_t *hand;
cvar_t *gender;
cvar_t *gender_auto;
cvar_t *gl_maxfps;
cvar_t *gl_stereo;
cvar_t *gl_stereo_separation;
cvar_t *gl_stereo_convergence;
@ -484,9 +480,7 @@ CL_InitLocal(void)
cl_footsteps = Cvar_Get("cl_footsteps", "1", 0);
cl_noskins = Cvar_Get("cl_noskins", "0", 0);
cl_predict = Cvar_Get("cl_predict", "1", 0);
cl_maxfps = Cvar_Get("cl_maxfps", "60", CVAR_ARCHIVE);
cl_drawfps = Cvar_Get("cl_drawfps", "0", CVAR_ARCHIVE);
cl_async = Cvar_Get("cl_async", "1", CVAR_ARCHIVE);
cl_upspeed = Cvar_Get("cl_upspeed", "200", 0);
cl_forwardspeed = Cvar_Get("cl_forwardspeed", "200", 0);
@ -513,8 +507,6 @@ CL_InitLocal(void)
cl_paused = Cvar_Get("paused", "0", 0);
cl_timedemo = Cvar_Get("timedemo", "0", 0);
gl_maxfps = Cvar_Get("gl_maxfps", "95", CVAR_ARCHIVE);
gl_stereo = Cvar_Get( "gl_stereo", "0", CVAR_ARCHIVE );
gl_stereo_separation = Cvar_Get( "gl_stereo_separation", "1", CVAR_ARCHIVE );
gl_stereo_convergence = Cvar_Get( "gl_stereo_convergence", "1.4", CVAR_ARCHIVE );
@ -711,60 +703,25 @@ CL_UpdateWindowedMouse(void)
}
}
int GLimp_GetRefreshRate(void);
qboolean R_IsVSyncActive(void);
void
CL_Frame(int msec)
CL_Frame(int packetdelta, int renderdelta, int miscdelta, int timedelta,
qboolean packetframe, qboolean renderframe, qboolean miscframe)
{
int nfps;
int rfps;
static int lasttimecalled;
static int packetdelta = 1000;
static int renderdelta = 1000;
static int miscdelta = 1000;
qboolean packetframe = true;
qboolean renderframe = true;
qboolean miscframe = true;
// Dedicated?
if (dedicated->value)
{
return;
}
// Target render frame rate
if (R_IsVSyncActive())
{
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;
miscdelta += msec;
// Calculate simulation time
cls.nframetime = packetdelta * 0.001f;
cls.rframetime = renderdelta * 0.001f;
// Calculate simulation time.
cls.nframetime = packetdelta / 1000000.0f;
cls.rframetime = renderdelta / 1000000.0f;
cls.realtime = curtime;
cl.time += msec;
cl.time += timedelta / 1000;
// Don't extrapolate too far ahead
// Don't extrapolate too far ahead.
if (cls.nframetime > 0.5f)
{
cls.nframetime = 0.5f;
@ -775,72 +732,27 @@ CL_Frame(int msec)
cls.rframetime = 0.5f;
}
/* if in the debugger last frame, don't timeout */
if (msec > 5000)
// if in the debugger last frame, don't timeout.
if (timedelta > 5000000)
{
cls.netchan.last_received = Sys_Milliseconds();
}
if (!cl_timedemo->value)
{
// Don't flood while connecting
if ((cls.state == ca_connected) && (packetdelta < 100))
// Don't throttle too much when connecting / loading.
if ((cls.state == ca_connected) && (packetdelta > 100000))
{
packetframe = false;
}
if (cl_async->value)
{
// Network frames
if (packetdelta < (1000.0f / nfps))
{
packetframe = false;
}
else if (cls.nframetime == cls.rframetime)
{
packetframe = false;
}
// Render frames
if (renderdelta < (1000.0f / rfps))
{
renderframe = false;
}
// Misc. stuff at 10 FPS
if (miscdelta < 100.0f)
{
miscframe = false;
}
}
else
{
// Cap frames at gl_maxfps
if (renderdelta < (1000.0f / rfps))
{
renderframe = false;
packetframe = false;
miscframe = false;
}
}
// Throttle the game a little bit. 1000 FPS are enough.
if (!packetframe && !renderframe && !cls.forcePacket && !userinfo_modified)
{
double frametime = (1000.0 / cl_maxfps->value - packetdelta) <= (1000.0 / gl_maxfps->value - renderdelta) ?
(1000.0 / cl_maxfps->value - packetdelta) : (1000.0 / gl_maxfps->value - renderdelta);
if (frametime > 1) // FIXME: why > ??
{
Sys_Sleep(1);
}
return;
packetframe = true;
}
}
else if (msec < 1)
else
{
return;
/* If we're running a timedemo ignore the global timing
and pump out as many frames as we can. */
packetframe = true;
renderframe = true;
miscframe = true;
}
// Update input stuff
@ -870,20 +782,14 @@ CL_Frame(int msec)
if (packetframe)
{
packetdelta = 0;
CL_SendCmd();
CL_CheckForResend();
}
if (renderframe)
{
renderdelta = 0;
if (miscframe)
{
miscdelta = 0;
VID_CheckChanges();
}

View file

@ -766,7 +766,6 @@ void Sys_Error(char *error, ...);
void Sys_Quit(void);
char *Sys_GetHomeDir(void);
const char *Sys_GetBinaryDir(void);
void Sys_Sleep(int msec);
long long Sys_Microseconds(void);
void Sys_FreeLibrary(void *handle);
void *Sys_LoadLibrary(const char *path, const char *sym, void **handle);
@ -778,7 +777,8 @@ void Sys_RedirectStdout(void);
void CL_Init(void);
void CL_Drop(void);
void CL_Shutdown(void);
void CL_Frame(int msec);
void CL_Frame(int packetdelta, int renderdelta, int miscdelta, int timedelta, qboolean packetframe, qboolean renderframe,
qboolean miscframe);
void Con_Print(char *text);
void SCR_BeginLoadingPlaque(void);

View file

@ -35,7 +35,11 @@ cvar_t *developer;
cvar_t *modder;
cvar_t *timescale;
cvar_t *fixedtime;
cvar_t *portable;
// For timing calculations.
cvar_t *cl_maxfps;
cvar_t *gl_maxfps;
cvar_t *cl_async;
#ifndef DEDICATED_ONLY
cvar_t *showtrace;
@ -46,6 +50,10 @@ extern cvar_t *logfile_active;
extern jmp_buf abortframe; /* an ERR_DROP occured, exit the entire frame */
extern zhead_t z_chain;
// Forward declarations
int GLimp_GetRefreshRate(void);
qboolean R_IsVSyncActive(void);
static byte chktbl[1024] = {
0x84, 0x47, 0x51, 0xc1, 0x93, 0x22, 0x21, 0x24, 0x2f, 0x66, 0x60, 0x4d, 0xb0, 0x7c, 0xda,
0x88, 0x54, 0x15, 0x2b, 0xc6, 0x6c, 0x89, 0xc5, 0x9d, 0x48, 0xee, 0xe6, 0x8a, 0xb5, 0xf4,
@ -244,6 +252,11 @@ Qcommon_Init(int argc, char **argv)
dedicated = Cvar_Get("dedicated", "0", CVAR_NOSET);
#endif
// For timing calculations.
cl_maxfps = Cvar_Get("cl_maxfps", "60", CVAR_ARCHIVE);
gl_maxfps = Cvar_Get("gl_maxfps", "95", CVAR_ARCHIVE);
cl_async = Cvar_Get("cl_async", "1", CVAR_ARCHIVE);
s = va("%s %s %s %s", YQ2VERSION, YQ2ARCH, BUILD_DATE, YQ2OSTYPE);
Cvar_Get("version", s, CVAR_SERVERINFO | CVAR_NOSET);
@ -299,11 +312,58 @@ Qcommon_Frame(int msec)
int time_after;
#endif
// Target packetframerate.
int pfps;
//Target renderframerate.
int rfps;
// Time since last packetframe in microsec.
static int packetdelta = 1000000;
// Time since last renderframe in microsec.
static int renderdelta = 1000000;
// Time since last misc. frame in microsec.
static int miscdelta = 100000;
// Accumulated time since last (packet|render|misc|time) frame.
static int timedelta = 1001;
/* A packetframe runs the server and the client,
but not the renderer. The minimal interval of
packetframes is about 10.000 microsec. If run
more often the movement prediction in pmove.c
breaks. That's the Q2 variant if the famous
125hz bug. */
qboolean packetframe = true;
/* A rendererframe runs the renderer, but not the
client. The minimal interval is about 1000
microseconds. */
qboolean renderframe = true;
/* A miscframe runs several maintenance task like
loading sound samples for the background music.
An interval of 100.000 microseconds is enough. */
qboolean miscframe = true;
/* Timeframes are empty frames. We need to call the
client at regular intervals to forward several
internal timers, even if there's nothing to do.
This is also necessary to speed up loading times. */
qboolean timeframe = true;
/* In case of ERR_DROP we're jumping here. Don't know
if that' really save but it seems to work. So leave
it alone. */
if (setjmp(abortframe))
{
return; /* an ERR_DROP was thrown */
return;
}
if (log_stats->modified)
{
log_stats->modified = false;
@ -360,6 +420,68 @@ Qcommon_Frame(int msec)
}
#endif
// Calculate target packet- and renderframerate.
if (R_IsVSyncActive())
{
rfps = GLimp_GetRefreshRate();
if (rfps > gl_maxfps->value)
{
rfps = (int)gl_maxfps->value;
}
}
else
{
rfps = (int)gl_maxfps->value;
}
pfps = (cl_maxfps->value > rfps) ? rfps : cl_maxfps->value;
// Calculate timings.
packetdelta += msec;
renderdelta += msec;
miscdelta += msec;
timedelta += msec;
if (cl_async->value)
{
// Network frames..
if (packetdelta < (1000000.0f / pfps))
{
packetframe = false;
}
// Render frames.
if (renderdelta < (1000000.0f / rfps))
{
renderframe = false;
}
// Misc. frames.
if (miscdelta < 100000.0f)
{
miscframe = false;
}
}
else
{
// Cap frames at target framerate.
if (renderdelta < (1000000.0f / rfps))
{
renderframe = false;
packetframe = false;
miscframe = false;
}
}
if (timedelta < 1001)
{
timeframe = false;
}
do
{
s = Sys_ConsoleInput();
@ -380,7 +502,9 @@ Qcommon_Frame(int msec)
}
#endif
SV_Frame();
if (packetframe) {
SV_Frame();
}
#ifndef DEDICATED_ONLY
if (host_speeds->value)
@ -388,7 +512,10 @@ Qcommon_Frame(int msec)
time_between = Sys_Milliseconds();
}
CL_Frame(msec / 1000);
if (packetframe || renderframe || miscframe || timeframe) {
CL_Frame(packetdelta, renderdelta, miscdelta, timedelta,
packetframe, renderframe, miscframe);
}
if (host_speeds->value)
{
@ -406,6 +533,25 @@ Qcommon_Frame(int msec)
all, sv, gm, cl, rf);
}
#endif
// Reset deltas if necessary.
if (packetframe) {
packetdelta = 0;
}
if (renderframe) {
renderdelta = 0;
}
if (miscframe) {
miscdelta = 0;
}
if (timeframe) {
timedelta = 0;
}
}
void