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