ovr_sdk
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/LibOVR/Src/Kernel/OVR_Timer.cpp Wed Jan 14 06:51:16 2015 +0200 1.3 @@ -0,0 +1,551 @@ 1.4 +/************************************************************************************ 1.5 + 1.6 +Filename : OVR_Timer.cpp 1.7 +Content : Provides static functions for precise timing 1.8 +Created : September 19, 2012 1.9 +Notes : 1.10 + 1.11 +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. 1.12 + 1.13 +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); 1.14 +you may not use the Oculus VR Rift SDK except in compliance with the License, 1.15 +which is provided at the time of installation or download, or which 1.16 +otherwise accompanies this software in either electronic or hard copy form. 1.17 + 1.18 +You may obtain a copy of the License at 1.19 + 1.20 +http://www.oculusvr.com/licenses/LICENSE-3.2 1.21 + 1.22 +Unless required by applicable law or agreed to in writing, the Oculus VR SDK 1.23 +distributed under the License is distributed on an "AS IS" BASIS, 1.24 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.25 +See the License for the specific language governing permissions and 1.26 +limitations under the License. 1.27 + 1.28 +************************************************************************************/ 1.29 + 1.30 +#include "OVR_Timer.h" 1.31 +#include "OVR_Log.h" 1.32 + 1.33 +#if defined(OVR_OS_MS) && !defined(OVR_OS_MS_MOBILE) 1.34 +#define WIN32_LEAN_AND_MEAN 1.35 +#include <windows.h> 1.36 +#include <MMSystem.h> 1.37 +#elif defined(OVR_OS_ANDROID) 1.38 +#include <time.h> 1.39 +#include <android/log.h> 1.40 +#elif defined(OVR_OS_MAC) 1.41 +#include <mach/mach_time.h> 1.42 +#else 1.43 +#include <time.h> 1.44 +#include <sys/time.h> 1.45 +#include <errno.h> 1.46 +#endif 1.47 + 1.48 + 1.49 +#if defined(OVR_BUILD_DEBUG) && defined(OVR_OS_WIN32) 1.50 + #ifndef NTSTATUS 1.51 + #define NTSTATUS DWORD 1.52 + #endif 1.53 + 1.54 + typedef NTSTATUS (NTAPI* NtQueryTimerResolutionType)(PULONG MaximumTime, PULONG MinimumTime, PULONG CurrentTime); 1.55 + NtQueryTimerResolutionType pNtQueryTimerResolution; 1.56 +#endif 1.57 + 1.58 + 1.59 + 1.60 +#if defined(OVR_OS_MS) && !defined(OVR_OS_WIN32) // Non-desktop Microsoft platforms... 1.61 + 1.62 +// Add this alias here because we're not going to include OVR_CAPI.cpp 1.63 +extern "C" { 1.64 + double ovr_GetTimeInSeconds() 1.65 + { 1.66 + return Timer::GetSeconds(); 1.67 + } 1.68 +} 1.69 + 1.70 +#endif 1.71 + 1.72 + 1.73 + 1.74 + 1.75 +namespace OVR { 1.76 + 1.77 +// For recorded data playback 1.78 +bool Timer::useFakeSeconds = false; 1.79 +double Timer::FakeSeconds = 0; 1.80 + 1.81 + 1.82 + 1.83 + 1.84 +//------------------------------------------------------------------------ 1.85 +// *** Android Specific Timer 1.86 + 1.87 +#if defined(OVR_OS_ANDROID) // To consider: This implementation can also work on most Linux distributions 1.88 + 1.89 +//------------------------------------------------------------------------ 1.90 +// *** Timer - Platform Independent functions 1.91 + 1.92 +// Returns global high-resolution application timer in seconds. 1.93 +double Timer::GetSeconds() 1.94 +{ 1.95 + if(useFakeSeconds) 1.96 + return FakeSeconds; 1.97 + 1.98 + // Choreographer vsync timestamp is based on. 1.99 + struct timespec tp; 1.100 + const int status = clock_gettime(CLOCK_MONOTONIC, &tp); 1.101 + 1.102 +#ifdef OVR_BUILD_DEBUG 1.103 + if (status != 0) 1.104 + { 1.105 + OVR_DEBUG_LOG(("clock_gettime status=%i", status )); 1.106 + } 1.107 +#else 1.108 + OVR_UNUSED(status); 1.109 +#endif 1.110 + 1.111 + return (double)tp.tv_sec; 1.112 +} 1.113 + 1.114 + 1.115 + 1.116 +uint64_t Timer::GetTicksNanos() 1.117 +{ 1.118 + if (useFakeSeconds) 1.119 + return (uint64_t) (FakeSeconds * NanosPerSecond); 1.120 + 1.121 + // Choreographer vsync timestamp is based on. 1.122 + struct timespec tp; 1.123 + const int status = clock_gettime(CLOCK_MONOTONIC, &tp); 1.124 + 1.125 +#ifdef OVR_BUILD_DEBUG 1.126 + if (status != 0) 1.127 + { 1.128 + OVR_DEBUG_LOG(("clock_gettime status=%i", status )); 1.129 + } 1.130 +#else 1.131 + OVR_UNUSED(status); 1.132 +#endif 1.133 + 1.134 + const uint64_t result = (uint64_t)tp.tv_sec * (uint64_t)(1000 * 1000 * 1000) + uint64_t(tp.tv_nsec); 1.135 + return result; 1.136 +} 1.137 + 1.138 + 1.139 +void Timer::initializeTimerSystem() 1.140 +{ 1.141 + // Empty for this platform. 1.142 +} 1.143 + 1.144 +void Timer::shutdownTimerSystem() 1.145 +{ 1.146 + // Empty for this platform. 1.147 +} 1.148 + 1.149 + 1.150 + 1.151 + 1.152 + 1.153 +//------------------------------------------------------------------------ 1.154 +// *** Win32 Specific Timer 1.155 + 1.156 +#elif defined (OVR_OS_MS) 1.157 + 1.158 + 1.159 +// This helper class implements high-resolution wrapper that combines timeGetTime() output 1.160 +// with QueryPerformanceCounter. timeGetTime() is lower precision but drives the high bits, 1.161 +// as it's tied to the system clock. 1.162 +struct PerformanceTimer 1.163 +{ 1.164 + PerformanceTimer() 1.165 + : UsingVistaOrLater(false), 1.166 + TimeCS(), 1.167 + OldMMTimeMs(0), 1.168 + MMTimeWrapCounter(0), 1.169 + PerfFrequency(0), 1.170 + PerfFrequencyInverse(0), 1.171 + PerfFrequencyInverseNanos(0), 1.172 + PerfMinusTicksDeltaNanos(0), 1.173 + LastResultNanos(0) 1.174 + { } 1.175 + 1.176 + enum { 1.177 + MMTimerResolutionNanos = 1000000 1.178 + }; 1.179 + 1.180 + void Initialize(); 1.181 + void Shutdown(); 1.182 + 1.183 + uint64_t GetTimeSeconds(); 1.184 + double GetTimeSecondsDouble(); 1.185 + uint64_t GetTimeNanos(); 1.186 + 1.187 + UINT64 getFrequency() 1.188 + { 1.189 + if (PerfFrequency == 0) 1.190 + { 1.191 + LARGE_INTEGER freq; 1.192 + QueryPerformanceFrequency(&freq); 1.193 + PerfFrequency = freq.QuadPart; 1.194 + PerfFrequencyInverse = 1.0 / (double)PerfFrequency; 1.195 + PerfFrequencyInverseNanos = 1000000000.0 / (double)PerfFrequency; 1.196 + } 1.197 + return PerfFrequency; 1.198 + } 1.199 + 1.200 + double GetFrequencyInverse() 1.201 + { 1.202 + OVR_ASSERT(PerfFrequencyInverse != 0.0); // Assert that the frequency has been initialized. 1.203 + return PerfFrequencyInverse; 1.204 + } 1.205 + 1.206 + bool UsingVistaOrLater; 1.207 + 1.208 + CRITICAL_SECTION TimeCS; 1.209 + // timeGetTime() support with wrap. 1.210 + uint32_t OldMMTimeMs; 1.211 + uint32_t MMTimeWrapCounter; 1.212 + // Cached performance frequency result. 1.213 + uint64_t PerfFrequency; // cycles per second, typically a large value like 3000000, but usually not the same as the CPU clock rate. 1.214 + double PerfFrequencyInverse; // seconds per cycle (will be a small fractional value). 1.215 + double PerfFrequencyInverseNanos; // nanoseconds per cycle. 1.216 + 1.217 + // Computed as (perfCounterNanos - ticksCounterNanos) initially, 1.218 + // and used to adjust timing. 1.219 + uint64_t PerfMinusTicksDeltaNanos; 1.220 + // Last returned value in nanoseconds, to ensure we don't back-step in time. 1.221 + uint64_t LastResultNanos; 1.222 +}; 1.223 + 1.224 +static PerformanceTimer Win32_PerfTimer; 1.225 + 1.226 + 1.227 +void PerformanceTimer::Initialize() 1.228 +{ 1.229 + #if defined(OVR_OS_WIN32) // Desktop Windows only 1.230 + // The following has the effect of setting the NT timer resolution (NtSetTimerResolution) to 1 millisecond. 1.231 + MMRESULT mmr = timeBeginPeriod(1); 1.232 + OVR_ASSERT(TIMERR_NOERROR == mmr); 1.233 + OVR_UNUSED(mmr); 1.234 + #endif 1.235 + 1.236 + InitializeCriticalSection(&TimeCS); 1.237 + MMTimeWrapCounter = 0; 1.238 + getFrequency(); 1.239 + 1.240 + #if defined(OVR_OS_WIN32) // Desktop Windows only 1.241 + // Set Vista flag. On Vista, we can just use QPC() without all the extra work 1.242 + OSVERSIONINFOEX ver; 1.243 + ZeroMemory(&ver, sizeof(OSVERSIONINFOEX)); 1.244 + ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 1.245 + ver.dwMajorVersion = 6; // Vista+ 1.246 + 1.247 + DWORDLONG condMask = 0; 1.248 + VER_SET_CONDITION(condMask, VER_MAJORVERSION, VER_GREATER_EQUAL); 1.249 + 1.250 + // VerifyVersionInfo returns true if the OS meets the conditions set above 1.251 + UsingVistaOrLater = VerifyVersionInfo(&ver, VER_MAJORVERSION, condMask) != 0; 1.252 + #else 1.253 + UsingVistaOrLater = true; 1.254 + #endif 1.255 + 1.256 + OVR_DEBUG_LOG(("PerformanceTimer UsingVistaOrLater = %d", (int)UsingVistaOrLater)); 1.257 + 1.258 + #if defined(OVR_BUILD_DEBUG) && defined(OVR_OS_WIN32) 1.259 + HMODULE hNtDll = LoadLibrary(L"NtDll.dll"); 1.260 + if (hNtDll) 1.261 + { 1.262 + pNtQueryTimerResolution = (NtQueryTimerResolutionType)GetProcAddress(hNtDll, "NtQueryTimerResolution"); 1.263 + //pNtSetTimerResolution = (NtSetTimerResolutionType)GetProcAddress(hNtDll, "NtSetTimerResolution"); 1.264 + 1.265 + if(pNtQueryTimerResolution) 1.266 + { 1.267 + ULONG MinimumResolution; // in 100-ns units 1.268 + ULONG MaximumResolution; 1.269 + ULONG ActualResolution; 1.270 + pNtQueryTimerResolution(&MinimumResolution, &MaximumResolution, &ActualResolution); 1.271 + OVR_DEBUG_LOG(("NtQueryTimerResolution = Min %ld us, Max %ld us, Current %ld us", MinimumResolution / 10, MaximumResolution / 10, ActualResolution / 10)); 1.272 + } 1.273 + 1.274 + FreeLibrary(hNtDll); 1.275 + } 1.276 + #endif 1.277 +} 1.278 + 1.279 +void PerformanceTimer::Shutdown() 1.280 +{ 1.281 + DeleteCriticalSection(&TimeCS); 1.282 + 1.283 + #if defined(OVR_OS_WIN32) // Desktop Windows only 1.284 + MMRESULT mmr = timeEndPeriod(1); 1.285 + OVR_ASSERT(TIMERR_NOERROR == mmr); 1.286 + OVR_UNUSED(mmr); 1.287 + #endif 1.288 +} 1.289 + 1.290 + 1.291 +uint64_t PerformanceTimer::GetTimeSeconds() 1.292 +{ 1.293 + if (UsingVistaOrLater) 1.294 + { 1.295 + LARGE_INTEGER li; 1.296 + QueryPerformanceCounter(&li); 1.297 + OVR_ASSERT(PerfFrequencyInverse != 0); // Initialize should have been called earlier. 1.298 + return (uint64_t)(li.QuadPart * PerfFrequencyInverse); 1.299 + } 1.300 + 1.301 + return (uint64_t)(GetTimeNanos() * .0000000001); 1.302 +} 1.303 + 1.304 + 1.305 +double PerformanceTimer::GetTimeSecondsDouble() 1.306 +{ 1.307 + if (UsingVistaOrLater) 1.308 + { 1.309 + LARGE_INTEGER li; 1.310 + QueryPerformanceCounter(&li); 1.311 + OVR_ASSERT(PerfFrequencyInverse != 0); 1.312 + return (li.QuadPart * PerfFrequencyInverse); 1.313 + } 1.314 + 1.315 + return (GetTimeNanos() * .0000000001); 1.316 +} 1.317 + 1.318 + 1.319 +uint64_t PerformanceTimer::GetTimeNanos() 1.320 +{ 1.321 + uint64_t resultNanos; 1.322 + LARGE_INTEGER li; 1.323 + 1.324 + OVR_ASSERT(PerfFrequencyInverseNanos != 0); // Initialize should have been called earlier. 1.325 + 1.326 + if (UsingVistaOrLater) // Includes non-desktop platforms 1.327 + { 1.328 + // Then we can use QPC() directly without all that extra work 1.329 + QueryPerformanceCounter(&li); 1.330 + resultNanos = (uint64_t)(li.QuadPart * PerfFrequencyInverseNanos); 1.331 + } 1.332 + else 1.333 + { 1.334 + // On Win32 QueryPerformanceFrequency is unreliable due to SMP and 1.335 + // performance levels, so use this logic to detect wrapping and track 1.336 + // high bits. 1.337 + ::EnterCriticalSection(&TimeCS); 1.338 + 1.339 + // Get raw value and perf counter "At the same time". 1.340 + QueryPerformanceCounter(&li); 1.341 + 1.342 + DWORD mmTimeMs = timeGetTime(); 1.343 + if (OldMMTimeMs > mmTimeMs) 1.344 + MMTimeWrapCounter++; 1.345 + OldMMTimeMs = mmTimeMs; 1.346 + 1.347 + // Normalize to nanoseconds. 1.348 + uint64_t perfCounterNanos = (uint64_t)(li.QuadPart * PerfFrequencyInverseNanos); 1.349 + uint64_t mmCounterNanos = ((uint64_t(MMTimeWrapCounter) << 32) | mmTimeMs) * 1000000; 1.350 + if (PerfMinusTicksDeltaNanos == 0) 1.351 + PerfMinusTicksDeltaNanos = perfCounterNanos - mmCounterNanos; 1.352 + 1.353 + // Compute result before snapping. 1.354 + // 1.355 + // On first call, this evaluates to: 1.356 + // resultNanos = mmCounterNanos. 1.357 + // Next call, assuming no wrap: 1.358 + // resultNanos = prev_mmCounterNanos + (perfCounterNanos - prev_perfCounterNanos). 1.359 + // After wrap, this would be: 1.360 + // resultNanos = snapped(prev_mmCounterNanos +/- 1ms) + (perfCounterNanos - prev_perfCounterNanos). 1.361 + // 1.362 + resultNanos = perfCounterNanos - PerfMinusTicksDeltaNanos; 1.363 + 1.364 + // Snap the range so that resultNanos never moves further apart then its target resolution. 1.365 + // It's better to allow more slack on the high side as timeGetTime() may be updated at sporadically 1.366 + // larger then 1 ms intervals even when 1 ms resolution is requested. 1.367 + if (resultNanos > (mmCounterNanos + MMTimerResolutionNanos*2)) 1.368 + { 1.369 + resultNanos = mmCounterNanos + MMTimerResolutionNanos*2; 1.370 + if (resultNanos < LastResultNanos) 1.371 + resultNanos = LastResultNanos; 1.372 + PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos; 1.373 + } 1.374 + else if (resultNanos < (mmCounterNanos - MMTimerResolutionNanos)) 1.375 + { 1.376 + resultNanos = mmCounterNanos - MMTimerResolutionNanos; 1.377 + if (resultNanos < LastResultNanos) 1.378 + resultNanos = LastResultNanos; 1.379 + PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos; 1.380 + } 1.381 + 1.382 + LastResultNanos = resultNanos; 1.383 + ::LeaveCriticalSection(&TimeCS); 1.384 + } 1.385 + 1.386 + //Tom's addition, to keep precision 1.387 + //static uint64_t initial_time = 0; 1.388 + //if (!initial_time) initial_time = resultNanos; 1.389 + //resultNanos -= initial_time; 1.390 + // FIXME: This cannot be used for cross-process timestamps 1.391 + 1.392 + return resultNanos; 1.393 +} 1.394 + 1.395 + 1.396 +//------------------------------------------------------------------------ 1.397 +// *** Timer - Platform Independent functions 1.398 + 1.399 +// Returns global high-resolution application timer in seconds. 1.400 +double Timer::GetSeconds() 1.401 +{ 1.402 + if(useFakeSeconds) 1.403 + return FakeSeconds; 1.404 + 1.405 + return Win32_PerfTimer.GetTimeSecondsDouble(); 1.406 +} 1.407 + 1.408 + 1.409 + 1.410 +// Delegate to PerformanceTimer. 1.411 +uint64_t Timer::GetTicksNanos() 1.412 +{ 1.413 + if (useFakeSeconds) 1.414 + return (uint64_t) (FakeSeconds * NanosPerSecond); 1.415 + 1.416 + return Win32_PerfTimer.GetTimeNanos(); 1.417 +} 1.418 +void Timer::initializeTimerSystem() 1.419 +{ 1.420 + Win32_PerfTimer.Initialize(); 1.421 +} 1.422 +void Timer::shutdownTimerSystem() 1.423 +{ 1.424 + Win32_PerfTimer.Shutdown(); 1.425 +} 1.426 + 1.427 + 1.428 + 1.429 +#elif defined(OVR_OS_MAC) 1.430 + 1.431 + 1.432 +double Timer::TimeConvertFactorNanos = 0.0; 1.433 +double Timer::TimeConvertFactorSeconds = 0.0; 1.434 + 1.435 + 1.436 +//------------------------------------------------------------------------ 1.437 +// *** Standard OS Timer 1.438 + 1.439 +// Returns global high-resolution application timer in seconds. 1.440 +double Timer::GetSeconds() 1.441 +{ 1.442 + if(useFakeSeconds) 1.443 + return FakeSeconds; 1.444 + 1.445 + OVR_ASSERT(TimeConvertFactorNanos != 0.0); 1.446 + return (double)mach_absolute_time() * TimeConvertFactorNanos; 1.447 +} 1.448 + 1.449 + 1.450 +uint64_t Timer::GetTicksNanos() 1.451 +{ 1.452 + if (useFakeSeconds) 1.453 + return (uint64_t) (FakeSeconds * NanosPerSecond); 1.454 + 1.455 + OVR_ASSERT(TimeConvertFactorSeconds != 0.0); 1.456 + return (uint64_t)(mach_absolute_time() * TimeConvertFactorSeconds); 1.457 +} 1.458 + 1.459 +void Timer::initializeTimerSystem() 1.460 +{ 1.461 + mach_timebase_info_data_t timeBase; 1.462 + mach_timebase_info(&timeBase); 1.463 + TimeConvertFactorSeconds = ((double)timeBase.numer / (double)timeBase.denom); 1.464 + TimeConvertFactorNanos = TimeConvertFactorSeconds / 1000000000.0; 1.465 +} 1.466 + 1.467 +void Timer::shutdownTimerSystem() 1.468 +{ 1.469 + // Empty for this platform. 1.470 +} 1.471 + 1.472 + 1.473 +#else // Posix platforms (e.g. Linux, BSD Unix) 1.474 + 1.475 + 1.476 +bool Timer::MonotonicClockAvailable = false; 1.477 + 1.478 + 1.479 +// Returns global high-resolution application timer in seconds. 1.480 +double Timer::GetSeconds() 1.481 +{ 1.482 + if(useFakeSeconds) 1.483 + return FakeSeconds; 1.484 + 1.485 + // http://linux/die/netman3/clock_gettime 1.486 + #if defined(CLOCK_MONOTONIC) // If we can use clock_gettime, which has nanosecond precision... 1.487 + if(MonotonicClockAvailable) 1.488 + { 1.489 + timespec ts; 1.490 + clock_gettime(CLOCK_MONOTONIC, &ts); // Better to use CLOCK_MONOTONIC than CLOCK_REALTIME. 1.491 + return static_cast<double>(ts.tv_sec) + static_cast<double>(ts.tv_nsec) / 1E9; 1.492 + } 1.493 + #endif 1.494 + 1.495 + // We cannot use rdtsc because its frequency changes at runtime. 1.496 + struct timeval tv; 1.497 + gettimeofday(&tv, 0); 1.498 + 1.499 + return static_cast<double>(tv.tv_sec) + static_cast<double>(tv.tv_usec) / 1E6; 1.500 +} 1.501 + 1.502 + 1.503 +uint64_t Timer::GetTicksNanos() 1.504 +{ 1.505 + if (useFakeSeconds) 1.506 + return (uint64_t) (FakeSeconds * NanosPerSecond); 1.507 + 1.508 + #if defined(CLOCK_MONOTONIC) // If we can use clock_gettime, which has nanosecond precision... 1.509 + if(MonotonicClockAvailable) 1.510 + { 1.511 + timespec ts; 1.512 + clock_gettime(CLOCK_MONOTONIC, &ts); 1.513 + return ((uint64_t)ts.tv_sec * 1000000000ULL) + (uint64_t)ts.tv_nsec; 1.514 + } 1.515 + #endif 1.516 + 1.517 + 1.518 + // We cannot use rdtsc because its frequency changes at runtime. 1.519 + uint64_t result; 1.520 + 1.521 + // Return microseconds. 1.522 + struct timeval tv; 1.523 + 1.524 + gettimeofday(&tv, 0); 1.525 + 1.526 + result = (uint64_t)tv.tv_sec * 1000000; 1.527 + result += tv.tv_usec; 1.528 + 1.529 + return result * 1000; 1.530 +} 1.531 + 1.532 + 1.533 +void Timer::initializeTimerSystem() 1.534 +{ 1.535 + #if defined(CLOCK_MONOTONIC) 1.536 + timespec ts; // We could also check for the availability of CLOCK_MONOTONIC with sysconf(_SC_MONOTONIC_CLOCK) 1.537 + int result = clock_gettime(CLOCK_MONOTONIC, &ts); 1.538 + MonotonicClockAvailable = (result == 0); 1.539 + #endif 1.540 +} 1.541 + 1.542 +void Timer::shutdownTimerSystem() 1.543 +{ 1.544 + // Empty for this platform. 1.545 +} 1.546 + 1.547 + 1.548 + 1.549 +#endif // OS-specific 1.550 + 1.551 + 1.552 + 1.553 +} // OVR 1.554 +