1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10
11
12 #include "globalincs/pstypes.h"
13 #include "io/timer.h"
14 #include "graphics/2d.h"
15 #include "globalincs/alphacolors.h"
16
17 #include "osapi/osapi.h" // for multi-thread macros
18
19 #include <SDL_timer.h>
20 #include <climits>
21
22 static Uint64 Timer_perf_counter_freq = 0; // perf counter frequency - number of ticks per second
23 static Uint64 Timer_base_value;
24
25 static int Timer_inited = 0;
26
27
28 #define MICROSECONDS_PER_SECOND 1000000
29 #define NANOSECONDS_PER_SECOND 1000000000
30
31 static long double Timer_to_microseconds;
32 static long double Timer_to_nanoseconds;
33
get_performance_counter()34 static uint64_t get_performance_counter()
35 {
36 Assertion(Timer_inited, "This function can only be used when the timer system is initialized!");
37
38 auto counter = SDL_GetPerformanceCounter();
39
40 return counter - Timer_base_value;
41 }
42
timer_close()43 void timer_close()
44 {
45 if ( Timer_inited ) {
46 Timer_inited = 0;
47 }
48 }
49
timer_init()50 void timer_init()
51 {
52 if ( !Timer_inited ) {
53 Timer_perf_counter_freq = SDL_GetPerformanceFrequency();
54 Timer_base_value = SDL_GetPerformanceCounter();
55 Timer_to_nanoseconds = (long double) NANOSECONDS_PER_SECOND / (long double) Timer_perf_counter_freq;
56 Timer_to_microseconds = (long double) MICROSECONDS_PER_SECOND / (long double) Timer_perf_counter_freq;
57 Timer_inited = 1;
58
59 atexit(timer_close);
60 }
61 }
62
timer_get_fixed_seconds()63 fix timer_get_fixed_seconds()
64 {
65 if (!Timer_inited) {
66 Int3(); // Make sure you call timer_init before anything that uses timer functions!
67 return 0;
68 }
69
70 auto time = timer_get_microseconds();
71 time *= 65536;
72
73 return (fix)(time / MICROSECONDS_PER_SECOND);
74 }
75
timer_get_approx_seconds()76 fix timer_get_approx_seconds()
77 {
78 return timer_get_fixed_seconds();
79 }
80
timer_get_seconds()81 int timer_get_seconds()
82 {
83 if (!Timer_inited) {
84 Int3();
85 return 0;
86 }
87
88 return (int) (timer_get_microseconds() / MICROSECONDS_PER_SECOND);
89 }
90
timer_get_milliseconds()91 int timer_get_milliseconds()
92 {
93 if (!Timer_inited) {
94 Int3(); // Make sure you call timer_init before anything that uses timer functions!
95 return 0;
96 }
97
98 return (int) (timer_get_microseconds() / 1000);
99 }
100
timer_get_microseconds()101 std::uint64_t timer_get_microseconds()
102 {
103 auto time = get_performance_counter();
104
105 return (uint64_t) (time * Timer_to_microseconds);
106 }
107
timer_get_nanoseconds()108 std::uint64_t timer_get_nanoseconds()
109 {
110 auto time = get_performance_counter();
111
112 return (uint64_t) (time * Timer_to_nanoseconds);
113 }
114
115 // 0 means invalid,
116 // 1 means always return true
117 // 2 and above actually check the time
118 std::uint64_t timestamp_ticker = 2;
119
timestamp_reset()120 void timestamp_reset()
121 {
122 timestamp_ticker = 2;
123 }
124
125 // Restrict all time values between 0 and MAX_TIME
126 // so we don't have to use UINTs to calculate rollover.
127 // For debugging & testing, you could set this to
128 // something like 1 minute (6000).
129 extern const std::uint32_t MAX_TIME = INT_MAX / 2;
130
timestamp_ms()131 static int timestamp_ms() {
132 if (timestamp_ticker <= 2) {
133 // These are special values, don't adjust them
134 return (int)timestamp_ticker;
135 }
136 return (int)(timestamp_ticker / 1000);
137 }
138
timestamp_inc(fix frametime)139 void timestamp_inc(fix frametime)
140 {
141 // Compute the microseconds, assumes that a fix uses the lower 16 bit for storing the fractional part
142 auto delta = (std::uint64_t)frametime;
143 delta = delta * (MICROSECONDS_PER_SECOND / 65536);
144
145 timestamp_ticker += delta;
146
147 if ( timestamp_ms() > (int)MAX_TIME ) {
148 timestamp_ticker = 2; // Roll!
149 }
150
151 if (timestamp_ticker < 2 ) {
152 mprintf(("Whoa!!! timestamp_ticker < 2 -- resetting to 2!!!\n"));
153 timestamp_ticker = 2;
154 }
155 }
156
timestamp(int delta_ms)157 int timestamp(int delta_ms ) {
158 int t2;
159 if (delta_ms < 0 ) return 0;
160 if (delta_ms == 0 ) return 1;
161 t2 = timestamp_ms() + delta_ms;
162 if ( t2 > (int)MAX_TIME ) {
163 // wrap!!!
164 t2 = delta_ms - (MAX_TIME-timestamp_ms());
165 }
166 if (t2 < 2 ) t2 = 2; // hack??
167 return t2;
168 }
169
170 // Returns milliseconds until timestamp will elapse.
171 // Negative value gives milliseconds ago that timestamp elapsed.
timestamp_until(int stamp)172 int timestamp_until(int stamp) {
173 // JAS: FIX
174 // HACK!! This doesn't handle rollover!
175 // (Will it ever happen?)
176
177 return stamp - timestamp_ms();
178
179 /*
180 uint delta;
181
182 delta = stamp - timestamp_ticker;
183
184
185 if (delta > UINT_MAX/2)
186 delta = UINT_MAX - delta + 1;
187 else if (delta < - ( (int) (UINT_MAX/2)))
188 delta = UINT_MAX + delta + 1;
189
190 return delta;
191 */
192 }
193
194 // alternate timestamp functions. The way these work is you call xtimestamp() to get the
195 // current counter value, and then call
timestamp()196 int timestamp() {
197 return timestamp_ms();
198 }
199
timestamp_has_time_elapsed(int stamp,int time)200 int timestamp_has_time_elapsed(int stamp, int time) {
201 int t;
202
203 if (time <= 0)
204 return 1;
205
206 t = stamp + time;
207 if (t <= timestamp_ms())
208 return 1; // if we are unlucky enough to have it wrap on us, this will assume time has elapsed.
209
210 return 0;
211 }
timestamp_elapsed(int stamp)212 bool timestamp_elapsed(int stamp) {
213 if (stamp == 0) {
214 return false;
215 }
216
217 return timestamp_ms() >= stamp;
218 }
timestamp_elapsed_safe(int a,int b)219 bool timestamp_elapsed_safe(int a, int b) {
220 if (a == 0) {
221 return true;
222 }
223
224 return timestamp_ms() >= a || timestamp_ms() < (a - b + 100);
225 }
timestamp_set_value(int value)226 void timestamp_set_value(int value) {
227 timestamp_ticker = (std::uint64_t) value * 1000;
228 }
229
230