ovr_sdk

view LibOVR/Src/Kernel/OVR_Timer.cpp @ 0:1b39a1b46319

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