/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_BASE_CHRONO_STEADY_CAST_H_ #define XENIA_BASE_CHRONO_STEADY_CAST_H_ #include #include "xenia/base/chrono.h" // This is in a separate header because casting to and from steady time points // usually doesn't make sense and is imprecise. However, NT uses the FileTime // epoch as a steady clock in waits. In such cases, include this header and use // clock_cast<>(). namespace date { // This conveniently works only for Host time domain because Guest needs // additional scaling. Convert XSystemClock to WinSystemClock first if // necessary. template <> struct clock_time_conversion<::xe::chrono::WinSystemClock, std::chrono::steady_clock> { // using NtSystemClock_ = ::xe::chrono::internal::NtSystemClock; using WinSystemClock_ = ::xe::chrono::WinSystemClock; using steady_clock_ = std::chrono::steady_clock; template typename WinSystemClock_::time_point operator()( const std::chrono::time_point& t) const { // Since there is no known epoch for steady_clock and even if, since it can // progress differently than other common clocks (e.g. stopping when the // computer is suspended), we need to use now() which introduces // imprecision. // Memory fences to keep the clock fetches close together to // minimize drift. This pattern was benchmarked to give the lowest // conversion error: error = sty_tpoint - // clock_cast(clock_cast(sty_tpoint)); std::atomic_thread_fence(std::memory_order_acq_rel); auto steady_now = steady_clock_::now(); auto nt_now = WinSystemClock_::now(); std::atomic_thread_fence(std::memory_order_acq_rel); auto delta = std::chrono::floor(t - steady_now); return nt_now + delta; } }; template <> struct clock_time_conversion { using WinSystemClock_ = ::xe::chrono::WinSystemClock; using steady_clock_ = std::chrono::steady_clock; template steady_clock_::time_point operator()( const std::chrono::time_point& t) const { std::atomic_thread_fence(std::memory_order_acq_rel); auto steady_now = steady_clock_::now(); auto nt_now = WinSystemClock_::now(); std::atomic_thread_fence(std::memory_order_acq_rel); auto delta = t - nt_now; return steady_now + delta; } }; } // namespace date #endif