diff options
Diffstat (limited to 'LibOVR/Src/Kernel/OVR_Timer.cpp')
-rw-r--r-- | LibOVR/Src/Kernel/OVR_Timer.cpp | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/LibOVR/Src/Kernel/OVR_Timer.cpp b/LibOVR/Src/Kernel/OVR_Timer.cpp new file mode 100644 index 0000000..a8de47d --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Timer.cpp @@ -0,0 +1,287 @@ +/************************************************************************************ + +Filename : OVR_Timer.cpp +Content : Provides static functions for precise timing +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +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-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, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "OVR_Timer.h" +#include "OVR_Log.h" + +#if defined (OVR_OS_WIN32) +#include <windows.h> +#elif defined(OVR_OS_ANDROID) +#include <time.h> +#include <android/log.h> + +#else +#include <sys/time.h> +#endif + +namespace OVR { + +// For recorded data playback +bool Timer::useFakeSeconds = false; +double Timer::FakeSeconds = 0; + + +//------------------------------------------------------------------------ +// *** Timer - Platform Independent functions + +// Returns global high-resolution application timer in seconds. +double Timer::GetSeconds() +{ + if(useFakeSeconds) + return FakeSeconds; + + return double(Timer::GetTicksNanos()) * 0.000000001; +} + + +#ifndef OVR_OS_WIN32 + +// Unused on OSs other then Win32. +void Timer::initializeTimerSystem() +{ +} +void Timer::shutdownTimerSystem() +{ +} + +#endif + + + +//------------------------------------------------------------------------ +// *** Android Specific Timer + +#if defined(OVR_OS_ANDROID) + +UInt64 Timer::GetTicksNanos() +{ + if (useFakeSeconds) + return (UInt64) (FakeSeconds * NanosPerSecond); + + // Choreographer vsync timestamp is based on. + struct timespec tp; + const int status = clock_gettime(CLOCK_MONOTONIC, &tp); + + if (status != 0) + { + OVR_DEBUG_LOG(("clock_gettime status=%i", status )); + } + const UInt64 result = (UInt64)tp.tv_sec * (UInt64)(1000 * 1000 * 1000) + UInt64(tp.tv_nsec); + return result; +} + + +//------------------------------------------------------------------------ +// *** Win32 Specific Timer + +#elif defined (OVR_OS_WIN32) + + +// 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(); +} + +void PerformanceTimer::Shutdown() +{ + DeleteCriticalSection(&TimeCS); + timeEndPeriod(1); +} + +UInt64 PerformanceTimer::GetTimeNanos() +{ + 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(&TimeCS); + + // Get raw value and perf counter "At the same time". + mmTimeMs = timeGetTime(); + QueryPerformanceCounter(&li); + + 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)) + { + 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; + } + + LastResultNanos = resultNanos; + ::LeaveCriticalSection(&TimeCS); + + //Tom's addition, to keep precision + static UInt64 initial_time = 0; + if (!initial_time) initial_time = resultNanos; + resultNanos -= initial_time; + + + return resultNanos; +} + + +// Delegate to PerformanceTimer. +UInt64 Timer::GetTicksNanos() +{ + 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 + + +//------------------------------------------------------------------------ +// *** Standard OS Timer + +UInt64 Timer::GetTicksNanos() +{ + if (useFakeSeconds) + return (UInt64) (FakeSeconds * NanosPerSecond); + + // TODO: prefer rdtsc when available? + UInt64 result; + + // Return microseconds. + struct timeval tv; + + gettimeofday(&tv, 0); + + result = (UInt64)tv.tv_sec * 1000000; + result += tv.tv_usec; + + return result * 1000; +} + +#endif // OS-specific + + + +} // OVR + |