[Base] Add chrono support
- WinSystemClock is a FILETIME clock without scaling, can convert to system_time - XSystemClock is a FILTETIME clock with scaling applied, can only convert to WinSystemClock
This commit is contained in:
parent
9b4168cce9
commit
23eef94984
|
@ -0,0 +1,176 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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_H_
|
||||||
|
#define XENIA_BASE_CHRONO_H_
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// https://github.com/HowardHinnant/date/commit/5ba1c1ad8514362dba596f228eb20eb13f63d948#r33275526
|
||||||
|
#define HAS_UNCAUGHT_EXCEPTIONS 1
|
||||||
|
#include "third_party/date/include/date/tz.h"
|
||||||
|
|
||||||
|
#include "xenia/base/clock.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
using hundrednano = std::ratio<1, 10000000>;
|
||||||
|
|
||||||
|
namespace chrono {
|
||||||
|
|
||||||
|
using hundrednanoseconds = std::chrono::duration<int64_t, hundrednano>;
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/sysinfo/converting-a-time-t-value-to-a-file-time
|
||||||
|
// Don't forget the 89 leap days.
|
||||||
|
static constexpr std::chrono::seconds seconds_1601_to_1970 =
|
||||||
|
(396 * 365 + 89) * std::chrono::seconds(60 * 60 * 24);
|
||||||
|
|
||||||
|
// TODO(JoelLinn) define xstead_clock xsystem_clock etc.
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
// Trick to reduce code duplication and keep all the chrono template magic
|
||||||
|
// working
|
||||||
|
enum class Domain {
|
||||||
|
// boring host clock:
|
||||||
|
Host,
|
||||||
|
// adheres to guest scaling (differrent speed, changing clock drift etc):
|
||||||
|
Guest
|
||||||
|
};
|
||||||
|
|
||||||
|
template <Domain domain_>
|
||||||
|
struct NtSystemClock {
|
||||||
|
using rep = int64_t;
|
||||||
|
using period = hundrednano;
|
||||||
|
using duration = hundrednanoseconds;
|
||||||
|
using time_point = std::chrono::time_point<NtSystemClock<domain_>>;
|
||||||
|
// This really depends on the context the clock is used in:
|
||||||
|
// static constexpr bool is_steady = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// The delta between std::chrono::system_clock (Jan 1 1970) and NT file
|
||||||
|
// time (Jan 1 1601), in seconds. In the spec std::chrono::system_clock's
|
||||||
|
// epoch is undefined, but C++20 cements it as Jan 1 1970.
|
||||||
|
static constexpr std::chrono::seconds unix_epoch_delta() {
|
||||||
|
using std::chrono::steady_clock;
|
||||||
|
auto filetime_epoch = date::year{1601} / date::month{1} / date::day{1};
|
||||||
|
auto system_clock_epoch = date::year{1970} / date::month{1} / date::day{1};
|
||||||
|
auto fp = static_cast<date::sys_seconds>(
|
||||||
|
static_cast<date::sys_days>(filetime_epoch));
|
||||||
|
auto sp = static_cast<date::sys_seconds>(
|
||||||
|
static_cast<date::sys_days>(system_clock_epoch));
|
||||||
|
return fp.time_since_epoch() - sp.time_since_epoch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr uint64_t to_file_time(time_point const& tp) noexcept {
|
||||||
|
return static_cast<uint64_t>(tp.time_since_epoch().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr time_point from_file_time(uint64_t const& tp) noexcept {
|
||||||
|
return time_point{duration{tp}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// To convert XSystemClock to sys, do clock_cast<WinSystemTime>(tp) first
|
||||||
|
// SFINAE hack https://stackoverflow.com/a/58813009
|
||||||
|
template <Domain domain_fresh_ = domain_>
|
||||||
|
static constexpr std::enable_if_t<domain_fresh_ == Domain::Host,
|
||||||
|
std::chrono::system_clock::time_point>
|
||||||
|
to_sys(const time_point& tp) {
|
||||||
|
using sys_duration = std::chrono::system_clock::duration;
|
||||||
|
using sys_time = std::chrono::system_clock::time_point;
|
||||||
|
|
||||||
|
auto dp = tp;
|
||||||
|
dp += unix_epoch_delta();
|
||||||
|
auto cdp = std::chrono::time_point_cast<sys_duration>(dp);
|
||||||
|
return sys_time{cdp.time_since_epoch()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Domain domain_fresh_ = domain_>
|
||||||
|
static constexpr std::enable_if_t<domain_fresh_ == Domain::Host, time_point>
|
||||||
|
from_sys(const std::chrono::system_clock::time_point& tp) {
|
||||||
|
auto ctp = std::chrono::time_point_cast<duration>(tp);
|
||||||
|
auto dp = time_point{ctp.time_since_epoch()};
|
||||||
|
dp -= unix_epoch_delta();
|
||||||
|
return dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static time_point now() noexcept {
|
||||||
|
if constexpr (domain_ == Domain::Host) {
|
||||||
|
// QueryHostSystemTime() returns windows epoch times even on POSIX
|
||||||
|
return from_file_time(Clock::QueryHostSystemTime());
|
||||||
|
} else if constexpr (domain_ == Domain::Guest) {
|
||||||
|
return from_file_time(Clock::QueryGuestSystemTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Unscaled system clock which can be used for filetime <-> system_clock
|
||||||
|
// conversion
|
||||||
|
using WinSystemClock = internal::NtSystemClock<internal::Domain::Host>;
|
||||||
|
|
||||||
|
// Guest system clock, scaled
|
||||||
|
using XSystemClock = internal::NtSystemClock<internal::Domain::Guest>;
|
||||||
|
|
||||||
|
} // namespace chrono
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
namespace date {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct clock_time_conversion<::xe::chrono::WinSystemClock,
|
||||||
|
::xe::chrono::XSystemClock> {
|
||||||
|
using WClock_ = ::xe::chrono::WinSystemClock;
|
||||||
|
using XClock_ = ::xe::chrono::XSystemClock;
|
||||||
|
|
||||||
|
template <typename Duration>
|
||||||
|
typename WClock_::time_point operator()(
|
||||||
|
const std::chrono::time_point<XClock_, Duration>& t) const {
|
||||||
|
// Consult chrono_steady_cast.h for explanation on this:
|
||||||
|
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||||
|
auto w_now = WClock_::now();
|
||||||
|
auto x_now = XClock_::now();
|
||||||
|
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||||
|
|
||||||
|
auto delta = (t - x_now);
|
||||||
|
if (!::cvars::clock_no_scaling) {
|
||||||
|
delta = std::chrono::floor<WClock_::duration>(
|
||||||
|
delta * xe::Clock::guest_time_scalar());
|
||||||
|
}
|
||||||
|
return w_now + delta;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct clock_time_conversion<::xe::chrono::XSystemClock,
|
||||||
|
::xe::chrono::WinSystemClock> {
|
||||||
|
using WClock_ = ::xe::chrono::WinSystemClock;
|
||||||
|
using XClock_ = ::xe::chrono::XSystemClock;
|
||||||
|
|
||||||
|
template <typename Duration>
|
||||||
|
typename XClock_::time_point operator()(
|
||||||
|
const std::chrono::time_point<WClock_, Duration>& t) const {
|
||||||
|
// Consult chrono_steady_cast.h for explanation on this:
|
||||||
|
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||||
|
auto w_now = WClock_::now();
|
||||||
|
auto x_now = XClock_::now();
|
||||||
|
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||||
|
|
||||||
|
xe::chrono::hundrednanoseconds delta = (t - w_now);
|
||||||
|
if (!::cvars::clock_no_scaling) {
|
||||||
|
delta = std::chrono::floor<WClock_::duration>(
|
||||||
|
delta / xe::Clock::guest_time_scalar());
|
||||||
|
}
|
||||||
|
return x_now + delta;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace date
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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 <atomic>
|
||||||
|
|
||||||
|
#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<domain_>;
|
||||||
|
using WinSystemClock_ = ::xe::chrono::WinSystemClock;
|
||||||
|
using steady_clock_ = std::chrono::steady_clock;
|
||||||
|
|
||||||
|
template <typename Duration>
|
||||||
|
typename WinSystemClock_::time_point operator()(
|
||||||
|
const std::chrono::time_point<steady_clock_, Duration>& 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<sty>(clock_cast<nt>(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<WinSystemClock_::duration>(t - steady_now);
|
||||||
|
return nt_now + delta;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct clock_time_conversion<std::chrono::steady_clock,
|
||||||
|
::xe::chrono::WinSystemClock> {
|
||||||
|
using WinSystemClock_ = ::xe::chrono::WinSystemClock;
|
||||||
|
using steady_clock_ = std::chrono::steady_clock;
|
||||||
|
|
||||||
|
template <typename Duration>
|
||||||
|
steady_clock_::time_point operator()(
|
||||||
|
const std::chrono::time_point<WinSystemClock_, Duration>& 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
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
#ifndef XENIA_BASE_CLOCK_H_
|
#ifndef XENIA_BASE_CLOCK_H_
|
||||||
#define XENIA_BASE_CLOCK_H_
|
#define XENIA_BASE_CLOCK_H_
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "xenia/base/cvar.h"
|
#include "xenia/base/cvar.h"
|
||||||
|
@ -24,6 +25,8 @@ DECLARE_bool(clock_source_raw);
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
||||||
|
// chrono APIs in xenia/base/chrono.h are preferred
|
||||||
|
|
||||||
class Clock {
|
class Clock {
|
||||||
public:
|
public:
|
||||||
// Host ticks-per-second. Generally QueryHostTickFrequency should be used.
|
// Host ticks-per-second. Generally QueryHostTickFrequency should be used.
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "xenia/base/atomic.h"
|
#include "xenia/base/atomic.h"
|
||||||
|
#include "xenia/base/chrono.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
|
@ -21,7 +22,6 @@
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
||||||
#include "xenia/kernel/xclock.h"
|
|
||||||
#include "xenia/kernel/xevent.h"
|
#include "xenia/kernel/xevent.h"
|
||||||
#include "xenia/kernel/xthread.h"
|
#include "xenia/kernel/xthread.h"
|
||||||
|
|
||||||
|
@ -511,7 +511,10 @@ static_assert_size(X_TIME_FIELDS, 16);
|
||||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtltimetotimefields
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtltimetotimefields
|
||||||
void RtlTimeToTimeFields_entry(lpqword_t time_ptr,
|
void RtlTimeToTimeFields_entry(lpqword_t time_ptr,
|
||||||
pointer_t<X_TIME_FIELDS> time_fields_ptr) {
|
pointer_t<X_TIME_FIELDS> time_fields_ptr) {
|
||||||
auto tp = XClock::to_sys(XClock::from_file_time(time_ptr.value()));
|
// Use host clock because we don't want scaling to be applied, just conversion
|
||||||
|
using xe::chrono::WinSystemClock;
|
||||||
|
auto tp =
|
||||||
|
WinSystemClock::to_sys(WinSystemClock::from_file_time(time_ptr.value()));
|
||||||
auto dp = date::floor<date::days>(tp);
|
auto dp = date::floor<date::days>(tp);
|
||||||
auto year_month_day = date::year_month_day{dp};
|
auto year_month_day = date::year_month_day{dp};
|
||||||
auto weekday = date::weekday{dp};
|
auto weekday = date::weekday{dp};
|
||||||
|
@ -531,6 +534,7 @@ DECLARE_XBOXKRNL_EXPORT1(RtlTimeToTimeFields, kNone, kImplemented);
|
||||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtltimefieldstotime
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtltimefieldstotime
|
||||||
dword_result_t RtlTimeFieldsToTime_entry(
|
dword_result_t RtlTimeFieldsToTime_entry(
|
||||||
pointer_t<X_TIME_FIELDS> time_fields_ptr, lpqword_t time_ptr) {
|
pointer_t<X_TIME_FIELDS> time_fields_ptr, lpqword_t time_ptr) {
|
||||||
|
using xe::chrono::WinSystemClock;
|
||||||
if (time_fields_ptr->year < 1601 || time_fields_ptr->month < 1 ||
|
if (time_fields_ptr->year < 1601 || time_fields_ptr->month < 1 ||
|
||||||
time_fields_ptr->month > 12 || time_fields_ptr->day < 1 ||
|
time_fields_ptr->month > 12 || time_fields_ptr->day < 1 ||
|
||||||
time_fields_ptr->day > 31 || time_fields_ptr->hour > 23 ||
|
time_fields_ptr->day > 31 || time_fields_ptr->hour > 23 ||
|
||||||
|
@ -551,7 +555,7 @@ dword_result_t RtlTimeFieldsToTime_entry(
|
||||||
time += std::chrono::minutes{time_fields_ptr->minute};
|
time += std::chrono::minutes{time_fields_ptr->minute};
|
||||||
time += std::chrono::seconds{time_fields_ptr->second};
|
time += std::chrono::seconds{time_fields_ptr->second};
|
||||||
time += std::chrono::milliseconds{time_fields_ptr->milliseconds};
|
time += std::chrono::milliseconds{time_fields_ptr->milliseconds};
|
||||||
*time_ptr = XClock::to_file_time(XClock::from_sys(time));
|
*time_ptr = WinSystemClock::to_file_time(WinSystemClock::from_sys(time));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT1(RtlTimeFieldsToTime, kNone, kImplemented);
|
DECLARE_XBOXKRNL_EXPORT1(RtlTimeFieldsToTime, kNone, kImplemented);
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
|
||||||
******************************************************************************
|
|
||||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
|
||||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef XENIA_KERNEL_XCLOCK_H_
|
|
||||||
#define XENIA_KERNEL_XCLOCK_H_
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
#include "xenia/base/clock.h"
|
|
||||||
|
|
||||||
// https://github.com/HowardHinnant/date/commit/5ba1c1ad8514362dba596f228eb20eb13f63d948#r33275526
|
|
||||||
#define HAS_UNCAUGHT_EXCEPTIONS 1
|
|
||||||
#include "third_party/date/include/date/date.h"
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace kernel {
|
|
||||||
|
|
||||||
struct XClock {
|
|
||||||
using rep = int64_t;
|
|
||||||
using period = std::ratio_multiply<std::ratio<100>, std::nano>;
|
|
||||||
using duration = std::chrono::duration<rep, period>;
|
|
||||||
using time_point = std::chrono::time_point<XClock>;
|
|
||||||
static constexpr bool is_steady = false;
|
|
||||||
|
|
||||||
static time_point now() noexcept {
|
|
||||||
return from_file_time(Clock::QueryGuestSystemTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t to_file_time(time_point const& tp) noexcept {
|
|
||||||
return static_cast<uint64_t>(tp.time_since_epoch().count());
|
|
||||||
}
|
|
||||||
|
|
||||||
static time_point from_file_time(uint64_t const& tp) noexcept {
|
|
||||||
return time_point{duration{tp}};
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::chrono::system_clock::time_point to_sys(time_point const& tp) {
|
|
||||||
// TODO(gibbed): verify behavior under Linux
|
|
||||||
using sys_duration = std::chrono::system_clock::duration;
|
|
||||||
using sys_time = std::chrono::system_clock::time_point;
|
|
||||||
auto dp = tp;
|
|
||||||
dp += system_clock_delta();
|
|
||||||
auto cdp = std::chrono::time_point_cast<sys_duration>(dp);
|
|
||||||
return sys_time{cdp.time_since_epoch()};
|
|
||||||
}
|
|
||||||
|
|
||||||
static time_point from_sys(std::chrono::system_clock::time_point const& tp) {
|
|
||||||
// TODO(gibbed): verify behavior under Linux
|
|
||||||
auto ctp = std::chrono::time_point_cast<duration>(tp);
|
|
||||||
auto dp = time_point{ctp.time_since_epoch()};
|
|
||||||
dp -= system_clock_delta();
|
|
||||||
return dp;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// The delta between std::chrono::system_clock (Jan 1 1970) and Xenon file
|
|
||||||
// time (Jan 1 1601), in seconds. In the spec std::chrono::system_clock's
|
|
||||||
// epoch is undefined, but C++20 cements it as Jan 1 1970.
|
|
||||||
static constexpr std::chrono::seconds system_clock_delta() {
|
|
||||||
auto filetime_epoch = date::year{1601} / date::month{1} / date::day{1};
|
|
||||||
auto system_clock_epoch = date::year{1970} / date::month{1} / date::day{1};
|
|
||||||
std::chrono::system_clock::time_point fp{
|
|
||||||
static_cast<date::sys_days>(filetime_epoch)};
|
|
||||||
std::chrono::system_clock::time_point sp{
|
|
||||||
static_cast<date::sys_days>(system_clock_epoch)};
|
|
||||||
return std::chrono::floor<std::chrono::seconds>(fp.time_since_epoch() -
|
|
||||||
sp.time_since_epoch());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace kernel
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_XCLOCK_H_
|
|
Loading…
Reference in New Issue