diff options
Diffstat (limited to 'ovr_sdk_win_23.0.0/LibOVRKernel/Src/Kernel/OVR_Timer.cpp')
-rw-r--r-- | ovr_sdk_win_23.0.0/LibOVRKernel/Src/Kernel/OVR_Timer.cpp | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/ovr_sdk_win_23.0.0/LibOVRKernel/Src/Kernel/OVR_Timer.cpp b/ovr_sdk_win_23.0.0/LibOVRKernel/Src/Kernel/OVR_Timer.cpp new file mode 100644 index 0000000..142cdd0 --- /dev/null +++ b/ovr_sdk_win_23.0.0/LibOVRKernel/Src/Kernel/OVR_Timer.cpp @@ -0,0 +1,453 @@ +/************************************************************************************ + +Filename : OVR_Timer.cpp +Content : Provides static functions for precise timing +Created : September 19, 2012 +Notes : + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +Licensed under the Oculus Master SDK License Version 1.0 (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 + +https://developer.oculus.com/licenses/oculusmastersdk-1.0 + +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" + +#if defined(OVR_OS_MS) && !defined(OVR_OS_MS_MOBILE) +#include "OVR_Win32_IncludeWindows.h" + +#include <MMSystem.h> +#pragma comment(lib, "winmm.lib") +#elif defined(OVR_OS_ANDROID) +#include <android/log.h> +#include <time.h> +#else +#include <chrono> +#endif + +#if defined(OVR_BUILD_DEBUG) && defined(OVR_OS_WIN32) +typedef NTSTATUS( + NTAPI* NtQueryTimerResolutionType)(PULONG MaximumTime, PULONG MinimumTime, PULONG CurrentTime); +static NtQueryTimerResolutionType pNtQueryTimerResolution; +#endif + +#if defined(OVR_OS_MS) && !defined(OVR_OS_WIN32) // Non-desktop Microsoft platforms... + +// Add this alias here because we're not going to include OVR_CAPI.cpp +extern "C" { +double ovr_GetTimeInSeconds() { + return Timer::GetSeconds(); +} +} + +#endif + +namespace OVR { + +// For recorded data playback +bool Timer::useVirtualSeconds = false; +double Timer::VirtualSeconds = 0.0; + +//------------------------------------------------------------------------ +// *** Android Specific Timer + +#if defined( \ + OVR_OS_ANDROID) // To consider: This implementation can also work on most Linux distributions + +//------------------------------------------------------------------------ +// *** Timer - Platform Independent functions + +// Returns global high-resolution application timer in seconds. +double Timer::GetSeconds() { + if (useVirtualSeconds) + return VirtualSeconds; + + // Choreographer vsync timestamp is based on. + struct timespec tp; + const int status = clock_gettime(CLOCK_MONOTONIC, &tp); + +#ifdef OVR_BUILD_DEBUG + if (status != 0) { + OVR_DEBUG_LOG(("clock_gettime status=%i", status)); + } +#else + OVR_UNUSED(status); +#endif + + return (double)tp.tv_sec; +} + +uint64_t Timer::GetTicksNanos() { + if (useVirtualSeconds) + return (uint64_t)(VirtualSeconds * NanosPerSecond); + + // Choreographer vsync timestamp is based on. + struct timespec tp; + const int status = clock_gettime(CLOCK_MONOTONIC, &tp); + +#ifdef OVR_BUILD_DEBUG + if (status != 0) { + OVR_DEBUG_LOG(("clock_gettime status=%i", status)); + } +#else + OVR_UNUSED(status); +#endif + + const uint64_t result = + (uint64_t)tp.tv_sec * (uint64_t)(1000 * 1000 * 1000) + uint64_t(tp.tv_nsec); + return result; +} + +void Timer::initializeTimerSystem() { + // Empty for this platform. +} + +void Timer::shutdownTimerSystem() { + // Empty for this platform. +} + +//------------------------------------------------------------------------ +// *** Win32 Specific Timer + +#elif defined(OVR_OS_MS) + +// 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() + : UsingVistaOrLater(false), + TimeCS(), + OldMMTimeMs(0), + MMTimeWrapCounter(0), + PerfFrequency(0), + PerfFrequencyInverse(0), + PerfFrequencyInverseNanos(0), + PerfMinusTicksDeltaNanos(0), + LastResultNanos(0) {} + + enum { MMTimerResolutionNanos = 1000000 }; + + void Initialize(); + void Shutdown(); + + uint64_t GetTimeSeconds(); + double GetTimeSecondsDouble(); + uint64_t GetTimeNanos(); + + UINT64 getFrequency() { + if (PerfFrequency == 0) { + LARGE_INTEGER freq; + ::QueryPerformanceFrequency(&freq); + PerfFrequency = freq.QuadPart; + PerfFrequencyInverse = 1.0 / (double)PerfFrequency; + PerfFrequencyInverseNanos = 1000000000.0 / (double)PerfFrequency; + } + return PerfFrequency; + } + + double GetFrequencyInverse() { + OVR_ASSERT(PerfFrequencyInverse != 0.0); // Assert that the frequency has been initialized. + return PerfFrequencyInverse; + } + + // In Vista+ we are able to use QPC exclusively. + bool UsingVistaOrLater; + + CRITICAL_SECTION TimeCS; + // timeGetTime() support with wrap. + uint32_t OldMMTimeMs; + uint32_t MMTimeWrapCounter; + // Cached performance frequency result. + uint64_t PerfFrequency; // cycles per second, typically a large value like 3000000, but usually + // not the same as the CPU clock rate. + double PerfFrequencyInverse; // seconds per cycle (will be a small fractional value). + double PerfFrequencyInverseNanos; // nanoseconds per cycle. + + // Computed as (perfCounterNanos - ticksCounterNanos) initially, + // and used to adjust timing. + uint64_t PerfMinusTicksDeltaNanos; + // Last returned value in nanoseconds, to ensure we don't back-step in time. + uint64_t LastResultNanos; +}; + +static PerformanceTimer Win32_PerfTimer; + +void PerformanceTimer::Initialize() { + ::InitializeCriticalSection(&TimeCS); + MMTimeWrapCounter = 0; + getFrequency(); + +#if defined(OVR_OS_WIN32) // Desktop Windows only + // Set Vista flag. On Vista, we can just use QPC() without all the extra work + OSVERSIONINFOEXW ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOEXW)); + ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + ver.dwMajorVersion = 6; // Vista+ + + DWORDLONG condMask = 0; + VER_SET_CONDITION(condMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + + // VerifyVersionInfo returns true if the OS meets the conditions set above + UsingVistaOrLater = ::VerifyVersionInfoW(&ver, VER_MAJORVERSION, condMask) != 0; +#else + UsingVistaOrLater = true; +#endif + + if (!UsingVistaOrLater) { +#if defined(OVR_OS_WIN32) // Desktop Windows only + // The following has the effect of setting the NT timer resolution (NtSetTimerResolution) to 1 + // millisecond. + MMRESULT mmr = timeBeginPeriod(1); + OVR_ASSERT(TIMERR_NOERROR == mmr); + OVR_UNUSED(mmr); +#endif + +#if defined(OVR_BUILD_DEBUG) && defined(OVR_OS_WIN32) + HMODULE hNtDll = ::LoadLibraryW(L"NtDll.dll"); + if (hNtDll) { + pNtQueryTimerResolution = + (NtQueryTimerResolutionType)::GetProcAddress(hNtDll, "NtQueryTimerResolution"); + // pNtSetTimerResolution = (NtSetTimerResolutionType)::GetProcAddress(hNtDll, + // "NtSetTimerResolution"); + + if (pNtQueryTimerResolution) { + ULONG MinimumResolution; // in 100-ns units + ULONG MaximumResolution; + ULONG ActualResolution; + pNtQueryTimerResolution(&MinimumResolution, &MaximumResolution, &ActualResolution); + } + + ::FreeLibrary(hNtDll); + } +#endif + } +} + +void PerformanceTimer::Shutdown() { + ::DeleteCriticalSection(&TimeCS); + + if (!UsingVistaOrLater) { +#if defined(OVR_OS_WIN32) // Desktop Windows only + MMRESULT mmr = timeEndPeriod(1); + OVR_ASSERT(TIMERR_NOERROR == mmr); + OVR_UNUSED(mmr); +#endif + } +} + +uint64_t PerformanceTimer::GetTimeSeconds() { + if (UsingVistaOrLater) { + LARGE_INTEGER li; + ::QueryPerformanceCounter(&li); + OVR_ASSERT(PerfFrequencyInverse != 0); // Initialize should have been called earlier. + return (uint64_t)(li.QuadPart * PerfFrequencyInverse); + } + + return (uint64_t)(GetTimeNanos() * .0000000001); +} + +double PerformanceTimer::GetTimeSecondsDouble() { + if (UsingVistaOrLater) { + LARGE_INTEGER li; + ::QueryPerformanceCounter(&li); + OVR_ASSERT(PerfFrequencyInverse != 0); + return (li.QuadPart * PerfFrequencyInverse); + } + + return (GetTimeNanos() * .0000000001); +} + +uint64_t PerformanceTimer::GetTimeNanos() { + uint64_t resultNanos; + LARGE_INTEGER li; + + OVR_ASSERT(PerfFrequencyInverseNanos != 0); // Initialize should have been called earlier. + + if (UsingVistaOrLater) // Includes non-desktop platforms + { + // Then we can use QPC() directly without all that extra work + ::QueryPerformanceCounter(&li); + resultNanos = (uint64_t)(li.QuadPart * PerfFrequencyInverseNanos); + return resultNanos; + } + + // Pre-Vista computers: + // Note that the Oculus SDK does not run on PCs before Windows 7 SP1 + // so this code path should never be taken in practice. We keep it here + // since this is a nice reusable timing library that can be useful for + // other projects. + + // 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". + ::QueryPerformanceCounter(&li); + + DWORD mmTimeMs = timeGetTime(); + if (OldMMTimeMs > mmTimeMs) + MMTimeWrapCounter++; + OldMMTimeMs = mmTimeMs; + + // Normalize to nanoseconds. + uint64_t perfCounterNanos = (uint64_t)(li.QuadPart * PerfFrequencyInverseNanos); + uint64_t mmCounterNanos = ((uint64_t(MMTimeWrapCounter) << 32) | mmTimeMs) * 1000000; + 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); + + return resultNanos; +} + +//------------------------------------------------------------------------ +// *** Timer - Platform Independent functions + +// Returns global high-resolution application timer in seconds. +double Timer::GetSeconds() { + return Win32_PerfTimer.GetTimeSecondsDouble(); +} + +double Timer::GetVirtualSeconds() { + if (useVirtualSeconds) + return VirtualSeconds; + + return Win32_PerfTimer.GetTimeSecondsDouble(); +} + +// Delegate to PerformanceTimer. +uint64_t Timer::GetVirtualTicksNanos() { + if (useVirtualSeconds) + return (uint64_t)(VirtualSeconds * NanosPerSecond); + + return Win32_PerfTimer.GetTimeNanos(); +} + +uint64_t Timer::GetTicksNanos() { + return Win32_PerfTimer.GetTimeNanos(); +} + +// Windows version also provides the performance frequency inverse. +double Timer::GetPerfFrequencyInverse() { + return Win32_PerfTimer.GetFrequencyInverse(); +} + +double Timer::GetPerfFrequency() { + return double(Win32_PerfTimer.getFrequency()); +} + +void Timer::initializeTimerSystem() { + Win32_PerfTimer.Initialize(); +} +void Timer::shutdownTimerSystem() { + Win32_PerfTimer.Shutdown(); +} + +#else // C++11 standard compliant platforms + +double Timer::GetSeconds() { + if (useVirtualSeconds) + return VirtualSeconds; + + using FpSeconds = std::chrono::duration<double, std::chrono::seconds::period>; + + auto now = std::chrono::high_resolution_clock::now(); + return FpSeconds(now.time_since_epoch()).count(); +} + +double Timer::GetVirtualSeconds() { + if (useVirtualSeconds) + return VirtualSeconds; + + using FpSeconds = std::chrono::duration<double, std::chrono::seconds::period>; + + auto now = std::chrono::high_resolution_clock::now(); + return FpSeconds(now.time_since_epoch()).count(); +} + +uint64_t Timer::GetTicksNanos() { + if (useVirtualSeconds) + return (uint64_t)(VirtualSeconds * NanosPerSecond); + + using Uint64Nanoseconds = std::chrono::duration<uint64_t, std::chrono::nanoseconds::period>; + + auto now = std::chrono::high_resolution_clock::now(); + return Uint64Nanoseconds(now.time_since_epoch()).count(); +} + +void Timer::initializeTimerSystem() {} + +void Timer::shutdownTimerSystem() {} + +#endif // OS-specific + +CountdownTimer::CountdownTimer(size_t countdownTimeMs, bool start) + : CountdownTime(std::chrono::duration_cast<std::chrono::steady_clock::duration>( + std::chrono::milliseconds(countdownTimeMs))) { + if (start) + Restart(); +} + +std::chrono::steady_clock::time_point CountdownTimer::CurrentTime() const { + return std::chrono::steady_clock::now(); +} + +bool CountdownTimer::IsTimeUp() const { + return (CurrentTime() > DoneTime); +} + +void CountdownTimer::Restart() { + DoneTime = (CurrentTime() + CountdownTime); +} + +void CountdownTimer::Restart(size_t countdownTimeMs) { + CountdownTime = std::chrono::duration_cast<std::chrono::steady_clock::duration>( + std::chrono::milliseconds(countdownTimeMs)); + Restart(); +}; + +} // namespace OVR |