1 /* $Id: tenm_timer.c,v 1.65 2009/11/10 18:09:57 oohara Exp $ */
2 
3 #include <stdio.h>
4 /* exit */
5 #include <stdlib.h>
6 #include <SDL.h>
7 
8 #include "tenm_sdl_init.h"
9 
10 #include "tenm_timer.h"
11 
12 /* The problem of SDL timer is that its granularity is 10 ms, that is,
13  * if the machine doesn't have a good hardware clock, whenever SDL_Delay()
14  * returns, (SDL_GetTicks() % 10) is always same.  In this case,
15  * a simple SDL_Delay(delay - (now - past)) is not enough.
16  */
17 
18 static Uint32 tick = 0;
19 static Uint32 tick_master = 0;
20 static int number_wait = 0;
21 static int clock_granularity = 0;
22 
23 /* return 0 on success, 1 on error */
24 int
tenm_timer_init(int num_wait)25 tenm_timer_init(int num_wait)
26 {
27   int i;
28   int t;
29   Uint32 tick_begin;
30 
31   /* sanity check */
32   if (num_wait <= 0)
33   {
34     fprintf(stderr, "tenm_timer_init: strange num_wait (%d)\n", num_wait);
35     return 1;
36   }
37 
38   if (tenm_sdl_init(SDL_INIT_TIMER) != 0)
39   {
40     fprintf(stderr, "tenm_timer_init: cannot initialize SDL timer\n");
41     return 1;
42   }
43 
44   number_wait = num_wait;
45   /* jump to another 10 ms */
46   SDL_Delay(10);
47 
48   /* clock accuracy tests */
49   tick_begin = SDL_GetTicks();
50   for (i = 0; i < 10; i++)
51     SDL_Delay(1);
52   t = SDL_GetTicks() - tick_begin;
53   if (t > 0)
54     t = t / 10 + 1;
55   if (t < 1)
56     t = 1;
57   clock_granularity = t;
58 
59   tenm_timer_reset();
60   return 0;
61 }
62 
63 void
tenm_timer_reset(void)64 tenm_timer_reset(void)
65 {
66   tick_master = SDL_GetTicks();
67   tick = tick_master;
68 }
69 
70 /* return 0 on success, 1 on error */
71 int
tenm_wait_next_frame(void)72 tenm_wait_next_frame(void)
73 {
74   Uint32 tick_now;
75 
76   tick_now = SDL_GetTicks();
77   if (tick_now < tick)
78   {
79     fprintf(stderr, "tenm_wait_next_frame: SDL_GetTicks ticks backward!\n");
80     return 1;
81   }
82 
83   /* yes, this is stupid, but it offers maximum accuracy */
84   if (clock_granularity> 0)
85   {
86     while (tick_now + clock_granularity < tick + number_wait * 10)
87     {
88       SDL_Delay(1);
89       tick_now = SDL_GetTicks();
90     }
91   }
92   while (tick_now < tick + number_wait * 10)
93     tick_now = SDL_GetTicks();
94 
95   tick = SDL_GetTicks();
96 
97   return 0;
98 }
99 
100 /* return average number of frames per second since tenm_timer_init() or
101  * tenm_timer_reset() is called
102  * return -1.0 on error */
103 double
tenm_calculate_fps(int frame_passed)104 tenm_calculate_fps(int frame_passed)
105 {
106   Uint32 time_passed;
107 
108   /* sanity check */
109   if (frame_passed < 0)
110   {
111     fprintf(stderr, "tenm_calculate_fps: "
112             "negative frame_passed\n");
113     return -1.0;
114   }
115 
116   time_passed = SDL_GetTicks();
117   if (time_passed < tick_master)
118   {
119     fprintf(stderr, "tenm_calculate_fps: "
120             "SDL_GetTicks ticks backward!\n");
121     return -1.0;
122   }
123   if (time_passed == tick_master)
124   {
125     fprintf(stderr, "tenm_calculate_fps: "
126             "no time passed\n");
127     return -1.0;
128   }
129 
130   time_passed -= tick_master;
131   return 1000.0 * ((double) frame_passed) / ((double) time_passed);
132 }
133