1 | #pragma once |
2 | /* |
3 | sokol_time.h -- simple cross-platform time measurement |
4 | |
5 | Do this: |
6 | #define SOKOL_IMPL |
7 | before you include this file in *one* C or C++ file to create the |
8 | implementation. |
9 | |
10 | Optionally provide the following defines with your own implementations: |
11 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) |
12 | SOKOL_API_DECL - public function declaration prefix (default: extern) |
13 | SOKOL_API_IMPL - public function implementation prefix (default: -) |
14 | |
15 | void stm_setup(); |
16 | Call once before any other functions to initialize sokol_time |
17 | (this calls for instance QueryPerformanceFrequency on Windows) |
18 | |
19 | uint64_t stm_now(); |
20 | Get current point in time in unspecified 'ticks'. The value that |
21 | is returned has no relation to the 'wall-clock' time and is |
22 | not in a specific time unit, it is only useful to compute |
23 | time differences. |
24 | |
25 | uint64_t stm_diff(uint64_t new, uint64_t old); |
26 | Computes the time difference between new and old. This will always |
27 | return a positive, non-zero value. |
28 | |
29 | uint64_t stm_since(uint64_t start); |
30 | Takes the current time, and returns the elapsed time since start |
31 | (this is a shortcut for "stm_diff(stm_now(), start)") |
32 | |
33 | uint64_t stm_laptime(uint64_t* last_time); |
34 | This is useful for measuring frame time and other recurring |
35 | events. It takes the current time, returns the time difference |
36 | to the value in last_time, and stores the current time in |
37 | last_time for the next call. If the value in last_time is 0, |
38 | the return value will be zero (this usually happens on the |
39 | very first call). |
40 | |
41 | Use the following functions to convert a duration in ticks into |
42 | useful time units: |
43 | |
44 | double stm_sec(uint64_t ticks); |
45 | double stm_ms(uint64_t ticks); |
46 | double stm_us(uint64_t ticks); |
47 | double stm_ns(uint64_t ticks); |
48 | Converts a tick value into seconds, milliseconds, microseconds |
49 | or nanoseconds. Note that not all platforms will have nanosecond |
50 | or even microsecond precision. |
51 | |
52 | Uses the following time measurement functions under the hood: |
53 | |
54 | Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() |
55 | MacOS/iOS: mach_absolute_time() |
56 | emscripten: clock_gettime(CLOCK_MONOTONIC) |
57 | Linux+others: clock_gettime(CLOCK_MONITONIC) |
58 | |
59 | zlib/libpng license |
60 | |
61 | Copyright (c) 2018 Andre Weissflog |
62 | |
63 | This software is provided 'as-is', without any express or implied warranty. |
64 | In no event will the authors be held liable for any damages arising from the |
65 | use of this software. |
66 | |
67 | Permission is granted to anyone to use this software for any purpose, |
68 | including commercial applications, and to alter it and redistribute it |
69 | freely, subject to the following restrictions: |
70 | |
71 | 1. The origin of this software must not be misrepresented; you must not |
72 | claim that you wrote the original software. If you use this software in a |
73 | product, an acknowledgment in the product documentation would be |
74 | appreciated but is not required. |
75 | |
76 | 2. Altered source versions must be plainly marked as such, and must not |
77 | be misrepresented as being the original software. |
78 | |
79 | 3. This notice may not be removed or altered from any source |
80 | distribution. |
81 | */ |
82 | #include <stdint.h> |
83 | |
84 | #ifndef SOKOL_API_DECL |
85 | #define SOKOL_API_DECL extern |
86 | #endif |
87 | |
88 | #ifdef __cplusplus |
89 | extern "C" { |
90 | #endif |
91 | |
92 | SOKOL_API_DECL void stm_setup(void); |
93 | SOKOL_API_DECL uint64_t stm_now(void); |
94 | SOKOL_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks); |
95 | SOKOL_API_DECL uint64_t stm_since(uint64_t start_ticks); |
96 | SOKOL_API_DECL uint64_t stm_laptime(uint64_t* last_time); |
97 | SOKOL_API_DECL double stm_sec(uint64_t ticks); |
98 | SOKOL_API_DECL double stm_ms(uint64_t ticks); |
99 | SOKOL_API_DECL double stm_us(uint64_t ticks); |
100 | SOKOL_API_DECL double stm_ns(uint64_t ticks); |
101 | |
102 | #ifdef __cplusplus |
103 | } /* extern "C" */ |
104 | #endif |
105 | |
106 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ |
107 | #ifdef SOKOL_IMPL |
108 | |
109 | #ifndef SOKOL_API_IMPL |
110 | #define SOKOL_API_IMPL |
111 | #endif |
112 | #ifndef SOKOL_ASSERT |
113 | #include <assert.h> |
114 | #define SOKOL_ASSERT(c) assert(c) |
115 | #endif |
116 | #ifndef _SOKOL_PRIVATE |
117 | #if defined(__GNUC__) |
118 | #define _SOKOL_PRIVATE __attribute__((unused)) static |
119 | #else |
120 | #define _SOKOL_PRIVATE static |
121 | #endif |
122 | #endif |
123 | |
124 | static int _stm_initialized; |
125 | #if defined(_WIN32) |
126 | #ifndef WIN32_LEAN_AND_MEAN |
127 | #define WIN32_LEAN_AND_MEAN |
128 | #endif |
129 | #include <windows.h> |
130 | static LARGE_INTEGER _stm_win_freq; |
131 | static LARGE_INTEGER _stm_win_start; |
132 | #elif defined(__APPLE__) && defined(__MACH__) |
133 | #include <mach/mach_time.h> |
134 | static mach_timebase_info_data_t _stm_osx_timebase; |
135 | static uint64_t _stm_osx_start; |
136 | #else /* anything else, this will need more care for non-Linux platforms */ |
137 | #include <time.h> |
138 | static uint64_t _stm_posix_start; |
139 | #endif |
140 | |
141 | /* prevent 64-bit overflow when computing relative timestamp |
142 | see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 |
143 | */ |
144 | #if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) |
145 | _SOKOL_PRIVATE int64_t int64_muldiv(int64_t value, int64_t numer, int64_t denom) { |
146 | int64_t q = value / denom; |
147 | int64_t r = value % denom; |
148 | return q * numer + r * numer / denom; |
149 | } |
150 | #endif |
151 | |
152 | |
153 | SOKOL_API_IMPL void stm_setup(void) { |
154 | SOKOL_ASSERT(0 == _stm_initialized); |
155 | _stm_initialized = 1; |
156 | #if defined(_WIN32) |
157 | QueryPerformanceFrequency(&_stm_win_freq); |
158 | QueryPerformanceCounter(&_stm_win_start); |
159 | #elif defined(__APPLE__) && defined(__MACH__) |
160 | mach_timebase_info(&_stm_osx_timebase); |
161 | _stm_osx_start = mach_absolute_time(); |
162 | #else |
163 | struct timespec ts; |
164 | clock_gettime(CLOCK_MONOTONIC, &ts); |
165 | _stm_posix_start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; |
166 | #endif |
167 | } |
168 | |
169 | SOKOL_API_IMPL uint64_t stm_now(void) { |
170 | SOKOL_ASSERT(_stm_initialized); |
171 | uint64_t now; |
172 | #if defined(_WIN32) |
173 | LARGE_INTEGER qpc_t; |
174 | QueryPerformanceCounter(&qpc_t); |
175 | now = int64_muldiv(qpc_t.QuadPart - _stm_win_start.QuadPart, 1000000000, _stm_win_freq.QuadPart); |
176 | #elif defined(__APPLE__) && defined(__MACH__) |
177 | const uint64_t mach_now = mach_absolute_time() - _stm_osx_start; |
178 | now = int64_muldiv(mach_now, _stm_osx_timebase.numer, _stm_osx_timebase.denom); |
179 | #else |
180 | struct timespec ts; |
181 | clock_gettime(CLOCK_MONOTONIC, &ts); |
182 | now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm_posix_start; |
183 | #endif |
184 | return now; |
185 | } |
186 | |
187 | SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { |
188 | if (new_ticks > old_ticks) { |
189 | return new_ticks - old_ticks; |
190 | } |
191 | else { |
192 | /* FIXME: this should be a value that converts to a non-null double */ |
193 | return 1; |
194 | } |
195 | } |
196 | |
197 | SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { |
198 | return stm_diff(stm_now(), start_ticks); |
199 | } |
200 | |
201 | SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { |
202 | SOKOL_ASSERT(last_time); |
203 | uint64_t dt = 0; |
204 | uint64_t now = stm_now(); |
205 | if (0 != *last_time) { |
206 | dt = stm_diff(now, *last_time); |
207 | } |
208 | *last_time = now; |
209 | return dt; |
210 | } |
211 | |
212 | SOKOL_API_IMPL double stm_sec(uint64_t ticks) { |
213 | return (double)ticks / 1000000000.0; |
214 | } |
215 | |
216 | SOKOL_API_IMPL double stm_ms(uint64_t ticks) { |
217 | return (double)ticks / 1000000.0; |
218 | } |
219 | |
220 | SOKOL_API_IMPL double stm_us(uint64_t ticks) { |
221 | return (double)ticks / 1000.0; |
222 | } |
223 | |
224 | SOKOL_API_IMPL double stm_ns(uint64_t ticks) { |
225 | return (double)ticks; |
226 | } |
227 | #endif /* SOKOL_IMPL */ |
228 | |
229 | |