From 406857f57ae835e9f3f2bdbfbd4b90ae02a10d91 Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Thu, 31 Aug 2017 18:41:19 +0200 Subject: [PATCH 01/13] Make the Linux / Unix backend year 2038 compliant. --- src/backends/unix/system.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/backends/unix/system.c b/src/backends/unix/system.c index 8627515d..bb792a8e 100644 --- a/src/backends/unix/system.c +++ b/src/backends/unix/system.c @@ -84,18 +84,17 @@ Sys_Milliseconds(void) { struct timeval tp; struct timezone tzp; - static int secbase; + static long secbase; gettimeofday(&tp, &tzp); if (!secbase) { secbase = tp.tv_sec; - return tp.tv_usec / 1000; + return (int)(tp.tv_usec / 1000); } - curtime = (tp.tv_sec - secbase) * 1000 + tp.tv_usec / 1000; - + curtime = (int)((tp.tv_sec - secbase) * 1000 + tp.tv_usec / 1000); return curtime; } From 0fafaf735fc26969d5053d8228020b84a6e7484a Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Sun, 3 Sep 2017 13:32:55 +0200 Subject: [PATCH 02/13] Implement a Sys_Microsecond(). This is the same as the well known Sys_Milliseconds() but like the name suggests with microsecond precision. To be used in the upcoming new framecounter. --- src/backends/unix/system.c | 37 +++++--- src/backends/windows/system.c | 29 ++++++ src/client/cl_main.c | 16 ++++ src/client/cl_screen.c | 163 ++++++++++++++++++++++++++++++++++ src/common/header/common.h | 2 +- 5 files changed, 234 insertions(+), 13 deletions(-) diff --git a/src/backends/unix/system.c b/src/backends/unix/system.c index bb792a8e..40feb4e2 100644 --- a/src/backends/unix/system.c +++ b/src/backends/unix/system.c @@ -79,22 +79,35 @@ Sys_Init(void) { } +long long +Sys_Microseconds(void) +{ + static struct timespec last; + struct timespec now; + + clock_gettime(CLOCK_REALTIME, &now); + if(last.tv_sec == 0) + { + clock_gettime(CLOCK_REALTIME, &last); + return last.tv_nsec / 1000ll; + } + + long long sec = now.tv_sec - last.tv_sec; + long long nsec = now.tv_nsec - last.tv_nsec; + if(nsec < 0) + { + nsec += 1000000000ll; // 1s in ns + --sec; + } + + return sec*1000000ll + nsec/1000ll; +} + int Sys_Milliseconds(void) { - struct timeval tp; - struct timezone tzp; - static long secbase; + curtime = (int)(Sys_Microseconds()/1000ll); - gettimeofday(&tp, &tzp); - - if (!secbase) - { - secbase = tp.tv_sec; - return (int)(tp.tv_usec / 1000); - } - - curtime = (int)((tp.tv_sec - secbase) * 1000 + tp.tv_usec / 1000); return curtime; } diff --git a/src/backends/windows/system.c b/src/backends/windows/system.c index 7a0141c5..3577af22 100644 --- a/src/backends/windows/system.c +++ b/src/backends/windows/system.c @@ -431,6 +431,35 @@ Sys_Milliseconds(void) return curtime; } +long long +Sys_Microseconds(void) +{ + long long microseconds; + long long seconds; + static long long uSecbase; + + FILETIME ft; + unsigned long long tmpres = 0; + + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + tmpres /= 10; // Convert to microseconds. + tmpres -= 11644473600000000ULL; // ...and to unix epoch. + + microseconds = tmpres; + + if (!uSecbase) + { + uSecbase = microseconds - 1001; + } + + return microseconds - uSecbase; +} + void Sys_Sleep(int msec) { diff --git a/src/client/cl_main.c b/src/client/cl_main.c index e3caee37..37e59d4b 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -812,6 +812,22 @@ CL_Frame(int msec) { miscframe = false; } + + // Evil hack for vsync. Because the vsync is effectively a break, delaying each frame + // by up 1000.0 / display_hz milliseconds we can't effort to lose even more time with + // synchronizing between render- and packageframes. This lost time would accumulate, + // delaying the renderframe which leads to microstuttering. Bite the bullet and force + // renderframe == clientframe. But only if rframetime and nframetime differ, otherwise + // the movement predictions breaks. + if (R_IsVSyncActive()) + { + if ((packetframe || renderframe) && (cls.nframetime != cls.rframetime)) + { + renderframe = true; + packetframe = true; + miscframe = true; + } + } } else { diff --git a/src/client/cl_screen.c b/src/client/cl_screen.c index 2c640d53..72bf2123 100644 --- a/src/client/cl_screen.c +++ b/src/client/cl_screen.c @@ -1410,6 +1410,169 @@ SCR_DrawLayout(void) SCR_ExecuteLayoutString(cl.layout); } +<<<<<<< HEAD +||||||| parent of 12f21e2... Fix Sys_Microseconds(), on Unix use it in Sys_Milliseconds() +// ---- + +void +SCR_Framecounter(void) { + long long newtime; + static int frame; + static int frametimes[60] = {0}; + static long long oldtime; + + newtime = Sys_Microseconds(); + frametimes[frame] = (int)(newtime - oldtime); + float scale = SCR_GetConsoleScale(); + + if (cl_drawfps->value == 1) { + // Calculate average of frames. + int avg = 0; + int num = 0; + + for (int i = 0; i < 60; i++) { + if (frametimes[i] != 0) { + avg += frametimes[i]; + num++; + } + } + + + char str[10]; + snprintf(str, sizeof(str), "%3.2ffps", (1000.0 * 1000.0) / (avg / num)); + DrawStringScaled(scale*(viddef.width - 80), 0, str, scale); + } else if (cl_drawfps->value >= 2) { + // Calculate average of frames. + int avg = 0; + int num = 0; + + for (int i = 0; i < 60; i++) { + if (frametimes[i] != 0) { + avg += frametimes[i]; + num++; + } + } + + + // Find lowest and highest + int min = frametimes[0]; + int max = frametimes[1]; + + for (int i = 1; i < 60; i++) { + if ((frametimes[i] > 0) && (min < frametimes[i])) { + min = frametimes[i]; + } + + if ((frametimes[i] > 0) && (max > frametimes[i])) { + max = frametimes[i]; + } + } + + char str[64]; + snprintf(str, sizeof(str), "Min: %7.2ffps, Max: %7.2ffps, Avg: %7.2ffps", + (1000.0 * 1000.0) / min, (1000.0 * 1000.0) / max, (1000.0 * 1000.0) / (avg / num)); + DrawStringScaled(viddef.width - scale*(strlen(str)*8 + 2), 0, str, scale); + + if (cl_drawfps->value > 2) + { + snprintf(str, sizeof(str), "Max: %5.2fms, Min: %5.2fms, Avg: %5.2fms", + 0.001f*min, 0.001f*max, 0.001f*(avg / num)); + DrawStringScaled(viddef.width - scale*(strlen(str)*8 + 2), scale*10, str, scale); + } + } + + frame++; + + if (frame > 59) { + frame = 0; + } + + oldtime = newtime; +} + +// ---- + +======= +// ---- + +void +SCR_Framecounter(void) { + long long newtime; + static int frame; + static int frametimes[60] = {0}; + static long long oldtime; + + newtime = Sys_Microseconds(); + frametimes[frame] = (int)(newtime - oldtime); + + oldtime = newtime; + frame++; + if (frame > 59) { + frame = 0; + } + + float scale = SCR_GetConsoleScale(); + + if (cl_drawfps->value == 1) { + // Calculate average of frames. + int avg = 0; + int num = 0; + + for (int i = 0; i < 60; i++) { + if (frametimes[i] != 0) { + avg += frametimes[i]; + num++; + } + } + + + char str[10]; + snprintf(str, sizeof(str), "%3.2ffps", (1000.0 * 1000.0) / (avg / num)); + DrawStringScaled(scale*(viddef.width - 80), 0, str, scale); + } else if (cl_drawfps->value >= 2) { + // Calculate average of frames. + int avg = 0; + int num = 0; + + for (int i = 0; i < 60; i++) { + if (frametimes[i] != 0) { + avg += frametimes[i]; + num++; + } + } + + + // Find lowest and highest + int min = frametimes[0]; + int max = frametimes[1]; + + for (int i = 1; i < 60; i++) { + if ((frametimes[i] > 0) && (min < frametimes[i])) { + min = frametimes[i]; + } + + if ((frametimes[i] > 0) && (max > frametimes[i])) { + max = frametimes[i]; + } + } + + char str[64]; + snprintf(str, sizeof(str), "Min: %7.2ffps, Max: %7.2ffps, Avg: %7.2ffps", + (1000.0 * 1000.0) / min, (1000.0 * 1000.0) / max, (1000.0 * 1000.0) / (avg / num)); + DrawStringScaled(viddef.width - scale*(strlen(str)*8 + 2), 0, str, scale); + + if (cl_drawfps->value > 2) + { + snprintf(str, sizeof(str), "Max: %5.2fms, Min: %5.2fms, Avg: %5.2fms", + 0.001f*min, 0.001f*max, 0.001f*(avg / num)); + DrawStringScaled(viddef.width - scale*(strlen(str)*8 + 2), scale*10, str, scale); + } + } +} + +// ---- + +>>>>>>> 12f21e2... Fix Sys_Microseconds(), on Unix use it in Sys_Milliseconds() /* * This is called every frame, and can also be called * explicitly to flush text to the screen. diff --git a/src/common/header/common.h b/src/common/header/common.h index 7e329180..3140d89b 100644 --- a/src/common/header/common.h +++ b/src/common/header/common.h @@ -767,7 +767,7 @@ 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); void *Sys_GetProcAddress(void *handle, const char *sym); From 79f73da62be34963ea4969029fdd0028cc21aaff Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Sun, 3 Sep 2017 13:38:24 +0200 Subject: [PATCH 03/13] Implement a much better / more accurate framecounter. The old framecounter had two problems: * It measured only the time of the current render frame, not the total time spend between the last and the current render frame. Therefor the calculated value was too high. * It was based upon milliseconds and rather inaccurate. This new frame counter solves both problems. The total time spend between two render frames is measured and the measurement done in microseconds. There're three modes: * cl_drawfps 1 displayes the average frame rate calculated over the last 60 frames. * cl_drawfps 2 displays a nice string with minimal framerate, maximum framerate and average framerate. All three values are calculated over the last 60 frames. * cl_drawfps 3 is the same as number 2 but with a second line showing the raw values. TODO: * Discuss if cl_drawfps should be renamed to cl_showfps. All other status displays are named cl_show*. While at it remove several unsused drawing functions. --- src/client/cl_console.c | 35 ---------------- src/client/cl_main.c | 16 ------- src/client/cl_screen.c | 93 +---------------------------------------- 3 files changed, 1 insertion(+), 143 deletions(-) diff --git a/src/client/cl_console.c b/src/client/cl_console.c index 10d69de1..277e1d5a 100644 --- a/src/client/cl_console.c +++ b/src/client/cl_console.c @@ -35,12 +35,6 @@ extern char key_lines[NUM_KEY_LINES][MAXCMDLINE]; extern int edit_line; extern int key_linepos; -void -DrawString(int x, int y, char *s) -{ - DrawStringScaled(x, y, s, 1.0f); -} - void DrawStringScaled(int x, int y, char *s, float factor) { @@ -52,12 +46,6 @@ DrawStringScaled(int x, int y, char *s, float factor) } } -void -DrawAltString(int x, int y, char *s) -{ - DrawAltStringScaled(x, y, s, 1.0f); -} - void DrawAltStringScaled(int x, int y, char *s, float factor) { @@ -452,29 +440,6 @@ Con_Print(char *txt) } } -void -Con_CenteredPrint(char *text) -{ - int l; - char buffer[1024]; - - l = strlen(text); - l = (con.linewidth - l) / 2; - - if (l <= 0) - { - l = 0; - } - else - { - memset(buffer, ' ', l); - } - - strcpy(buffer + l, text); - strcat(buffer, "\n"); - Con_Print(buffer); -} - /* * The input line scrolls horizontally if * typing goes beyond the right edge diff --git a/src/client/cl_main.c b/src/client/cl_main.c index 37e59d4b..e3caee37 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -812,22 +812,6 @@ CL_Frame(int msec) { miscframe = false; } - - // Evil hack for vsync. Because the vsync is effectively a break, delaying each frame - // by up 1000.0 / display_hz milliseconds we can't effort to lose even more time with - // synchronizing between render- and packageframes. This lost time would accumulate, - // delaying the renderframe which leads to microstuttering. Bite the bullet and force - // renderframe == clientframe. But only if rframetime and nframetime differ, otherwise - // the movement predictions breaks. - if (R_IsVSyncActive()) - { - if ((packetframe || renderframe) && (cls.nframetime != cls.rframetime)) - { - renderframe = true; - packetframe = true; - miscframe = true; - } - } } else { diff --git a/src/client/cl_screen.c b/src/client/cl_screen.c index 72bf2123..e7a6f9d4 100644 --- a/src/client/cl_screen.c +++ b/src/client/cl_screen.c @@ -1410,89 +1410,6 @@ SCR_DrawLayout(void) SCR_ExecuteLayoutString(cl.layout); } -<<<<<<< HEAD -||||||| parent of 12f21e2... Fix Sys_Microseconds(), on Unix use it in Sys_Milliseconds() -// ---- - -void -SCR_Framecounter(void) { - long long newtime; - static int frame; - static int frametimes[60] = {0}; - static long long oldtime; - - newtime = Sys_Microseconds(); - frametimes[frame] = (int)(newtime - oldtime); - float scale = SCR_GetConsoleScale(); - - if (cl_drawfps->value == 1) { - // Calculate average of frames. - int avg = 0; - int num = 0; - - for (int i = 0; i < 60; i++) { - if (frametimes[i] != 0) { - avg += frametimes[i]; - num++; - } - } - - - char str[10]; - snprintf(str, sizeof(str), "%3.2ffps", (1000.0 * 1000.0) / (avg / num)); - DrawStringScaled(scale*(viddef.width - 80), 0, str, scale); - } else if (cl_drawfps->value >= 2) { - // Calculate average of frames. - int avg = 0; - int num = 0; - - for (int i = 0; i < 60; i++) { - if (frametimes[i] != 0) { - avg += frametimes[i]; - num++; - } - } - - - // Find lowest and highest - int min = frametimes[0]; - int max = frametimes[1]; - - for (int i = 1; i < 60; i++) { - if ((frametimes[i] > 0) && (min < frametimes[i])) { - min = frametimes[i]; - } - - if ((frametimes[i] > 0) && (max > frametimes[i])) { - max = frametimes[i]; - } - } - - char str[64]; - snprintf(str, sizeof(str), "Min: %7.2ffps, Max: %7.2ffps, Avg: %7.2ffps", - (1000.0 * 1000.0) / min, (1000.0 * 1000.0) / max, (1000.0 * 1000.0) / (avg / num)); - DrawStringScaled(viddef.width - scale*(strlen(str)*8 + 2), 0, str, scale); - - if (cl_drawfps->value > 2) - { - snprintf(str, sizeof(str), "Max: %5.2fms, Min: %5.2fms, Avg: %5.2fms", - 0.001f*min, 0.001f*max, 0.001f*(avg / num)); - DrawStringScaled(viddef.width - scale*(strlen(str)*8 + 2), scale*10, str, scale); - } - } - - frame++; - - if (frame > 59) { - frame = 0; - } - - oldtime = newtime; -} - -// ---- - -======= // ---- void @@ -1571,8 +1488,6 @@ SCR_Framecounter(void) { } // ---- - ->>>>>>> 12f21e2... Fix Sys_Microseconds(), on Unix use it in Sys_Milliseconds() /* * This is called every frame, and can also be called * explicitly to flush text to the screen. @@ -1698,13 +1613,6 @@ SCR_UpdateScreen(void) SCR_DrawNet(); SCR_CheckDrawCenterString(); - if (cl_drawfps->value) - { - char s[8]; - sprintf(s, "%3.0ffps", 1 / cls.rframetime); - DrawString(viddef.width - 64, 0, s); - } - if (scr_timegraph->value) { SCR_DebugGraph(cls.rframetime * 300, 0); @@ -1726,6 +1634,7 @@ SCR_UpdateScreen(void) } } + SCR_Framecounter(); R_EndFrame(); } From f16242e923d2085c023349cfea8a7c9f2f92818d Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Wed, 6 Sep 2017 09:01:20 +0200 Subject: [PATCH 04/13] Determine svs.realtime from curtime and not the global timing. This is the same as the client does for it's realtime. It looks at least somewhat more correct since it pevents rounding errors. And things are simplified a litte bit since the server timing is now independent of the global timing. --- src/common/header/common.h | 2 +- src/common/misc.c | 2 +- src/server/sv_main.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/header/common.h b/src/common/header/common.h index 3140d89b..efe679f7 100644 --- a/src/common/header/common.h +++ b/src/common/header/common.h @@ -784,6 +784,6 @@ void SCR_BeginLoadingPlaque(void); void SV_Init(void); void SV_Shutdown(char *finalmsg, qboolean reconnect); -void SV_Frame(int msec); +void SV_Frame(); #endif diff --git a/src/common/misc.c b/src/common/misc.c index 4cc2adb4..a5578fe3 100644 --- a/src/common/misc.c +++ b/src/common/misc.c @@ -380,7 +380,7 @@ Qcommon_Frame(int msec) } #endif - SV_Frame(msec); + SV_Frame(); #ifndef DEDICATED_ONLY if (host_speeds->value) diff --git a/src/server/sv_main.c b/src/server/sv_main.c index 6b0d0b32..1d9cfa96 100644 --- a/src/server/sv_main.c +++ b/src/server/sv_main.c @@ -376,7 +376,7 @@ SV_RunGameFrame(void) } void -SV_Frame(int msec) +SV_Frame() { #ifndef DEDICATED_ONLY time_before_game = time_after_game = 0; @@ -388,7 +388,7 @@ SV_Frame(int msec) return; } - svs.realtime += msec; + svs.realtime = curtime; /* keep the random time dependent */ randk(); From a0aa1c87c7612727bc38b4bb3f507eaa707fb67e Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Wed, 6 Sep 2017 09:09:17 +0200 Subject: [PATCH 05/13] Change the global timing in main() from milli- to microseconds. This is a no-op for now. We need this to get a much higher precision when calculating the frame times. This changes the fixedtime cvar from milli- to microseconds. --- src/backends/unix/main.c | 8 ++++---- src/common/misc.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/backends/unix/main.c b/src/backends/unix/main.c index ab742309..77ebaaa4 100644 --- a/src/backends/unix/main.c +++ b/src/backends/unix/main.c @@ -38,7 +38,7 @@ qboolean is_portable; int main(int argc, char **argv) { - int time, oldtime, newtime; + long long time, oldtime, newtime; int verLen, i; const char* versionString; @@ -134,7 +134,7 @@ main(int argc, char **argv) /* Do not delay reads on stdin*/ fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, NULL) | FNDELAY); - oldtime = Sys_Milliseconds(); + oldtime = Sys_Microseconds(); t.tv_sec = 0; /* The legendary Quake II mainloop */ @@ -149,10 +149,10 @@ main(int argc, char **argv) nanosleep(&t, NULL); #endif - newtime = Sys_Milliseconds(); + newtime = Sys_Microseconds(); time = newtime - oldtime; } - while (time < 1); + while (time < 1000); Qcommon_Frame(time); oldtime = newtime; diff --git a/src/common/misc.c b/src/common/misc.c index a5578fe3..046ddd26 100644 --- a/src/common/misc.c +++ b/src/common/misc.c @@ -388,7 +388,7 @@ Qcommon_Frame(int msec) time_between = Sys_Milliseconds(); } - CL_Frame(msec); + CL_Frame(msec / 1000); if (host_speeds->value) { From c32f4b0e4ab24b42d68d07bb330493ece6059b6f Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Wed, 6 Sep 2017 10:24:25 +0200 Subject: [PATCH 06/13] 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. --- src/backends/unix/system.c | 6 -- src/client/cl_main.c | 132 +++++-------------------------- src/common/header/common.h | 4 +- src/common/misc.c | 154 ++++++++++++++++++++++++++++++++++++- 4 files changed, 171 insertions(+), 125 deletions(-) diff --git a/src/backends/unix/system.c b/src/backends/unix/system.c index 40feb4e2..c055f156 100644 --- a/src/backends/unix/system.c +++ b/src/backends/unix/system.c @@ -111,12 +111,6 @@ Sys_Milliseconds(void) return curtime; } -void -Sys_Sleep(int msec) -{ - usleep((unsigned int)1000 * msec); -} - void Sys_Mkdir(char *path) { diff --git a/src/client/cl_main.c b/src/client/cl_main.c index e3caee37..ae750034 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -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(); } diff --git a/src/common/header/common.h b/src/common/header/common.h index efe679f7..cd0ccb04 100644 --- a/src/common/header/common.h +++ b/src/common/header/common.h @@ -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); diff --git a/src/common/misc.c b/src/common/misc.c index 046ddd26..d78a72b5 100644 --- a/src/common/misc.c +++ b/src/common/misc.c @@ -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 From 4ac97f8a2b2fc232d9603d47f73d38ed9b10758b Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Wed, 6 Sep 2017 10:28:57 +0200 Subject: [PATCH 07/13] Remove the brake from the mainloop. It's longer necessary now that we've refactored the global timing. --- src/backends/unix/main.c | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/src/backends/unix/main.c b/src/backends/unix/main.c index 77ebaaa4..20529e7a 100644 --- a/src/backends/unix/main.c +++ b/src/backends/unix/main.c @@ -38,14 +38,10 @@ qboolean is_portable; int main(int argc, char **argv) { - long long time, oldtime, newtime; int verLen, i; + long long oldtime, newtime; const char* versionString; -#ifndef BUSY_WAIT - struct timespec t; -#endif - /* register signal handler */ registerHandler(); @@ -135,26 +131,12 @@ main(int argc, char **argv) fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, NULL) | FNDELAY); oldtime = Sys_Microseconds(); - t.tv_sec = 0; - /* The legendary Quake II mainloop */ + /* The mainloop. The legend. */ while (1) { - /* find time spent rendering last frame */ - do - { -#ifndef BUSY_WAIT - /* Sleep 10 microseconds */ - t.tv_nsec = 10000; - nanosleep(&t, NULL); -#endif - - newtime = Sys_Microseconds(); - time = newtime - oldtime; - } - while (time < 1000); - - Qcommon_Frame(time); + newtime = Sys_Microseconds(); + Qcommon_Frame(newtime - oldtime); oldtime = newtime; } From 0a3c6f3786c97c6d62cb38c34ba9356641dd5af7 Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Wed, 6 Sep 2017 10:45:21 +0200 Subject: [PATCH 08/13] Fix timing debug cvars and don't run the terminal console too often. --- src/common/misc.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/common/misc.c b/src/common/misc.c index d78a72b5..ade63c2a 100644 --- a/src/common/misc.c +++ b/src/common/misc.c @@ -393,20 +393,18 @@ Qcommon_Frame(int msec) } } + + // Timing debug crap. Just for historical reasons. if (fixedtime->value) { - msec = fixedtime->value; + msec = (int)fixedtime->value; } else if (timescale->value) { msec *= timescale->value; - - if (msec < 1) - { - msec = 1; - } } + #ifndef DEDICATED_ONLY if (showtrace->value) { @@ -482,18 +480,19 @@ Qcommon_Frame(int msec) } - do - { - s = Sys_ConsoleInput(); + // No need to run the terminal console too often. + if (timeframe) { + do { + s = Sys_ConsoleInput(); - if (s) - { - Cbuf_AddText(va("%s\n", s)); - } + if (s) { + Cbuf_AddText(va("%s\n", s)); + } + } while (s); + + Cbuf_Execute(); } - while (s); - Cbuf_Execute(); #ifndef DEDICATED_ONLY if (host_speeds->value) @@ -502,21 +501,27 @@ Qcommon_Frame(int msec) } #endif + + // Run the serverframe. if (packetframe) { SV_Frame(); } + #ifndef DEDICATED_ONLY if (host_speeds->value) { time_between = Sys_Milliseconds(); } + + // Run the client frame. if (packetframe || renderframe || miscframe || timeframe) { CL_Frame(packetdelta, renderdelta, miscdelta, timedelta, packetframe, renderframe, miscframe); } + if (host_speeds->value) { int all, sv, gm, cl, rf; @@ -551,7 +556,6 @@ Qcommon_Frame(int msec) if (timeframe) { timedelta = 0; } - } void From 465963a1a5cd95c9a2c99b9039029ce47d9720f3 Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Wed, 6 Sep 2017 14:38:00 +0200 Subject: [PATCH 09/13] Reconnect the server to the global timing. Having the server in an own timing zone seems to simplify things but introduces slight timing discrepancies. The most visible effect is that the game runs a little bit too fast, especially in the first cl_maxfps frames. Therefor: Remove timeframes, they're unnecessary. Track the time since the last (client|server) frame instead and pass it to the client and server when it's called. --- src/common/header/common.h | 2 +- src/common/misc.c | 51 +++++++++++++++----------------------- src/server/sv_main.c | 4 +-- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/common/header/common.h b/src/common/header/common.h index cd0ccb04..4021ab68 100644 --- a/src/common/header/common.h +++ b/src/common/header/common.h @@ -784,6 +784,6 @@ void SCR_BeginLoadingPlaque(void); void SV_Init(void); void SV_Shutdown(char *finalmsg, qboolean reconnect); -void SV_Frame(); +void SV_Frame(int msec); #endif diff --git a/src/common/misc.c b/src/common/misc.c index ade63c2a..a83c877a 100644 --- a/src/common/misc.c +++ b/src/common/misc.c @@ -327,8 +327,11 @@ Qcommon_Frame(int msec) // Time since last misc. frame in microsec. static int miscdelta = 100000; - // Accumulated time since last (packet|render|misc|time) frame. - static int timedelta = 1001; + // Accumulated time since last client run. + static int clienttimedelta = 0; + + // Accumulated time since last server run. + static int servertimedelta = 0; /* A packetframe runs the server and the client, but not the renderer. The minimal interval of @@ -348,12 +351,6 @@ Qcommon_Frame(int msec) 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 @@ -441,7 +438,8 @@ Qcommon_Frame(int msec) packetdelta += msec; renderdelta += msec; miscdelta += msec; - timedelta += msec; + clienttimedelta += msec; + servertimedelta += msec; if (cl_async->value) { @@ -474,24 +472,17 @@ Qcommon_Frame(int msec) } } - if (timedelta < 1001) - { - timeframe = false; - } + // Dedicated server terminal console. + do { + s = Sys_ConsoleInput(); - // No need to run the terminal console too often. - if (timeframe) { - do { - s = Sys_ConsoleInput(); + if (s) { + Cbuf_AddText(va("%s\n", s)); + } + } while (s); - if (s) { - Cbuf_AddText(va("%s\n", s)); - } - } while (s); - - Cbuf_Execute(); - } + Cbuf_Execute(); #ifndef DEDICATED_ONLY @@ -504,7 +495,8 @@ Qcommon_Frame(int msec) // Run the serverframe. if (packetframe) { - SV_Frame(); + SV_Frame(servertimedelta); + servertimedelta = 0; } @@ -516,9 +508,10 @@ Qcommon_Frame(int msec) // Run the client frame. - if (packetframe || renderframe || miscframe || timeframe) { - CL_Frame(packetdelta, renderdelta, miscdelta, timedelta, + if (packetframe || renderframe || miscframe) { + CL_Frame(packetdelta, renderdelta, miscdelta, clienttimedelta, packetframe, renderframe, miscframe); + clienttimedelta = 0; } @@ -552,10 +545,6 @@ Qcommon_Frame(int msec) if (miscframe) { miscdelta = 0; } - - if (timeframe) { - timedelta = 0; - } } void diff --git a/src/server/sv_main.c b/src/server/sv_main.c index 1d9cfa96..69f86d8c 100644 --- a/src/server/sv_main.c +++ b/src/server/sv_main.c @@ -376,7 +376,7 @@ SV_RunGameFrame(void) } void -SV_Frame() +SV_Frame(int msec) { #ifndef DEDICATED_ONLY time_before_game = time_after_game = 0; @@ -388,7 +388,7 @@ SV_Frame() return; } - svs.realtime = curtime; + svs.realtime += msec / 1000; /* keep the random time dependent */ randk(); From a3ce70d2e3327ca06fc084645286eda67d7927a3 Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Wed, 6 Sep 2017 15:53:44 +0200 Subject: [PATCH 10/13] Fix dedicated server build. --- src/common/misc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/common/misc.c b/src/common/misc.c index a83c877a..e0f95765 100644 --- a/src/common/misc.c +++ b/src/common/misc.c @@ -51,8 +51,10 @@ extern jmp_buf abortframe; /* an ERR_DROP occured, exit the entire frame */ extern zhead_t z_chain; // Forward declarations +#ifndef DEDICATED_ONLY int GLimp_GetRefreshRate(void); qboolean R_IsVSyncActive(void); +#endif static byte chktbl[1024] = { 0x84, 0x47, 0x51, 0xc1, 0x93, 0x22, 0x21, 0x24, 0x2f, 0x66, 0x60, 0x4d, 0xb0, 0x7c, 0xda, @@ -417,6 +419,7 @@ Qcommon_Frame(int msec) // Calculate target packet- and renderframerate. +#ifndef DEDICATED_ONLY if (R_IsVSyncActive()) { rfps = GLimp_GetRefreshRate(); @@ -432,6 +435,10 @@ Qcommon_Frame(int msec) } pfps = (cl_maxfps->value > rfps) ? rfps : cl_maxfps->value; +#else + pfps = (int)cl_maxfps->value; + rfps = (int)gl_maxfps->value; +#endif // Calculate timings. From 5ece000c1896bab0b7d75f56b0d81acb2a71312c Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Wed, 6 Sep 2017 17:42:52 +0200 Subject: [PATCH 11/13] Insert 5 microseconds sleep time each frame. This shouldn't have any noteable impact on timing (besides the machine is way too slow for Quake II) and saves a lot of CPU cycles. 100% load vs. 17% load on my desktop. --- src/backends/unix/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/backends/unix/main.c b/src/backends/unix/main.c index 20529e7a..899b3962 100644 --- a/src/backends/unix/main.c +++ b/src/backends/unix/main.c @@ -135,6 +135,9 @@ main(int argc, char **argv) /* The mainloop. The legend. */ while (1) { + struct timespec t = {0, 5000}; + nanosleep(&t, NULL); + newtime = Sys_Microseconds(); Qcommon_Frame(newtime - oldtime); oldtime = newtime; From 0c3c2976cffaddf41d0b446dcaad10b1f552d41a Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Wed, 6 Sep 2017 17:56:34 +0200 Subject: [PATCH 12/13] Fix timedemos. --- src/client/cl_main.c | 10 --------- src/common/misc.c | 49 ++++++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/src/client/cl_main.c b/src/client/cl_main.c index ae750034..7b9e08e3 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -56,7 +56,6 @@ cvar_t *cl_showmiss; cvar_t *cl_showclamp; cvar_t *cl_paused; -cvar_t *cl_timedemo; cvar_t *lookspring; cvar_t *lookstrafe; @@ -505,7 +504,6 @@ CL_InitLocal(void) cl_showclamp = Cvar_Get("showclamp", "0", 0); cl_timeout = Cvar_Get("cl_timeout", "120", 0); cl_paused = Cvar_Get("paused", "0", 0); - cl_timedemo = Cvar_Get("timedemo", "0", 0); gl_stereo = Cvar_Get( "gl_stereo", "0", CVAR_ARCHIVE ); gl_stereo_separation = Cvar_Get( "gl_stereo_separation", "1", CVAR_ARCHIVE ); @@ -746,14 +744,6 @@ CL_Frame(int packetdelta, int renderdelta, int miscdelta, int timedelta, packetframe = true; } } - else - { - /* 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 if (packetframe || renderframe) diff --git a/src/common/misc.c b/src/common/misc.c index e0f95765..e55e29f2 100644 --- a/src/common/misc.c +++ b/src/common/misc.c @@ -40,6 +40,7 @@ cvar_t *fixedtime; cvar_t *cl_maxfps; cvar_t *gl_maxfps; cvar_t *cl_async; +cvar_t *cl_timedemo; #ifndef DEDICATED_ONLY cvar_t *showtrace; @@ -244,6 +245,7 @@ Qcommon_Init(int argc, char **argv) timescale = Cvar_Get("timescale", "1", 0); fixedtime = Cvar_Get("fixedtime", "0", 0); logfile_active = Cvar_Get("logfile", "1", CVAR_ARCHIVE); + cl_timedemo = Cvar_Get("timedemo", "0", 0); #ifndef DEDICATED_ONLY showtrace = Cvar_Get("showtrace", "0", 0); #endif @@ -448,35 +450,34 @@ Qcommon_Frame(int msec) clienttimedelta += msec; servertimedelta += msec; - if (cl_async->value) - { - // Network frames.. - if (packetdelta < (1000000.0f / pfps)) - { - packetframe = false; - } + if (!cl_timedemo->value) { + if (cl_async->value) { + // Network frames.. + if (packetdelta < (1000000.0f / pfps)) { + packetframe = false; + } - // Render frames. - if (renderdelta < (1000000.0f / rfps)) - { - renderframe = false; - } + // Render frames. + if (renderdelta < (1000000.0f / rfps)) { + renderframe = false; + } - // Misc. frames. - if (miscdelta < 100000.0f) - { - miscframe = 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; + } } } - else + else if (clienttimedelta < 1000 || servertimedelta < 1000) { - // Cap frames at target framerate. - if (renderdelta < (1000000.0f / rfps)) - { - renderframe = false; - packetframe = false; - miscframe = false; - } + return; } From d0cb89ff52f0d309a3a823fe3c5d30e0ab20fc68 Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Wed, 6 Sep 2017 18:07:34 +0200 Subject: [PATCH 13/13] Change Windows mainloop to microseconds. While at it have another look at it's Sys_*seconds() implementations. Also add a Sys_Nanosleep() and use it to throttle the game a litte bit. --- src/backends/unix/main.c | 3 +- src/backends/unix/system.c | 8 +++-- src/backends/windows/system.c | 58 +++++++++++++++++++---------------- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/backends/unix/main.c b/src/backends/unix/main.c index 899b3962..bd8a206c 100644 --- a/src/backends/unix/main.c +++ b/src/backends/unix/main.c @@ -41,6 +41,7 @@ main(int argc, char **argv) int verLen, i; long long oldtime, newtime; const char* versionString; + struct timespec t = {0, 5000}; /* register signal handler */ registerHandler(); @@ -135,7 +136,7 @@ main(int argc, char **argv) /* The mainloop. The legend. */ while (1) { - struct timespec t = {0, 5000}; + // Throttle the game a little bit. nanosleep(&t, NULL); newtime = Sys_Microseconds(); diff --git a/src/backends/unix/system.c b/src/backends/unix/system.c index c055f156..64944480 100644 --- a/src/backends/unix/system.c +++ b/src/backends/unix/system.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "../../common/header/common.h" #include "../../common/header/glob.h" @@ -85,21 +86,24 @@ Sys_Microseconds(void) static struct timespec last; struct timespec now; - clock_gettime(CLOCK_REALTIME, &now); + clock_gettime(CLOCK_MONOTONIC, &now); + if(last.tv_sec == 0) { - clock_gettime(CLOCK_REALTIME, &last); + clock_gettime(CLOCK_MONOTONIC, &last); return last.tv_nsec / 1000ll; } long long sec = now.tv_sec - last.tv_sec; long long nsec = now.tv_nsec - last.tv_nsec; + if(nsec < 0) { nsec += 1000000000ll; // 1s in ns --sec; } + curtime = (int)((sec*1000000ll + nsec/1000ll) / 1000ll); return sec*1000000ll + nsec/1000ll; } diff --git a/src/backends/windows/system.c b/src/backends/windows/system.c index 3577af22..d6e22d3b 100644 --- a/src/backends/windows/system.c +++ b/src/backends/windows/system.c @@ -414,28 +414,10 @@ ParseCommandLine(LPSTR lpCmdLine) /* ======================================================================= */ -int -Sys_Milliseconds(void) -{ - static int base; - static qboolean initialized = false; - - if (!initialized) - { /* let base retain 16 bits of effectively random data */ - base = timeGetTime() & 0xffff0000; - initialized = true; - } - - curtime = timeGetTime() - base; - - return curtime; -} - long long Sys_Microseconds(void) { long long microseconds; - long long seconds; static long long uSecbase; FILETIME ft; @@ -457,15 +439,40 @@ Sys_Microseconds(void) uSecbase = microseconds - 1001; } + curtime = (int)((microseconds - uSecbase) / 1000ll); return microseconds - uSecbase; } +int +Sys_Milliseconds(void) +{ + curtime = (int)(Sys_Microseconds()/1000ll); + + return curtime; +} + void Sys_Sleep(int msec) { Sleep(msec); } +void Sys_Nanosleep(int nanosec) +{ + HANDLE timer; + LARGE_INTEGER li; + + timer = CreateWaitableTimer(NULL, TRUE, NULL); + + // Windows has a max. resolution of 100ns. + li.QuadPart = -nanosec / 100; + + SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE); + WaitForSingleObject(timer, INFINITE); + + CloseHandle(timer); +} + /* ======================================================================= */ static qboolean @@ -774,7 +781,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; - int time, oldtime, newtime; + long long oldtime, newtime; /* Previous instances do not exist in Win32 */ if (hPrevInstance) @@ -846,7 +853,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, Qcommon_Init(argc, argv); /* Save our time */ - oldtime = Sys_Milliseconds(); + oldtime = Sys_Microseconds(); /* The legendary main loop */ while (1) @@ -869,14 +876,11 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, DispatchMessage(&msg); } - do - { - newtime = Sys_Milliseconds(); - time = newtime - oldtime; - } - while (time < 1); + // Throttle the game a little bit + Sys_Nanosleep(5000); - Qcommon_Frame(time); + newtime = Sys_Microseconds(); + Qcommon_Frame(newtime - oldtime); oldtime = newtime; }