From bd09c825218aac9255643b9aa7c75f5ba156038c Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Wed, 1 Mar 2023 19:17:50 -0500
Subject: [PATCH] common: Implement a high resolution steady clock

This implementation provides a consistent, high performance, and high resolution clock where/when std::chrono::steady_clock does not provide sufficient precision.
---
 src/common/CMakeLists.txt   |  2 ++
 src/common/steady_clock.cpp | 56 +++++++++++++++++++++++++++++++++++++
 src/common/steady_clock.h   | 23 +++++++++++++++
 3 files changed, 81 insertions(+)
 create mode 100644 src/common/steady_clock.cpp
 create mode 100644 src/common/steady_clock.h

diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 56b247ac47..9f5d4c2653 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -113,6 +113,8 @@ add_library(common STATIC
     socket_types.h
     spin_lock.cpp
     spin_lock.h
+    steady_clock.cpp
+    steady_clock.h
     stream.cpp
     stream.h
     string_util.cpp
diff --git a/src/common/steady_clock.cpp b/src/common/steady_clock.cpp
new file mode 100644
index 0000000000..0d5908aa79
--- /dev/null
+++ b/src/common/steady_clock.cpp
@@ -0,0 +1,56 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#if defined(_WIN32)
+#include <windows.h>
+#else
+#include <time.h>
+#endif
+
+#include "common/steady_clock.h"
+
+namespace Common {
+
+#ifdef _WIN32
+static s64 WindowsQueryPerformanceFrequency() {
+    LARGE_INTEGER frequency;
+    QueryPerformanceFrequency(&frequency);
+    return frequency.QuadPart;
+}
+
+static s64 WindowsQueryPerformanceCounter() {
+    LARGE_INTEGER counter;
+    QueryPerformanceCounter(&counter);
+    return counter.QuadPart;
+}
+#endif
+
+SteadyClock::time_point SteadyClock::Now() noexcept {
+#if defined(_WIN32)
+    static const auto freq = WindowsQueryPerformanceFrequency();
+    const auto counter = WindowsQueryPerformanceCounter();
+
+    // 10 MHz is a very common QPC frequency on modern PCs.
+    // Optimizing for this specific frequency can double the performance of
+    // this function by avoiding the expensive frequency conversion path.
+    static constexpr s64 TenMHz = 10'000'000;
+
+    if (freq == TenMHz) [[likely]] {
+        static_assert(period::den % TenMHz == 0);
+        static constexpr s64 Multiplier = period::den / TenMHz;
+        return time_point{duration{counter * Multiplier}};
+    }
+
+    const auto whole = (counter / freq) * period::den;
+    const auto part = (counter % freq) * period::den / freq;
+    return time_point{duration{whole + part}};
+#elif defined(__APPLE__)
+    return time_point{duration{clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW)}};
+#else
+    timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    return time_point{std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec}};
+#endif
+}
+
+}; // namespace Common
diff --git a/src/common/steady_clock.h b/src/common/steady_clock.h
new file mode 100644
index 0000000000..9497cf865d
--- /dev/null
+++ b/src/common/steady_clock.h
@@ -0,0 +1,23 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <chrono>
+
+#include "common/common_types.h"
+
+namespace Common {
+
+struct SteadyClock {
+    using rep = s64;
+    using period = std::nano;
+    using duration = std::chrono::nanoseconds;
+    using time_point = std::chrono::time_point<SteadyClock>;
+
+    static constexpr bool is_steady = true;
+
+    [[nodiscard]] static time_point Now() noexcept;
+};
+
+} // namespace Common