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