]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
sys_usenoclockbutbenchmark: use double for the timer,
[xonotic/darkplaces.git] / sys_shared.c
1 #ifdef WIN32
2 # ifndef DONT_USE_SETDLLDIRECTORY
3 #  define _WIN32_WINNT 0x0502
4 # endif
5 #endif
6
7 #include "quakedef.h"
8
9 #define SUPPORTDLL
10
11 #ifdef WIN32
12 # include <windows.h>
13 # include <mmsystem.h> // timeGetTime
14 # include <time.h> // localtime
15 #ifdef _MSC_VER
16 #pragma comment(lib, "winmm.lib")
17 #endif
18 #else
19 # include <unistd.h>
20 # include <fcntl.h>
21 # include <sys/time.h>
22 # include <time.h>
23 # ifdef SUPPORTDLL
24 #  include <dlfcn.h>
25 # endif
26 #endif
27
28 static char sys_timestring[128];
29 char *Sys_TimeString(const char *timeformat)
30 {
31         time_t mytime = time(NULL);
32 #if _MSC_VER >= 1400
33         struct tm mytm;
34         localtime_s(&mytm, &mytime);
35         strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
36 #else
37         strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
38 #endif
39         return sys_timestring;
40 }
41
42
43 extern qboolean host_shuttingdown;
44 void Sys_Quit (int returnvalue)
45 {
46         if (COM_CheckParm("-profilegameonly"))
47                 Sys_AllowProfiling(false);
48         host_shuttingdown = true;
49         Host_Shutdown();
50         exit(returnvalue);
51 }
52
53 #if defined(__linux__) || defined(__FreeBSD__)
54 #ifdef __cplusplus
55 extern "C"
56 #endif
57 int moncontrol(int);
58 #endif
59
60 void Sys_AllowProfiling(qboolean enable)
61 {
62 #if defined(__linux__) || defined(__FreeBSD__)
63         moncontrol(enable);
64 #endif
65 }
66
67
68 /*
69 ===============================================================================
70
71 DLL MANAGEMENT
72
73 ===============================================================================
74 */
75
76 static qboolean Sys_LoadLibraryFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qboolean complain, qboolean has_next)
77 {
78         const dllfunction_t *func;
79         if(dllhandle)
80         {
81                 for (func = fcts; func && func->name != NULL; func++)
82                         if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
83                         {
84                                 if(complain)
85                                 {
86                                         Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
87                                         if(has_next)
88                                                 Con_DPrintf("\nContinuing with");
89                                 }
90                                 goto notfound;
91                         }
92                 return true;
93
94         notfound:
95                 for (func = fcts; func && func->name != NULL; func++)
96                         *func->funcvariable = NULL;
97         }
98         return false;
99 }
100
101 qboolean Sys_LoadLibrary (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
102 {
103 #ifdef SUPPORTDLL
104         const dllfunction_t *func;
105         dllhandle_t dllhandle = 0;
106         unsigned int i;
107
108         if (handle == NULL)
109                 return false;
110
111 #ifndef WIN32
112 #ifdef PREFER_PRELOAD
113         dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
114         if(Sys_LoadLibraryFunctions(dllhandle, fcts, false, false))
115         {
116                 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
117                 *handle = dllhandle;
118                 return true;
119         }
120         else
121                 Sys_UnloadLibrary(&dllhandle);
122 notfound:
123 #endif
124 #endif
125
126         // Initializations
127         for (func = fcts; func && func->name != NULL; func++)
128                 *func->funcvariable = NULL;
129
130         // Try every possible name
131         Con_DPrintf ("Trying to load library...");
132         for (i = 0; dllnames[i] != NULL; i++)
133         {
134                 Con_DPrintf (" \"%s\"", dllnames[i]);
135 #ifdef WIN32
136 # ifndef DONT_USE_SETDLLDIRECTORY
137 #  ifdef _WIN64
138                 SetDllDirectory("bin64");
139 #  else
140                 SetDllDirectory("bin32");
141 #  endif
142 # endif
143                 dllhandle = LoadLibrary (dllnames[i]);
144                 // no need to unset this - we want ALL dlls to be loaded from there, anyway
145 #else
146                 dllhandle = dlopen (dllnames[i], RTLD_LAZY | RTLD_GLOBAL);
147 #endif
148                 if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(com_argv[0], '/'))))
149                         break;
150                 else
151                         Sys_UnloadLibrary (&dllhandle);
152         }
153
154         // see if the names can be loaded relative to the executable path
155         // (this is for Mac OSX which does not check next to the executable)
156         if (!dllhandle && strrchr(com_argv[0], '/'))
157         {
158                 char path[MAX_OSPATH];
159                 strlcpy(path, com_argv[0], sizeof(path));
160                 strrchr(path, '/')[1] = 0;
161                 for (i = 0; dllnames[i] != NULL; i++)
162                 {
163                         char temp[MAX_OSPATH];
164                         strlcpy(temp, path, sizeof(temp));
165                         strlcat(temp, dllnames[i], sizeof(temp));
166                         Con_DPrintf (" \"%s\"", temp);
167 #ifdef WIN32
168                         dllhandle = LoadLibrary (temp);
169 #else
170                         dllhandle = dlopen (temp, RTLD_LAZY | RTLD_GLOBAL);
171 #endif
172                         if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, dllnames[i+1] != NULL))
173                                 break;
174                         else
175                                 Sys_UnloadLibrary (&dllhandle);
176                 }
177         }
178
179         // No DLL found
180         if (! dllhandle)
181         {
182                 Con_DPrintf(" - failed.\n");
183                 return false;
184         }
185
186         Con_DPrintf(" - loaded.\n");
187
188         *handle = dllhandle;
189         return true;
190 #else
191         return false;
192 #endif
193 }
194
195 void Sys_UnloadLibrary (dllhandle_t* handle)
196 {
197 #ifdef SUPPORTDLL
198         if (handle == NULL || *handle == NULL)
199                 return;
200
201 #ifdef WIN32
202         FreeLibrary (*handle);
203 #else
204         dlclose (*handle);
205 #endif
206
207         *handle = NULL;
208 #endif
209 }
210
211 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
212 {
213 #ifdef SUPPORTDLL
214 #ifdef WIN32
215         return (void *)GetProcAddress (handle, name);
216 #else
217         return (void *)dlsym (handle, name);
218 #endif
219 #else
220         return NULL;
221 #endif
222 }
223
224 #ifdef WIN32
225 # define HAVE_TIMEGETTIME 1
226 # define HAVE_QUERYPERFORMANCECOUNTER 1
227 # define HAVE_Sleep 1
228 #endif
229
230 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
231 # define HAVE_CLOCKGETTIME 1
232 #endif
233
234 #ifndef WIN32
235 // FIXME improve this check, manpage hints to DST_NONE
236 # define HAVE_GETTIMEOFDAY 1
237 #endif
238
239 #ifndef WIN32
240 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
241 // (so much for POSIX...)
242 # ifdef FD_SET
243 #  define HAVE_SELECT 1
244 # endif
245 #endif
246
247 #ifndef WIN32
248 // FIXME improve this check
249 # define HAVE_USLEEP 1
250 #endif
251
252 // this one is referenced elsewhere
253 cvar_t sys_usenoclockbutbenchmark = {CVAR_SAVE, "sys_usenoclockbutbenchmark", "0", "don't use ANY real timing, and simulate a clock (for benchmarking); the game then runs as fast as possible. Run a QC mod with bots that does some stuff, then does a quit at the end, to benchmark a server. NEVER do this on a public server."};
254
255 // these are not
256 static cvar_t sys_debugsleep = {0, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
257 static cvar_t sys_usesdlgetticks = {CVAR_SAVE, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
258 static cvar_t sys_usesdldelay = {CVAR_SAVE, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
259 #if HAVE_QUERYPERFORMANCECOUNTER
260 static cvar_t sys_usequeryperformancecounter = {CVAR_SAVE, "sys_usequeryperformancecounter", "0", "use windows QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power) for timing rather than timeGetTime function (which has issues on some motherboards)"};
261 #endif
262 #if HAVE_CLOCKGETTIME
263 static cvar_t sys_useclockgettime = {CVAR_SAVE, "sys_useclockgettime", "0", "use POSIX clock_gettime function (which has issues if the system clock speed is far off, as it can't get fixed by NTP) for timing rather than gettimeofday (which has issues if the system time is stepped by ntpdate, or apparently on some Xen installations)"};
264 #endif
265
266 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
267
268 void Sys_Init_Commands (void)
269 {
270         Cvar_RegisterVariable(&sys_debugsleep);
271         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
272 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
273         if(sys_supportsdlgetticks)
274         {
275                 Cvar_RegisterVariable(&sys_usesdlgetticks);
276                 Cvar_RegisterVariable(&sys_usesdldelay);
277         }
278 #endif
279 #if HAVE_QUERYPERFORMANCECOUNTER
280         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
281 #endif
282 #if HAVE_CLOCKGETTIME
283         Cvar_RegisterVariable(&sys_useclockgettime);
284 #endif
285 }
286
287 double Sys_DoubleTime(void)
288 {
289         static int first = true;
290         static double oldtime = 0.0, curtime = 0.0;
291         double newtime;
292         if(sys_usenoclockbutbenchmark.integer)
293         {
294                 double old_benchmark_time = benchmark_time;
295                 benchmark_time += 1;
296                 if(benchmark_time == old_benchmark_time)
297                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
298                 return benchmark_time * 0.000001;
299         }
300
301         // first all the OPTIONAL timers
302
303 #if HAVE_QUERYPERFORMANCECOUNTER
304         else if (sys_usequeryperformancecounter.integer)
305         {
306                 // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
307                 // QueryPerformanceCounter
308                 // platform:
309                 // Windows 95/98/ME/NT/2000/XP
310                 // features:
311                 // very accurate (CPU cycles)
312                 // known issues:
313                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
314                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
315                 double timescale;
316                 LARGE_INTEGER PerformanceFreq;
317                 LARGE_INTEGER PerformanceCount;
318
319                 if (!QueryPerformanceFrequency (&PerformanceFreq))
320                 {
321                         Con_Printf ("No hardware timer available\n");
322                         // fall back to timeGetTime
323                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
324                         return Sys_DoubleTime();
325                 }
326                 QueryPerformanceCounter (&PerformanceCount);
327
328                 #ifdef __BORLANDC__
329                 timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
330                 newtime = ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
331                 #else
332                 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
333                 newtime = ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
334                 #endif
335         }
336 #endif
337
338 #if HAVE_CLOCKGETTIME
339         else if (sys_useclockgettime.integer)
340         {
341                 struct timespec ts;
342 #  ifdef CLOCK_MONOTONIC
343                 // linux
344                 clock_gettime(CLOCK_MONOTONIC, &ts);
345 #  else
346                 // sunos
347                 clock_gettime(CLOCK_HIGHRES, &ts);
348 #  endif
349                 newtime = (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
350         }
351 #endif
352
353         // now all the FALLBACK timers
354         else if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
355         {
356                 newtime = (double) Sys_SDL_GetTicks() / 1000.0;
357         }
358 #if HAVE_GETTIMEOFDAY
359         else
360         {
361                 struct timeval tp;
362                 gettimeofday(&tp, NULL);
363                 newtime = (double) tp.tv_sec + tp.tv_usec / 1000000.0;
364         }
365 #elif HAVE_TIMEGETTIME
366         else
367         {
368                 static int firsttimegettime = true;
369                 // timeGetTime
370                 // platform:
371                 // Windows 95/98/ME/NT/2000/XP
372                 // features:
373                 // reasonable accuracy (millisecond)
374                 // issues:
375                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
376
377                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
378                 if (firsttimegettime)
379                 {
380                         timeBeginPeriod (1);
381                         firsttimegettime = false;
382                 }
383
384                 newtime = (double) timeGetTime () / 1000.0;
385         }
386 #else
387         // fallback for using the SDL timer if no other timer is available
388         else
389         {
390                 newtime = (double) Sys_SDL_GetTicks() / 1000.0;
391                 // this calls Sys_Error() if not linking against SDL
392         }
393 #endif
394
395         if (first)
396         {
397                 first = false;
398                 oldtime = newtime;
399         }
400
401         if (newtime < oldtime)
402         {
403                 // warn if it's significant
404                 if (newtime - oldtime < -0.01)
405                         Con_Printf("Sys_DoubleTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
406         }
407         else if (newtime > oldtime + 1800)
408         {
409                 Con_Printf("Sys_DoubleTime: time stepped forward (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
410         }
411         else
412                 curtime += newtime - oldtime;
413         oldtime = newtime;
414
415         return curtime;
416 }
417
418 void Sys_Sleep(int microseconds)
419 {
420         double t = 0;
421         if(sys_usenoclockbutbenchmark.integer)
422         {
423                 if(microseconds)
424                 {
425                         double old_benchmark_time = benchmark_time;
426                         benchmark_time += microseconds;
427                         if(benchmark_time == old_benchmark_time)
428                                 Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
429                 }
430                 return;
431         }
432         if(sys_debugsleep.integer)
433         {
434                 t = Sys_DoubleTime();
435         }
436         if(sys_supportsdlgetticks && sys_usesdldelay.integer)
437         {
438                 Sys_SDL_Delay(microseconds / 1000);
439         }
440 #if HAVE_SELECT
441         else
442         {
443                 struct timeval tv;
444                 tv.tv_sec = microseconds / 1000000;
445                 tv.tv_usec = microseconds % 1000000;
446                 select(0, NULL, NULL, NULL, &tv);
447         }
448 #elif HAVE_USLEEP
449         else
450         {
451                 usleep(microseconds);
452         }
453 #elif HAVE_Sleep
454         else
455         {
456                 Sleep(microseconds / 1000);
457         }
458 #else
459         else
460         {
461                 Sys_SDL_Delay(microseconds / 1000);
462         }
463 #endif
464         if(sys_debugsleep.integer)
465         {
466                 t = Sys_DoubleTime() - t;
467                 printf("%d %d # debugsleep\n", microseconds, (unsigned int)(t * 1000000));
468         }
469 }
470
471 const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
472 {
473         const char *p = PATH;
474         const char *q;
475         if(p && name)
476         {
477                 while((q = strchr(p, ':')))
478                 {
479                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
480                         if(FS_SysFileExists(buf))
481                                 return buf;
482                         p = q + 1;
483                 }
484                 if(!q) // none found - try the last item
485                 {
486                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
487                         if(FS_SysFileExists(buf))
488                                 return buf;
489                 }
490         }
491         return name;
492 }
493
494 const char *Sys_FindExecutableName(void)
495 {
496 #if defined(WIN32)
497         return com_argv[0];
498 #else
499         static char exenamebuf[MAX_OSPATH+1];
500         ssize_t n = -1;
501 #if defined(__FreeBSD__)
502         n = readlink("/proc/curproc/file", exenamebuf, sizeof(exenamebuf)-1);
503 #elif defined(__linux__)
504         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
505 #endif
506         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
507         {
508                 exenamebuf[n] = 0;
509                 return exenamebuf;
510         }
511         if(strchr(com_argv[0], '/'))
512                 return com_argv[0]; // possibly a relative path
513         else
514                 return Sys_FindInPATH(com_argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
515 #endif
516 }
517
518 void Sys_ProvideSelfFD(void)
519 {
520         if(com_selffd != -1)
521                 return;
522         com_selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
523 }
524
525 // for x86 cpus only...  (x64 has SSE2_PRESENT)
526 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
527 // code from SDL, shortened as we can expect CPUID to work
528 static int CPUID_Features(void)
529 {
530         int features = 0;
531 # if defined(__GNUC__) && defined(__i386__)
532         __asm__ (
533 "        movl    %%ebx,%%edi\n"
534 "        xorl    %%eax,%%eax                                           \n"
535 "        incl    %%eax                                                 \n"
536 "        cpuid                       # Get family/model/stepping/features\n"
537 "        movl    %%edx,%0                                              \n"
538 "        movl    %%edi,%%ebx\n"
539         : "=m" (features)
540         :
541         : "%eax", "%ecx", "%edx", "%edi"
542         );
543 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
544         __asm {
545         xor     eax, eax
546         inc     eax
547         cpuid                       ; Get family/model/stepping/features
548         mov     features, edx
549         }
550 # else
551 #  error SSE_POSSIBLE set but no CPUID implementation
552 # endif
553         return features;
554 }
555
556 qboolean Sys_HaveSSE(void)
557 {
558         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
559         if(COM_CheckParm("-nosse"))
560                 return false;
561         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
562         if(COM_CheckParm("-forcesse") || COM_CheckParm("-forcesse2"))
563                 return true;
564         if(CPUID_Features() & (1 << 25))
565                 return true;
566         return false;
567 }
568
569 qboolean Sys_HaveSSE2(void)
570 {
571         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
572         if(COM_CheckParm("-nosse") || COM_CheckParm("-nosse2"))
573                 return false;
574         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
575         if(COM_CheckParm("-forcesse2"))
576                 return true;
577         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
578                 return true;
579         return false;
580 }
581 #endif