diff options
Diffstat (limited to 'LibOVR/Src/Kernel/OVR_Timer.cpp')
-rw-r--r-- | LibOVR/Src/Kernel/OVR_Timer.cpp | 256 |
1 files changed, 159 insertions, 97 deletions
diff --git a/LibOVR/Src/Kernel/OVR_Timer.cpp b/LibOVR/Src/Kernel/OVR_Timer.cpp index 84ff4a1..a8de47d 100644 --- a/LibOVR/Src/Kernel/OVR_Timer.cpp +++ b/LibOVR/Src/Kernel/OVR_Timer.cpp @@ -5,16 +5,16 @@ Content : Provides static functions for precise timing Created : September 19, 2012 Notes : -Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. -Licensed under the Oculus VR SDK License Version 2.0 (the "License"); -you may not use the Oculus VR SDK except in compliance with the License, +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, which is provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. You may obtain a copy of the License at -http://www.oculusvr.com/licenses/LICENSE-2.0 +http://www.oculusvr.com/licenses/LICENSE-3.1 Unless required by applicable law or agreed to in writing, the Oculus VR SDK distributed under the License is distributed on an "AS IS" BASIS, @@ -39,49 +39,48 @@ limitations under the License. namespace OVR { +// For recorded data playback +bool Timer::useFakeSeconds = false; +double Timer::FakeSeconds = 0; + //------------------------------------------------------------------------ // *** Timer - Platform Independent functions -double ovr_GetTimeInSeconds() +// Returns global high-resolution application timer in seconds. +double Timer::GetSeconds() { - return Timer::GetSeconds(); + if(useFakeSeconds) + return FakeSeconds; + + return double(Timer::GetTicksNanos()) * 0.000000001; } -UInt64 Timer::GetProfileTicks() +#ifndef OVR_OS_WIN32 + +// Unused on OSs other then Win32. +void Timer::initializeTimerSystem() { - return (GetRawTicks() * MksPerSecond) / GetRawFrequency(); } -double Timer::GetProfileSeconds() +void Timer::shutdownTimerSystem() { - static UInt64 StartTime = GetProfileTicks(); - return TicksToSeconds(GetProfileTicks()-StartTime); } -#ifndef OVR_OS_ANDROID - -double Timer::GetSeconds() -{ - return (double)Timer::GetRawTicks() / (double) GetRawFrequency(); -} #endif + //------------------------------------------------------------------------ // *** Android Specific Timer - #if defined(OVR_OS_ANDROID) -// Returns global high-resolution application timer in seconds. -double Timer::GetSeconds() +UInt64 Timer::GetTicksNanos() { - return double(Timer::GetRawTicks()) * 0.000000001; -} + if (useFakeSeconds) + return (UInt64) (FakeSeconds * NanosPerSecond); -UInt64 Timer::GetRawTicks() -{ // Choreographer vsync timestamp is based on. struct timespec tp; const int status = clock_gettime(CLOCK_MONOTONIC, &tp); @@ -94,108 +93,178 @@ UInt64 Timer::GetRawTicks() return result; } -UInt64 Timer::GetRawFrequency() -{ - return MksPerSecond * 1000; -} - -#endif - //------------------------------------------------------------------------ // *** Win32 Specific Timer -#if defined (OVR_OS_WIN32) +#elif defined (OVR_OS_WIN32) -CRITICAL_SECTION WinAPI_GetTimeCS; -volatile UInt32 WinAPI_OldTime = 0; -volatile UInt32 WinAPI_WrapCounter = 0; +// This helper class implements high-resolution wrapper that combines timeGetTime() output +// with QueryPerformanceCounter. timeGetTime() is lower precision but drives the high bits, +// as it's tied to the system clock. +struct PerformanceTimer +{ + PerformanceTimer() + : OldMMTimeMs(0), MMTimeWrapCounter(0), PrefFrequency(0), + LastResultNanos(0), PerfMinusTicksDeltaNanos(0) + { } + + enum { + MMTimerResolutionNanos = 1000000 + }; + + void Initialize(); + void Shutdown(); + + UInt64 GetTimeNanos(); + + + UINT64 getFrequency() + { + if (PrefFrequency == 0) + { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + PrefFrequency = freq.QuadPart; + } + return PrefFrequency; + } + + + CRITICAL_SECTION TimeCS; + // timeGetTime() support with wrap. + UInt32 OldMMTimeMs; + UInt32 MMTimeWrapCounter; + // Cached performance frequency result. + UInt64 PrefFrequency; + + // Computed as (perfCounterNanos - ticksCounterNanos) initially, + // and used to adjust timing. + UInt64 PerfMinusTicksDeltaNanos; + // Last returned value in nanoseconds, to ensure we don't back-step in time. + UInt64 LastResultNanos; +}; + +PerformanceTimer Win32_PerfTimer; + + +void PerformanceTimer::Initialize() +{ + timeBeginPeriod(1); + InitializeCriticalSection(&TimeCS); + MMTimeWrapCounter = 0; + getFrequency(); +} -UInt32 Timer::GetTicksMs() +void PerformanceTimer::Shutdown() { - return timeGetTime(); + DeleteCriticalSection(&TimeCS); + timeEndPeriod(1); } -UInt64 Timer::GetTicks() +UInt64 PerformanceTimer::GetTimeNanos() { - DWORD ticks = timeGetTime(); - UInt64 result; + UInt64 resultNanos; + LARGE_INTEGER li; + DWORD mmTimeMs; // On Win32 QueryPerformanceFrequency is unreliable due to SMP and // performance levels, so use this logic to detect wrapping and track // high bits. - ::EnterCriticalSection(&WinAPI_GetTimeCS); - - if (WinAPI_OldTime > ticks) - WinAPI_WrapCounter++; - WinAPI_OldTime = ticks; + ::EnterCriticalSection(&TimeCS); - result = (UInt64(WinAPI_WrapCounter) << 32) | ticks; - ::LeaveCriticalSection(&WinAPI_GetTimeCS); - - return result * MksPerMs; -} - -UInt64 Timer::GetRawTicks() -{ - LARGE_INTEGER li; + // Get raw value and perf counter "At the same time". + mmTimeMs = timeGetTime(); QueryPerformanceCounter(&li); - return li.QuadPart; -} -UInt64 Timer::GetRawFrequency() -{ - static UInt64 perfFreq = 0; - if (perfFreq == 0) + if (OldMMTimeMs > mmTimeMs) + MMTimeWrapCounter++; + OldMMTimeMs = mmTimeMs; + + // Normalize to nanoseconds. + UInt64 mmCounterNanos = ((UInt64(MMTimeWrapCounter) << 32) | mmTimeMs) * 1000000; + UInt64 frequency = getFrequency(); + UInt64 perfCounterSeconds = UInt64(li.QuadPart) / frequency; + UInt64 perfRemainderNanos = ( (UInt64(li.QuadPart) - perfCounterSeconds * frequency) * + Timer::NanosPerSecond ) / frequency; + UInt64 perfCounterNanos = perfCounterSeconds * Timer::NanosPerSecond + perfRemainderNanos; + + if (PerfMinusTicksDeltaNanos == 0) + PerfMinusTicksDeltaNanos = perfCounterNanos - mmCounterNanos; + + + // Compute result before snapping. + // + // On first call, this evaluates to: + // resultNanos = mmCounterNanos. + // Next call, assuming no wrap: + // resultNanos = prev_mmCounterNanos + (perfCounterNanos - prev_perfCounterNanos). + // After wrap, this would be: + // resultNanos = snapped(prev_mmCounterNanos +/- 1ms) + (perfCounterNanos - prev_perfCounterNanos). + // + resultNanos = perfCounterNanos - PerfMinusTicksDeltaNanos; + + // Snap the range so that resultNanos never moves further apart then its target resolution. + // It's better to allow more slack on the high side as timeGetTime() may be updated at sporadically + // larger then 1 ms intervals even when 1 ms resolution is requested. + if (resultNanos > (mmCounterNanos + MMTimerResolutionNanos*2)) { - LARGE_INTEGER freq; - QueryPerformanceFrequency(&freq); - perfFreq = freq.QuadPart; + resultNanos = mmCounterNanos + MMTimerResolutionNanos*2; + if (resultNanos < LastResultNanos) + resultNanos = LastResultNanos; + PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos; + } + else if (resultNanos < (mmCounterNanos - MMTimerResolutionNanos)) + { + resultNanos = mmCounterNanos - MMTimerResolutionNanos; + if (resultNanos < LastResultNanos) + resultNanos = LastResultNanos; + PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos; } - return perfFreq; -} -void Timer::initializeTimerSystem() -{ - timeBeginPeriod(1); - InitializeCriticalSection(&WinAPI_GetTimeCS); + LastResultNanos = resultNanos; + ::LeaveCriticalSection(&TimeCS); -} -void Timer::shutdownTimerSystem() -{ - DeleteCriticalSection(&WinAPI_GetTimeCS); - timeEndPeriod(1); -} + //Tom's addition, to keep precision + static UInt64 initial_time = 0; + if (!initial_time) initial_time = resultNanos; + resultNanos -= initial_time; -#else // !OVR_OS_WIN32 + return resultNanos; +} -//------------------------------------------------------------------------ -// *** Standard OS Timer -UInt32 Timer::GetTicksMs() -{ - return (UInt32)(GetProfileTicks() / 1000); -} -// The profile ticks implementation is just fine for a normal timer. -UInt64 Timer::GetTicks() +// Delegate to PerformanceTimer. +UInt64 Timer::GetTicksNanos() { - return GetProfileTicks(); -} + if (useFakeSeconds) + return (UInt64) (FakeSeconds * NanosPerSecond); + return Win32_PerfTimer.GetTimeNanos(); +} void Timer::initializeTimerSystem() { + Win32_PerfTimer.Initialize(); + } void Timer::shutdownTimerSystem() { + Win32_PerfTimer.Shutdown(); } +#else // !OVR_OS_WIN32 && !OVR_OS_ANDROID + -#if !defined(OVR_OS_ANDROID) +//------------------------------------------------------------------------ +// *** Standard OS Timer -UInt64 Timer::GetRawTicks() +UInt64 Timer::GetTicksNanos() { + if (useFakeSeconds) + return (UInt64) (FakeSeconds * NanosPerSecond); + // TODO: prefer rdtsc when available? UInt64 result; @@ -207,17 +276,10 @@ UInt64 Timer::GetRawTicks() result = (UInt64)tv.tv_sec * 1000000; result += tv.tv_usec; - return result; -} - -UInt64 Timer::GetRawFrequency() -{ - return MksPerSecond; + return result * 1000; } -#endif // !OVR_OS_ANDROID - -#endif // !OVR_OS_WIN32 +#endif // OS-specific |