rev |
line source |
nuclear@0
|
1 /************************************************************************************
|
nuclear@0
|
2
|
nuclear@0
|
3 Filename : OVR_Timer.cpp
|
nuclear@0
|
4 Content : Provides static functions for precise timing
|
nuclear@0
|
5 Created : September 19, 2012
|
nuclear@0
|
6 Notes :
|
nuclear@0
|
7
|
nuclear@0
|
8 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
|
nuclear@0
|
9
|
nuclear@0
|
10 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
|
nuclear@0
|
11 you may not use the Oculus VR Rift SDK except in compliance with the License,
|
nuclear@0
|
12 which is provided at the time of installation or download, or which
|
nuclear@0
|
13 otherwise accompanies this software in either electronic or hard copy form.
|
nuclear@0
|
14
|
nuclear@0
|
15 You may obtain a copy of the License at
|
nuclear@0
|
16
|
nuclear@0
|
17 http://www.oculusvr.com/licenses/LICENSE-3.2
|
nuclear@0
|
18
|
nuclear@0
|
19 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
|
nuclear@0
|
20 distributed under the License is distributed on an "AS IS" BASIS,
|
nuclear@0
|
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
nuclear@0
|
22 See the License for the specific language governing permissions and
|
nuclear@0
|
23 limitations under the License.
|
nuclear@0
|
24
|
nuclear@0
|
25 ************************************************************************************/
|
nuclear@0
|
26
|
nuclear@0
|
27 #include "OVR_Timer.h"
|
nuclear@0
|
28 #include "OVR_Log.h"
|
nuclear@0
|
29
|
nuclear@0
|
30 #if defined(OVR_OS_MS) && !defined(OVR_OS_MS_MOBILE)
|
nuclear@0
|
31 #define WIN32_LEAN_AND_MEAN
|
nuclear@0
|
32 #include <windows.h>
|
nuclear@0
|
33 #include <MMSystem.h>
|
nuclear@0
|
34 #elif defined(OVR_OS_ANDROID)
|
nuclear@0
|
35 #include <time.h>
|
nuclear@0
|
36 #include <android/log.h>
|
nuclear@0
|
37 #elif defined(OVR_OS_MAC)
|
nuclear@0
|
38 #include <mach/mach_time.h>
|
nuclear@0
|
39 #else
|
nuclear@0
|
40 #include <time.h>
|
nuclear@0
|
41 #include <sys/time.h>
|
nuclear@0
|
42 #include <errno.h>
|
nuclear@0
|
43 #endif
|
nuclear@0
|
44
|
nuclear@0
|
45
|
nuclear@0
|
46 #if defined(OVR_BUILD_DEBUG) && defined(OVR_OS_WIN32)
|
nuclear@0
|
47 #ifndef NTSTATUS
|
nuclear@0
|
48 #define NTSTATUS DWORD
|
nuclear@0
|
49 #endif
|
nuclear@0
|
50
|
nuclear@0
|
51 typedef NTSTATUS (NTAPI* NtQueryTimerResolutionType)(PULONG MaximumTime, PULONG MinimumTime, PULONG CurrentTime);
|
nuclear@0
|
52 NtQueryTimerResolutionType pNtQueryTimerResolution;
|
nuclear@0
|
53 #endif
|
nuclear@0
|
54
|
nuclear@0
|
55
|
nuclear@0
|
56
|
nuclear@0
|
57 #if defined(OVR_OS_MS) && !defined(OVR_OS_WIN32) // Non-desktop Microsoft platforms...
|
nuclear@0
|
58
|
nuclear@0
|
59 // Add this alias here because we're not going to include OVR_CAPI.cpp
|
nuclear@0
|
60 extern "C" {
|
nuclear@0
|
61 double ovr_GetTimeInSeconds()
|
nuclear@0
|
62 {
|
nuclear@0
|
63 return Timer::GetSeconds();
|
nuclear@0
|
64 }
|
nuclear@0
|
65 }
|
nuclear@0
|
66
|
nuclear@0
|
67 #endif
|
nuclear@0
|
68
|
nuclear@0
|
69
|
nuclear@0
|
70
|
nuclear@0
|
71
|
nuclear@0
|
72 namespace OVR {
|
nuclear@0
|
73
|
nuclear@0
|
74 // For recorded data playback
|
nuclear@0
|
75 bool Timer::useFakeSeconds = false;
|
nuclear@0
|
76 double Timer::FakeSeconds = 0;
|
nuclear@0
|
77
|
nuclear@0
|
78
|
nuclear@0
|
79
|
nuclear@0
|
80
|
nuclear@0
|
81 //------------------------------------------------------------------------
|
nuclear@0
|
82 // *** Android Specific Timer
|
nuclear@0
|
83
|
nuclear@0
|
84 #if defined(OVR_OS_ANDROID) // To consider: This implementation can also work on most Linux distributions
|
nuclear@0
|
85
|
nuclear@0
|
86 //------------------------------------------------------------------------
|
nuclear@0
|
87 // *** Timer - Platform Independent functions
|
nuclear@0
|
88
|
nuclear@0
|
89 // Returns global high-resolution application timer in seconds.
|
nuclear@0
|
90 double Timer::GetSeconds()
|
nuclear@0
|
91 {
|
nuclear@0
|
92 if(useFakeSeconds)
|
nuclear@0
|
93 return FakeSeconds;
|
nuclear@0
|
94
|
nuclear@0
|
95 // Choreographer vsync timestamp is based on.
|
nuclear@0
|
96 struct timespec tp;
|
nuclear@0
|
97 const int status = clock_gettime(CLOCK_MONOTONIC, &tp);
|
nuclear@0
|
98
|
nuclear@0
|
99 #ifdef OVR_BUILD_DEBUG
|
nuclear@0
|
100 if (status != 0)
|
nuclear@0
|
101 {
|
nuclear@0
|
102 OVR_DEBUG_LOG(("clock_gettime status=%i", status ));
|
nuclear@0
|
103 }
|
nuclear@0
|
104 #else
|
nuclear@0
|
105 OVR_UNUSED(status);
|
nuclear@0
|
106 #endif
|
nuclear@0
|
107
|
nuclear@0
|
108 return (double)tp.tv_sec;
|
nuclear@0
|
109 }
|
nuclear@0
|
110
|
nuclear@0
|
111
|
nuclear@0
|
112
|
nuclear@0
|
113 uint64_t Timer::GetTicksNanos()
|
nuclear@0
|
114 {
|
nuclear@0
|
115 if (useFakeSeconds)
|
nuclear@0
|
116 return (uint64_t) (FakeSeconds * NanosPerSecond);
|
nuclear@0
|
117
|
nuclear@0
|
118 // Choreographer vsync timestamp is based on.
|
nuclear@0
|
119 struct timespec tp;
|
nuclear@0
|
120 const int status = clock_gettime(CLOCK_MONOTONIC, &tp);
|
nuclear@0
|
121
|
nuclear@0
|
122 #ifdef OVR_BUILD_DEBUG
|
nuclear@0
|
123 if (status != 0)
|
nuclear@0
|
124 {
|
nuclear@0
|
125 OVR_DEBUG_LOG(("clock_gettime status=%i", status ));
|
nuclear@0
|
126 }
|
nuclear@0
|
127 #else
|
nuclear@0
|
128 OVR_UNUSED(status);
|
nuclear@0
|
129 #endif
|
nuclear@0
|
130
|
nuclear@0
|
131 const uint64_t result = (uint64_t)tp.tv_sec * (uint64_t)(1000 * 1000 * 1000) + uint64_t(tp.tv_nsec);
|
nuclear@0
|
132 return result;
|
nuclear@0
|
133 }
|
nuclear@0
|
134
|
nuclear@0
|
135
|
nuclear@0
|
136 void Timer::initializeTimerSystem()
|
nuclear@0
|
137 {
|
nuclear@0
|
138 // Empty for this platform.
|
nuclear@0
|
139 }
|
nuclear@0
|
140
|
nuclear@0
|
141 void Timer::shutdownTimerSystem()
|
nuclear@0
|
142 {
|
nuclear@0
|
143 // Empty for this platform.
|
nuclear@0
|
144 }
|
nuclear@0
|
145
|
nuclear@0
|
146
|
nuclear@0
|
147
|
nuclear@0
|
148
|
nuclear@0
|
149
|
nuclear@0
|
150 //------------------------------------------------------------------------
|
nuclear@0
|
151 // *** Win32 Specific Timer
|
nuclear@0
|
152
|
nuclear@0
|
153 #elif defined (OVR_OS_MS)
|
nuclear@0
|
154
|
nuclear@0
|
155
|
nuclear@0
|
156 // This helper class implements high-resolution wrapper that combines timeGetTime() output
|
nuclear@0
|
157 // with QueryPerformanceCounter. timeGetTime() is lower precision but drives the high bits,
|
nuclear@0
|
158 // as it's tied to the system clock.
|
nuclear@0
|
159 struct PerformanceTimer
|
nuclear@0
|
160 {
|
nuclear@0
|
161 PerformanceTimer()
|
nuclear@0
|
162 : UsingVistaOrLater(false),
|
nuclear@0
|
163 TimeCS(),
|
nuclear@0
|
164 OldMMTimeMs(0),
|
nuclear@0
|
165 MMTimeWrapCounter(0),
|
nuclear@0
|
166 PerfFrequency(0),
|
nuclear@0
|
167 PerfFrequencyInverse(0),
|
nuclear@0
|
168 PerfFrequencyInverseNanos(0),
|
nuclear@0
|
169 PerfMinusTicksDeltaNanos(0),
|
nuclear@0
|
170 LastResultNanos(0)
|
nuclear@0
|
171 { }
|
nuclear@0
|
172
|
nuclear@0
|
173 enum {
|
nuclear@0
|
174 MMTimerResolutionNanos = 1000000
|
nuclear@0
|
175 };
|
nuclear@0
|
176
|
nuclear@0
|
177 void Initialize();
|
nuclear@0
|
178 void Shutdown();
|
nuclear@0
|
179
|
nuclear@0
|
180 uint64_t GetTimeSeconds();
|
nuclear@0
|
181 double GetTimeSecondsDouble();
|
nuclear@0
|
182 uint64_t GetTimeNanos();
|
nuclear@0
|
183
|
nuclear@0
|
184 UINT64 getFrequency()
|
nuclear@0
|
185 {
|
nuclear@0
|
186 if (PerfFrequency == 0)
|
nuclear@0
|
187 {
|
nuclear@0
|
188 LARGE_INTEGER freq;
|
nuclear@0
|
189 QueryPerformanceFrequency(&freq);
|
nuclear@0
|
190 PerfFrequency = freq.QuadPart;
|
nuclear@0
|
191 PerfFrequencyInverse = 1.0 / (double)PerfFrequency;
|
nuclear@0
|
192 PerfFrequencyInverseNanos = 1000000000.0 / (double)PerfFrequency;
|
nuclear@0
|
193 }
|
nuclear@0
|
194 return PerfFrequency;
|
nuclear@0
|
195 }
|
nuclear@0
|
196
|
nuclear@0
|
197 double GetFrequencyInverse()
|
nuclear@0
|
198 {
|
nuclear@0
|
199 OVR_ASSERT(PerfFrequencyInverse != 0.0); // Assert that the frequency has been initialized.
|
nuclear@0
|
200 return PerfFrequencyInverse;
|
nuclear@0
|
201 }
|
nuclear@0
|
202
|
nuclear@0
|
203 bool UsingVistaOrLater;
|
nuclear@0
|
204
|
nuclear@0
|
205 CRITICAL_SECTION TimeCS;
|
nuclear@0
|
206 // timeGetTime() support with wrap.
|
nuclear@0
|
207 uint32_t OldMMTimeMs;
|
nuclear@0
|
208 uint32_t MMTimeWrapCounter;
|
nuclear@0
|
209 // Cached performance frequency result.
|
nuclear@0
|
210 uint64_t PerfFrequency; // cycles per second, typically a large value like 3000000, but usually not the same as the CPU clock rate.
|
nuclear@0
|
211 double PerfFrequencyInverse; // seconds per cycle (will be a small fractional value).
|
nuclear@0
|
212 double PerfFrequencyInverseNanos; // nanoseconds per cycle.
|
nuclear@0
|
213
|
nuclear@0
|
214 // Computed as (perfCounterNanos - ticksCounterNanos) initially,
|
nuclear@0
|
215 // and used to adjust timing.
|
nuclear@0
|
216 uint64_t PerfMinusTicksDeltaNanos;
|
nuclear@0
|
217 // Last returned value in nanoseconds, to ensure we don't back-step in time.
|
nuclear@0
|
218 uint64_t LastResultNanos;
|
nuclear@0
|
219 };
|
nuclear@0
|
220
|
nuclear@0
|
221 static PerformanceTimer Win32_PerfTimer;
|
nuclear@0
|
222
|
nuclear@0
|
223
|
nuclear@0
|
224 void PerformanceTimer::Initialize()
|
nuclear@0
|
225 {
|
nuclear@0
|
226 #if defined(OVR_OS_WIN32) // Desktop Windows only
|
nuclear@0
|
227 // The following has the effect of setting the NT timer resolution (NtSetTimerResolution) to 1 millisecond.
|
nuclear@0
|
228 MMRESULT mmr = timeBeginPeriod(1);
|
nuclear@0
|
229 OVR_ASSERT(TIMERR_NOERROR == mmr);
|
nuclear@0
|
230 OVR_UNUSED(mmr);
|
nuclear@0
|
231 #endif
|
nuclear@0
|
232
|
nuclear@0
|
233 InitializeCriticalSection(&TimeCS);
|
nuclear@0
|
234 MMTimeWrapCounter = 0;
|
nuclear@0
|
235 getFrequency();
|
nuclear@0
|
236
|
nuclear@0
|
237 #if defined(OVR_OS_WIN32) // Desktop Windows only
|
nuclear@0
|
238 // Set Vista flag. On Vista, we can just use QPC() without all the extra work
|
nuclear@0
|
239 OSVERSIONINFOEX ver;
|
nuclear@0
|
240 ZeroMemory(&ver, sizeof(OSVERSIONINFOEX));
|
nuclear@0
|
241 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
nuclear@0
|
242 ver.dwMajorVersion = 6; // Vista+
|
nuclear@0
|
243
|
nuclear@0
|
244 DWORDLONG condMask = 0;
|
nuclear@0
|
245 VER_SET_CONDITION(condMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
nuclear@0
|
246
|
nuclear@0
|
247 // VerifyVersionInfo returns true if the OS meets the conditions set above
|
nuclear@0
|
248 UsingVistaOrLater = VerifyVersionInfo(&ver, VER_MAJORVERSION, condMask) != 0;
|
nuclear@0
|
249 #else
|
nuclear@0
|
250 UsingVistaOrLater = true;
|
nuclear@0
|
251 #endif
|
nuclear@0
|
252
|
nuclear@0
|
253 OVR_DEBUG_LOG(("PerformanceTimer UsingVistaOrLater = %d", (int)UsingVistaOrLater));
|
nuclear@0
|
254
|
nuclear@0
|
255 #if defined(OVR_BUILD_DEBUG) && defined(OVR_OS_WIN32)
|
nuclear@0
|
256 HMODULE hNtDll = LoadLibrary(L"NtDll.dll");
|
nuclear@0
|
257 if (hNtDll)
|
nuclear@0
|
258 {
|
nuclear@0
|
259 pNtQueryTimerResolution = (NtQueryTimerResolutionType)GetProcAddress(hNtDll, "NtQueryTimerResolution");
|
nuclear@0
|
260 //pNtSetTimerResolution = (NtSetTimerResolutionType)GetProcAddress(hNtDll, "NtSetTimerResolution");
|
nuclear@0
|
261
|
nuclear@0
|
262 if(pNtQueryTimerResolution)
|
nuclear@0
|
263 {
|
nuclear@0
|
264 ULONG MinimumResolution; // in 100-ns units
|
nuclear@0
|
265 ULONG MaximumResolution;
|
nuclear@0
|
266 ULONG ActualResolution;
|
nuclear@0
|
267 pNtQueryTimerResolution(&MinimumResolution, &MaximumResolution, &ActualResolution);
|
nuclear@0
|
268 OVR_DEBUG_LOG(("NtQueryTimerResolution = Min %ld us, Max %ld us, Current %ld us", MinimumResolution / 10, MaximumResolution / 10, ActualResolution / 10));
|
nuclear@0
|
269 }
|
nuclear@0
|
270
|
nuclear@0
|
271 FreeLibrary(hNtDll);
|
nuclear@0
|
272 }
|
nuclear@0
|
273 #endif
|
nuclear@0
|
274 }
|
nuclear@0
|
275
|
nuclear@0
|
276 void PerformanceTimer::Shutdown()
|
nuclear@0
|
277 {
|
nuclear@0
|
278 DeleteCriticalSection(&TimeCS);
|
nuclear@0
|
279
|
nuclear@0
|
280 #if defined(OVR_OS_WIN32) // Desktop Windows only
|
nuclear@0
|
281 MMRESULT mmr = timeEndPeriod(1);
|
nuclear@0
|
282 OVR_ASSERT(TIMERR_NOERROR == mmr);
|
nuclear@0
|
283 OVR_UNUSED(mmr);
|
nuclear@0
|
284 #endif
|
nuclear@0
|
285 }
|
nuclear@0
|
286
|
nuclear@0
|
287
|
nuclear@0
|
288 uint64_t PerformanceTimer::GetTimeSeconds()
|
nuclear@0
|
289 {
|
nuclear@0
|
290 if (UsingVistaOrLater)
|
nuclear@0
|
291 {
|
nuclear@0
|
292 LARGE_INTEGER li;
|
nuclear@0
|
293 QueryPerformanceCounter(&li);
|
nuclear@0
|
294 OVR_ASSERT(PerfFrequencyInverse != 0); // Initialize should have been called earlier.
|
nuclear@0
|
295 return (uint64_t)(li.QuadPart * PerfFrequencyInverse);
|
nuclear@0
|
296 }
|
nuclear@0
|
297
|
nuclear@0
|
298 return (uint64_t)(GetTimeNanos() * .0000000001);
|
nuclear@0
|
299 }
|
nuclear@0
|
300
|
nuclear@0
|
301
|
nuclear@0
|
302 double PerformanceTimer::GetTimeSecondsDouble()
|
nuclear@0
|
303 {
|
nuclear@0
|
304 if (UsingVistaOrLater)
|
nuclear@0
|
305 {
|
nuclear@0
|
306 LARGE_INTEGER li;
|
nuclear@0
|
307 QueryPerformanceCounter(&li);
|
nuclear@0
|
308 OVR_ASSERT(PerfFrequencyInverse != 0);
|
nuclear@0
|
309 return (li.QuadPart * PerfFrequencyInverse);
|
nuclear@0
|
310 }
|
nuclear@0
|
311
|
nuclear@0
|
312 return (GetTimeNanos() * .0000000001);
|
nuclear@0
|
313 }
|
nuclear@0
|
314
|
nuclear@0
|
315
|
nuclear@0
|
316 uint64_t PerformanceTimer::GetTimeNanos()
|
nuclear@0
|
317 {
|
nuclear@0
|
318 uint64_t resultNanos;
|
nuclear@0
|
319 LARGE_INTEGER li;
|
nuclear@0
|
320
|
nuclear@0
|
321 OVR_ASSERT(PerfFrequencyInverseNanos != 0); // Initialize should have been called earlier.
|
nuclear@0
|
322
|
nuclear@0
|
323 if (UsingVistaOrLater) // Includes non-desktop platforms
|
nuclear@0
|
324 {
|
nuclear@0
|
325 // Then we can use QPC() directly without all that extra work
|
nuclear@0
|
326 QueryPerformanceCounter(&li);
|
nuclear@0
|
327 resultNanos = (uint64_t)(li.QuadPart * PerfFrequencyInverseNanos);
|
nuclear@0
|
328 }
|
nuclear@0
|
329 else
|
nuclear@0
|
330 {
|
nuclear@0
|
331 // On Win32 QueryPerformanceFrequency is unreliable due to SMP and
|
nuclear@0
|
332 // performance levels, so use this logic to detect wrapping and track
|
nuclear@0
|
333 // high bits.
|
nuclear@0
|
334 ::EnterCriticalSection(&TimeCS);
|
nuclear@0
|
335
|
nuclear@0
|
336 // Get raw value and perf counter "At the same time".
|
nuclear@0
|
337 QueryPerformanceCounter(&li);
|
nuclear@0
|
338
|
nuclear@0
|
339 DWORD mmTimeMs = timeGetTime();
|
nuclear@0
|
340 if (OldMMTimeMs > mmTimeMs)
|
nuclear@0
|
341 MMTimeWrapCounter++;
|
nuclear@0
|
342 OldMMTimeMs = mmTimeMs;
|
nuclear@0
|
343
|
nuclear@0
|
344 // Normalize to nanoseconds.
|
nuclear@0
|
345 uint64_t perfCounterNanos = (uint64_t)(li.QuadPart * PerfFrequencyInverseNanos);
|
nuclear@0
|
346 uint64_t mmCounterNanos = ((uint64_t(MMTimeWrapCounter) << 32) | mmTimeMs) * 1000000;
|
nuclear@0
|
347 if (PerfMinusTicksDeltaNanos == 0)
|
nuclear@0
|
348 PerfMinusTicksDeltaNanos = perfCounterNanos - mmCounterNanos;
|
nuclear@0
|
349
|
nuclear@0
|
350 // Compute result before snapping.
|
nuclear@0
|
351 //
|
nuclear@0
|
352 // On first call, this evaluates to:
|
nuclear@0
|
353 // resultNanos = mmCounterNanos.
|
nuclear@0
|
354 // Next call, assuming no wrap:
|
nuclear@0
|
355 // resultNanos = prev_mmCounterNanos + (perfCounterNanos - prev_perfCounterNanos).
|
nuclear@0
|
356 // After wrap, this would be:
|
nuclear@0
|
357 // resultNanos = snapped(prev_mmCounterNanos +/- 1ms) + (perfCounterNanos - prev_perfCounterNanos).
|
nuclear@0
|
358 //
|
nuclear@0
|
359 resultNanos = perfCounterNanos - PerfMinusTicksDeltaNanos;
|
nuclear@0
|
360
|
nuclear@0
|
361 // Snap the range so that resultNanos never moves further apart then its target resolution.
|
nuclear@0
|
362 // It's better to allow more slack on the high side as timeGetTime() may be updated at sporadically
|
nuclear@0
|
363 // larger then 1 ms intervals even when 1 ms resolution is requested.
|
nuclear@0
|
364 if (resultNanos > (mmCounterNanos + MMTimerResolutionNanos*2))
|
nuclear@0
|
365 {
|
nuclear@0
|
366 resultNanos = mmCounterNanos + MMTimerResolutionNanos*2;
|
nuclear@0
|
367 if (resultNanos < LastResultNanos)
|
nuclear@0
|
368 resultNanos = LastResultNanos;
|
nuclear@0
|
369 PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos;
|
nuclear@0
|
370 }
|
nuclear@0
|
371 else if (resultNanos < (mmCounterNanos - MMTimerResolutionNanos))
|
nuclear@0
|
372 {
|
nuclear@0
|
373 resultNanos = mmCounterNanos - MMTimerResolutionNanos;
|
nuclear@0
|
374 if (resultNanos < LastResultNanos)
|
nuclear@0
|
375 resultNanos = LastResultNanos;
|
nuclear@0
|
376 PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos;
|
nuclear@0
|
377 }
|
nuclear@0
|
378
|
nuclear@0
|
379 LastResultNanos = resultNanos;
|
nuclear@0
|
380 ::LeaveCriticalSection(&TimeCS);
|
nuclear@0
|
381 }
|
nuclear@0
|
382
|
nuclear@0
|
383 //Tom's addition, to keep precision
|
nuclear@0
|
384 //static uint64_t initial_time = 0;
|
nuclear@0
|
385 //if (!initial_time) initial_time = resultNanos;
|
nuclear@0
|
386 //resultNanos -= initial_time;
|
nuclear@0
|
387 // FIXME: This cannot be used for cross-process timestamps
|
nuclear@0
|
388
|
nuclear@0
|
389 return resultNanos;
|
nuclear@0
|
390 }
|
nuclear@0
|
391
|
nuclear@0
|
392
|
nuclear@0
|
393 //------------------------------------------------------------------------
|
nuclear@0
|
394 // *** Timer - Platform Independent functions
|
nuclear@0
|
395
|
nuclear@0
|
396 // Returns global high-resolution application timer in seconds.
|
nuclear@0
|
397 double Timer::GetSeconds()
|
nuclear@0
|
398 {
|
nuclear@0
|
399 if(useFakeSeconds)
|
nuclear@0
|
400 return FakeSeconds;
|
nuclear@0
|
401
|
nuclear@0
|
402 return Win32_PerfTimer.GetTimeSecondsDouble();
|
nuclear@0
|
403 }
|
nuclear@0
|
404
|
nuclear@0
|
405
|
nuclear@0
|
406
|
nuclear@0
|
407 // Delegate to PerformanceTimer.
|
nuclear@0
|
408 uint64_t Timer::GetTicksNanos()
|
nuclear@0
|
409 {
|
nuclear@0
|
410 if (useFakeSeconds)
|
nuclear@0
|
411 return (uint64_t) (FakeSeconds * NanosPerSecond);
|
nuclear@0
|
412
|
nuclear@0
|
413 return Win32_PerfTimer.GetTimeNanos();
|
nuclear@0
|
414 }
|
nuclear@0
|
415 void Timer::initializeTimerSystem()
|
nuclear@0
|
416 {
|
nuclear@0
|
417 Win32_PerfTimer.Initialize();
|
nuclear@0
|
418 }
|
nuclear@0
|
419 void Timer::shutdownTimerSystem()
|
nuclear@0
|
420 {
|
nuclear@0
|
421 Win32_PerfTimer.Shutdown();
|
nuclear@0
|
422 }
|
nuclear@0
|
423
|
nuclear@0
|
424
|
nuclear@0
|
425
|
nuclear@0
|
426 #elif defined(OVR_OS_MAC)
|
nuclear@0
|
427
|
nuclear@0
|
428
|
nuclear@0
|
429 double Timer::TimeConvertFactorNanos = 0.0;
|
nuclear@0
|
430 double Timer::TimeConvertFactorSeconds = 0.0;
|
nuclear@0
|
431
|
nuclear@0
|
432
|
nuclear@0
|
433 //------------------------------------------------------------------------
|
nuclear@0
|
434 // *** Standard OS Timer
|
nuclear@0
|
435
|
nuclear@0
|
436 // Returns global high-resolution application timer in seconds.
|
nuclear@0
|
437 double Timer::GetSeconds()
|
nuclear@0
|
438 {
|
nuclear@0
|
439 if(useFakeSeconds)
|
nuclear@0
|
440 return FakeSeconds;
|
nuclear@0
|
441
|
nuclear@0
|
442 OVR_ASSERT(TimeConvertFactorNanos != 0.0);
|
nuclear@0
|
443 return (double)mach_absolute_time() * TimeConvertFactorNanos;
|
nuclear@0
|
444 }
|
nuclear@0
|
445
|
nuclear@0
|
446
|
nuclear@0
|
447 uint64_t Timer::GetTicksNanos()
|
nuclear@0
|
448 {
|
nuclear@0
|
449 if (useFakeSeconds)
|
nuclear@0
|
450 return (uint64_t) (FakeSeconds * NanosPerSecond);
|
nuclear@0
|
451
|
nuclear@0
|
452 OVR_ASSERT(TimeConvertFactorSeconds != 0.0);
|
nuclear@0
|
453 return (uint64_t)(mach_absolute_time() * TimeConvertFactorSeconds);
|
nuclear@0
|
454 }
|
nuclear@0
|
455
|
nuclear@0
|
456 void Timer::initializeTimerSystem()
|
nuclear@0
|
457 {
|
nuclear@0
|
458 mach_timebase_info_data_t timeBase;
|
nuclear@0
|
459 mach_timebase_info(&timeBase);
|
nuclear@0
|
460 TimeConvertFactorSeconds = ((double)timeBase.numer / (double)timeBase.denom);
|
nuclear@0
|
461 TimeConvertFactorNanos = TimeConvertFactorSeconds / 1000000000.0;
|
nuclear@0
|
462 }
|
nuclear@0
|
463
|
nuclear@0
|
464 void Timer::shutdownTimerSystem()
|
nuclear@0
|
465 {
|
nuclear@0
|
466 // Empty for this platform.
|
nuclear@0
|
467 }
|
nuclear@0
|
468
|
nuclear@0
|
469
|
nuclear@0
|
470 #else // Posix platforms (e.g. Linux, BSD Unix)
|
nuclear@0
|
471
|
nuclear@0
|
472
|
nuclear@0
|
473 bool Timer::MonotonicClockAvailable = false;
|
nuclear@0
|
474
|
nuclear@0
|
475
|
nuclear@0
|
476 // Returns global high-resolution application timer in seconds.
|
nuclear@0
|
477 double Timer::GetSeconds()
|
nuclear@0
|
478 {
|
nuclear@0
|
479 if(useFakeSeconds)
|
nuclear@0
|
480 return FakeSeconds;
|
nuclear@0
|
481
|
nuclear@0
|
482 // http://linux/die/netman3/clock_gettime
|
nuclear@0
|
483 #if defined(CLOCK_MONOTONIC) // If we can use clock_gettime, which has nanosecond precision...
|
nuclear@0
|
484 if(MonotonicClockAvailable)
|
nuclear@0
|
485 {
|
nuclear@0
|
486 timespec ts;
|
nuclear@0
|
487 clock_gettime(CLOCK_MONOTONIC, &ts); // Better to use CLOCK_MONOTONIC than CLOCK_REALTIME.
|
nuclear@0
|
488 return static_cast<double>(ts.tv_sec) + static_cast<double>(ts.tv_nsec) / 1E9;
|
nuclear@0
|
489 }
|
nuclear@0
|
490 #endif
|
nuclear@0
|
491
|
nuclear@0
|
492 // We cannot use rdtsc because its frequency changes at runtime.
|
nuclear@0
|
493 struct timeval tv;
|
nuclear@0
|
494 gettimeofday(&tv, 0);
|
nuclear@0
|
495
|
nuclear@0
|
496 return static_cast<double>(tv.tv_sec) + static_cast<double>(tv.tv_usec) / 1E6;
|
nuclear@0
|
497 }
|
nuclear@0
|
498
|
nuclear@0
|
499
|
nuclear@0
|
500 uint64_t Timer::GetTicksNanos()
|
nuclear@0
|
501 {
|
nuclear@0
|
502 if (useFakeSeconds)
|
nuclear@0
|
503 return (uint64_t) (FakeSeconds * NanosPerSecond);
|
nuclear@0
|
504
|
nuclear@0
|
505 #if defined(CLOCK_MONOTONIC) // If we can use clock_gettime, which has nanosecond precision...
|
nuclear@0
|
506 if(MonotonicClockAvailable)
|
nuclear@0
|
507 {
|
nuclear@0
|
508 timespec ts;
|
nuclear@0
|
509 clock_gettime(CLOCK_MONOTONIC, &ts);
|
nuclear@0
|
510 return ((uint64_t)ts.tv_sec * 1000000000ULL) + (uint64_t)ts.tv_nsec;
|
nuclear@0
|
511 }
|
nuclear@0
|
512 #endif
|
nuclear@0
|
513
|
nuclear@0
|
514
|
nuclear@0
|
515 // We cannot use rdtsc because its frequency changes at runtime.
|
nuclear@0
|
516 uint64_t result;
|
nuclear@0
|
517
|
nuclear@0
|
518 // Return microseconds.
|
nuclear@0
|
519 struct timeval tv;
|
nuclear@0
|
520
|
nuclear@0
|
521 gettimeofday(&tv, 0);
|
nuclear@0
|
522
|
nuclear@0
|
523 result = (uint64_t)tv.tv_sec * 1000000;
|
nuclear@0
|
524 result += tv.tv_usec;
|
nuclear@0
|
525
|
nuclear@0
|
526 return result * 1000;
|
nuclear@0
|
527 }
|
nuclear@0
|
528
|
nuclear@0
|
529
|
nuclear@0
|
530 void Timer::initializeTimerSystem()
|
nuclear@0
|
531 {
|
nuclear@0
|
532 #if defined(CLOCK_MONOTONIC)
|
nuclear@0
|
533 timespec ts; // We could also check for the availability of CLOCK_MONOTONIC with sysconf(_SC_MONOTONIC_CLOCK)
|
nuclear@0
|
534 int result = clock_gettime(CLOCK_MONOTONIC, &ts);
|
nuclear@0
|
535 MonotonicClockAvailable = (result == 0);
|
nuclear@0
|
536 #endif
|
nuclear@0
|
537 }
|
nuclear@0
|
538
|
nuclear@0
|
539 void Timer::shutdownTimerSystem()
|
nuclear@0
|
540 {
|
nuclear@0
|
541 // Empty for this platform.
|
nuclear@0
|
542 }
|
nuclear@0
|
543
|
nuclear@0
|
544
|
nuclear@0
|
545
|
nuclear@0
|
546 #endif // OS-specific
|
nuclear@0
|
547
|
nuclear@0
|
548
|
nuclear@0
|
549
|
nuclear@0
|
550 } // OVR
|
nuclear@0
|
551
|