]> icculus.org git repositories - taylor/freespace2.git/blob - src/io/timer.cpp
Partial Joystick implementation, needs joy_process in os_poll and a few more function...
[taylor/freespace2.git] / src / io / timer.cpp
1 /*
2  * $Logfile: /Freespace2/code/Io/Timer.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Include file for timer stuff
8  *
9  * $Log$
10  * Revision 1.5  2002/05/29 21:38:20  relnev
11  * fixed?
12  *
13  * Revision 1.4  2002/05/28 21:36:10  relnev
14  * some more timer junk.
15  *
16  * tried to fix software mode.
17  *
18  * Revision 1.3  2002/05/28 17:26:57  theoddone33
19  * Fill in some timer and palette setting stubs.  Still no display
20  *
21  * Revision 1.2  2002/05/07 03:16:46  theoddone33
22  * The Great Newline Fix
23  *
24  * Revision 1.1.1.1  2002/05/03 03:28:09  root
25  * Initial import.
26  *
27  * 
28  * 4     5/26/99 3:19p Dave
29  * Fixed release build errors.
30  * 
31  * 3     5/17/99 6:03p Dave
32  * Added new timing code. Put in code to allow camera speed zooming.
33  * 
34  * 2     10/07/98 10:53a Dave
35  * Initial checkin.
36  * 
37  * 1     10/07/98 10:49a Dave
38  * 
39  * 14    4/13/98 10:16a John
40  * Switched gettime back to timer_get_milliseconds, which is now thread
41  * safe.
42  * 
43  * 13    4/13/98 10:11a John
44  * Made timer functions thread safe.  Made timer_init be called in all
45  * projects.
46  * 
47  * 12    4/13/98 8:09a John
48  * Made timer_get functions thread safe.
49  * 
50  * 11    12/02/97 3:56p John
51  * fixed a bug with timestamp_until, created one with it rolling over.
52  * 
53  * 10    9/11/97 7:12p Hoffoss
54  * Added more functionality to training sexp handling code.
55  * 
56  * 9     8/29/97 4:49p Allender
57  * added mprintf to check for wrap problems with timestamp ticker
58  * 
59  * 8     8/28/97 1:38p Allender
60  * from John:  changes to timer func to detect early rollever
61  * 
62  * 7     7/29/97 5:30p Lawrance
63  * move gettime() from keyboard module to timer module
64  * 
65  * 6     7/16/97 4:42p Mike
66  * Make afterburner shake viewer, not ship.
67  * Shake for limited time.
68  * Add timestamp_until() function to timer library.
69  * 
70  * 5     2/17/97 5:18p John
71  * Added a bunch of RCS headers to a bunch of old files that don't have
72  * them.
73  *
74  * $NoKeywords: $
75  */
76
77 #ifndef PLAT_UNIX
78 #include <windows.h>
79 #endif
80
81 #include        "limits.h"
82 #include "pstypes.h"
83 #include "timer.h"
84 #include "2d.h"
85 #include "alphacolors.h"
86
87 #ifndef NDEBUG
88         #define USE_TIMING
89 #endif
90
91 static longlong Timer_last_value, Timer_base;
92 static uint Timer_freq=0;
93
94 static int Timer_inited = 0;
95
96 static CRITICAL_SECTION Timer_lock;
97
98 void timer_close()
99 {
100         if ( Timer_inited )     {
101                 Timer_inited = 0;
102 #ifdef PLAT_UNIX
103                 STUB_FUNCTION;
104 #else
105                 DeleteCriticalSection( &Timer_lock );
106 #endif
107         }
108 }
109
110 void timer_init()
111 {
112         if ( !Timer_inited )    {
113 #ifdef PLAT_UNIX
114                 SDL_InitSubSystem(SDL_INIT_TIMER);
115 #else
116                 LARGE_INTEGER tmp;
117                 QueryPerformanceFrequency(&tmp);
118                 Assert( tmp.HighPart == 0 );
119                 Timer_freq = tmp.LowPart;
120
121                 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_base);
122                 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_last_value);
123
124                 InitializeCriticalSection(&Timer_lock);
125 #endif
126                 
127                 Timer_inited = 1;
128
129                 atexit(timer_close);
130         }
131 }
132
133 // Fills Time_now with the ticks since program start
134 static void timer_get(LARGE_INTEGER * out)
135 {
136 #ifdef PLAT_UNIX
137         STUB_FUNCTION;
138 #else
139         EnterCriticalSection(&Timer_lock);
140
141         longlong time_tmp;
142         longlong Time_now;
143
144         QueryPerformanceCounter((LARGE_INTEGER *)&time_tmp);
145         if ( time_tmp < Timer_last_value )      {
146                 // The clock has rolled!
147                 Timer_base = time_tmp;
148                 mprintf(( "TIMER ROLLED!\n" ));
149                 // Hack: I'm not accounting for the time before roll occured,
150                 // since I'm not sure at what value this timer is going to roll at.
151                 Time_now = time_tmp;
152         }
153         Time_now = time_tmp - Timer_base;
154         Timer_last_value = time_tmp;
155
156         out->QuadPart = Time_now;
157
158         LeaveCriticalSection(&Timer_lock);
159 #endif
160 }
161
162 fix timer_get_fixed_seconds()
163 {
164 #ifdef PLAT_UNIX
165 //      STUB_FUNCTION;
166 //      return 0;
167
168 //      return (SDL_GetTicks() << 16) / 1000;
169         __extension__ long long a = SDL_GetTicks();
170         
171         a *= 65536;
172         return (fix)(a / 1000);
173 #else
174         int tmp;
175         LARGE_INTEGER temp_large;
176
177         if (!Timer_inited) {
178                 Int3();                                 // Make sure you call timer_init before anything that uses timer functions!
179                 return 0;
180         }
181
182         timer_get(&temp_large);
183
184         // Timing in fixed point (16.16) seconds.
185         // Can be used for up to 1000 hours
186         _asm    mov edx, temp_large.HighPart
187         _asm    mov eax, temp_large.LowPart
188
189         _asm    shld    edx, eax, 16            ; Keep 32+11 bits
190         _asm    shl     eax, 16                 
191         // edx:eax = number of 1.19Mhz pulses elapsed.
192         _asm    mov     ebx, Timer_freq
193
194         // Make sure we won't divide overflow.  Make time wrap at about 9 hours
195 sub_again:
196         _asm    sub     edx, ebx        ; subtract until negative...
197         _asm    jns     sub_again       ; ...to prevent divide overflow...
198         _asm    add     edx, ebx        ; ...then add in to get correct value.
199         _asm    div     ebx
200         //eax = fixed point seconds elapsed...
201
202         _asm mov tmp, eax
203
204         return tmp;
205 #endif
206 }
207
208 fix timer_get_fixed_secondsX()
209 {
210         return timer_get_fixed_seconds();
211 }
212
213 fix timer_get_approx_seconds()
214 {
215         return timer_get_fixed_seconds();
216 }
217
218 int timer_get_milliseconds()
219 {
220 #ifdef PLAT_UNIX
221         return SDL_GetTicks();
222 #else
223         int tmp;
224         LARGE_INTEGER temp_large;
225
226         if (!Timer_inited) {
227                 Int3();                                 // Make sure you call timer_init before anything that uses timer functions!
228                 return 0;
229         }
230
231         timer_get(&temp_large);
232
233         temp_large.QuadPart *= (longlong)1000;
234
235         // Timing in milliseconds.
236         _asm    mov edx, temp_large.HighPart
237         _asm    mov eax, temp_large.LowPart
238
239         //_asm  shld    edx, eax, 16            ; Keep 32+11 bits
240         //_asm  shl     eax, 16                 
241         // edx:eax = number of 1.19Mhz pulses elapsed.
242         _asm    mov     ebx, Timer_freq
243
244         // Make sure we won't divide overflow.  Make time wrap at about 9 hours
245 sub_again:
246         _asm    sub     edx, ebx        ; subtract until negative...
247         _asm    jns     sub_again       ; ...to prevent divide overflow...
248         _asm    add     edx, ebx        ; ...then add in to get correct value.
249         _asm    div     ebx
250         //eax = milliseconds elapsed...
251
252         _asm mov tmp, eax
253
254         return tmp;
255 #endif
256 }
257
258 int timer_get_microseconds()
259 {
260 #ifdef PLAT_UNIX
261 //      STUB_FUNCTION;
262 //      return 0;
263
264         return SDL_GetTicks() * 1000;
265 #else
266         int tmp;
267         LARGE_INTEGER temp_large;
268
269         if (!Timer_inited) {
270                 Int3();                                 // Make sure you call timer_init before anything that uses timer functions!
271                 return 0;
272         }
273
274         timer_get(&temp_large);
275
276         temp_large.QuadPart *= (longlong)1000000;
277
278         // Timing in milliseconds.
279         _asm    mov edx, temp_large.HighPart
280         _asm    mov eax, temp_large.LowPart
281
282         //_asm  shld    edx, eax, 16            ; Keep 32+11 bits
283         //_asm  shl     eax, 16                 
284         // edx:eax = number of 1.19Mhz pulses elapsed.
285         _asm    mov     ebx, Timer_freq
286
287         // Make sure we won't divide overflow.  Make time wrap at about 9 hours
288 sub_again:
289         _asm    sub     edx, ebx        ; subtract until negative...
290         _asm    jns     sub_again       ; ...to prevent divide overflow...
291         _asm    add     edx, ebx        ; ...then add in to get correct value.
292         _asm    div     ebx
293         //eax = milliseconds elapsed...
294
295         _asm mov tmp, eax
296
297         return tmp;
298 #endif
299 }
300
301 // 0 means invalid,
302 // 1 means always return true
303 // 2 and above actually check the time
304 int timestamp_ticker = 2;
305
306 void timestamp_reset()
307 {
308         timestamp_ticker = 2;
309 }
310
311 // Restrict all time values between 0 and MAX_TIME
312 // so we don't have to use UINTs to calculate rollover.
313 // For debugging & testing, you could set this to 
314 // something like 1 minute (6000).
315 #define MAX_TIME (INT_MAX/2)
316
317 void timestamp_inc(float frametime)
318 {
319         timestamp_ticker += (int)(frametime*TIMESTAMP_FREQUENCY);
320
321         if ( timestamp_ticker > MAX_TIME )      {
322                 timestamp_ticker = 2;           // Roll!
323         }
324
325         if (timestamp_ticker < 2 ) {
326                 mprintf(("Whoa!!!  timestamp_ticker < 2 -- resetting to 2!!!\n"));
327                 timestamp_ticker = 2;
328         }
329 }
330
331 int timestamp(int delta_ms )
332 {
333         int t2;
334         if (delta_ms < 0 ) return 0;
335         if (delta_ms == 0 ) return 1;
336         t2 = timestamp_ticker + delta_ms;
337         if ( t2 > MAX_TIME )    {
338                 // wrap!!!
339                 t2 = delta_ms - (MAX_TIME-timestamp_ticker);
340         }
341         if (t2 < 2 ) t2 = 2;    // hack??
342         return t2;
343 }
344
345 //      Returns milliseconds until timestamp will elapse.
346 //      Negative value gives milliseconds ago that timestamp elapsed.
347 int timestamp_until(int stamp)
348 {
349         // JAS: FIX
350         // HACK!! This doesn't handle rollover!
351         // (Will it ever happen?)
352         
353         return stamp - timestamp_ticker;
354
355 /*
356         uint    delta;
357
358         delta = stamp - timestamp_ticker;
359         
360
361         if (delta > UINT_MAX/2)
362                 delta = UINT_MAX - delta + 1;
363         else if (delta < - ( (int) (UINT_MAX/2)))
364                 delta = UINT_MAX + delta + 1;
365
366         return delta;
367 */
368 }
369
370 // alternate timestamp functions.  The way these work is you call xtimestamp() to get the
371 // current counter value, and then call
372 int timestamp()
373 {
374         return timestamp_ticker;
375 }
376
377 int timestamp_has_time_elapsed(int stamp, int time)
378 {
379         int t;
380
381         if (time <= 0)
382                 return 1;
383
384         t = stamp + time;
385         if (t <= timestamp_ticker)
386                 return 1;  // if we are unlucky enough to have it wrap on us, this will assume time has elapsed.
387
388         return 0;
389 }
390
391 // timing functions -------------------------------------------------------------------------------
392
393 #define MAX_TIMING_EVENTS               15
394
395 // timing struct
396 #ifdef USE_TIMING
397
398 typedef struct timing {
399         char name[64];
400         int microseconds_total;
401         int start;
402         int ref_count;
403 } timing;
404
405 timing Timing_frame;
406 timing Timing_events[MAX_TIMING_EVENTS];
407 int Timing_event_count = 0;
408
409 #endif
410
411 // lookup a timing event
412 int timing_event_lookup(char *event_name)
413 {
414 #ifndef USE_TIMING
415         return -1;
416 #else
417         int idx;
418
419         // sanity
420         if(event_name == NULL){
421                 return -1;
422         }
423
424         // look through all events
425         for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
426                 if(!stricmp(Timing_events[idx].name, event_name)){
427                         return idx;
428                 }
429         }
430
431         return -1;
432 #endif
433 }
434
435 // start timing frame stuff
436 void timing_frame_start()
437 {
438 #ifndef USE_TIMING
439         return;
440 #else
441         int idx;
442
443         // restart the frame
444         Timing_event_count = 0;
445         Timing_frame.start = timer_get_microseconds();
446         for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
447                 Timing_events[idx].microseconds_total = 0;
448                 strcpy(Timing_events[idx].name, "");
449                 Timing_events[idx].ref_count = 0;
450         }
451 #endif
452 }
453
454 // done timing the frame
455 void timing_frame_stop()
456 {
457 #ifndef USE_TIMING
458         return;
459 #else   
460         int stop_time;
461
462         // stop time
463         stop_time = timer_get_microseconds();
464
465         // calc times
466         Timing_frame.microseconds_total = stop_time - Timing_frame.start;       
467 #endif
468 }
469
470 // get the total frame time in microseconds
471 int timing_frame_total()
472 {
473 #ifndef USE_TIMING
474         return 0;
475 #else
476         return Timing_frame.microseconds_total;
477 #endif
478 }
479
480 // time an individual event
481 void timing_event_start(char *event_name)
482 {
483 #ifndef USE_TIMING
484         return;
485 #else
486         int event;
487
488         // sanity
489         if(event_name == NULL){
490                 return;
491         }
492
493         // try and find the event
494         event = timing_event_lookup(event_name);
495
496         // if we already have one
497         if(event != -1){
498                 Assert(Timing_events[event].ref_count == 0);
499                 Timing_events[event].start = timer_get_microseconds();
500                 Timing_events[event].ref_count++;
501         }
502         // if we need to add a new one
503         else {
504                 if(Timing_event_count < MAX_TIMING_EVENTS){
505                         strcpy(Timing_events[Timing_event_count].name, event_name);
506                         Timing_events[Timing_event_count].start = timer_get_microseconds();
507                         Timing_events[Timing_event_count++].ref_count++;
508                 }
509         }
510 #endif
511 }
512
513 // stop timing an event
514 void timing_event_stop(char *event_name)
515 {
516 #ifndef USE_TIMING
517         return;
518 #else
519         int event;
520
521         // sanity
522         if(event_name == NULL){
523                 return;
524         }
525
526         // try and find the event
527         event = timing_event_lookup(event_name);
528
529         // if we already have one
530         if(event != -1){
531                 Assert(Timing_events[event].ref_count == 1);
532                 Timing_events[event].microseconds_total += timer_get_microseconds() - Timing_events[event].start;
533                 Timing_events[event].ref_count--;
534         }
535 #endif
536 }
537
538 // get the total time for an event in microseconds
539 int timing_event_total(char *event_name)
540 {
541 #ifndef USE_TIMING
542         return -1;
543 #else
544         int event;
545
546         // sanity
547         if(event_name == NULL){
548                 return -1;
549         }
550
551         // try and find the event
552         event = timing_event_lookup(event_name);
553         if(event == -1){
554                 return -1;
555         }
556         
557         return Timing_events[event].microseconds_total;
558 #endif
559 }
560
561 // get the percentage of total frametime for the event (0.0 to 1.0)
562 float timing_event_pct(char *event_name)
563 {
564 #ifndef USE_TIMING
565         return 0.0f;
566 #else
567         int event;
568
569         // sanity
570         if(event_name == NULL){
571                 return -1.0f;
572         }
573
574         // try and find the event
575         event = timing_event_lookup(event_name);
576         if(event == -1){
577                 return -1.0f;
578         }
579
580         return (float)((double)Timing_events[event].microseconds_total / (double)Timing_frame.microseconds_total);
581 #endif
582 }
583
584 // display timing 
585 void timing_display(int x, int y)
586 {
587 #ifndef USE_TIMING
588         return;
589 #else
590         int idx;
591
592         gr_set_color_fast(&Color_bright_blue);
593
594         // total time
595         gr_printf(x, y, "Total time %f\n", (float)timing_frame_total() / 1000000.0f);
596         y += 10;
597
598         // each event percentage
599         for(idx=0; idx<Timing_event_count; idx++){
600                 gr_printf(x, y, "%s : %f\n", Timing_events[idx].name, timing_event_pct(Timing_events[idx].name));
601                 y += 10;
602         }
603 #endif
604 }
605