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