Просмотр исходного кода

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.
Morph 3 лет назад
Родитель
Сommit
bd09c82521
3 измененных файлов с 81 добавлено и 0 удалено
  1. 2 0
      src/common/CMakeLists.txt
  2. 56 0
      src/common/steady_clock.cpp
  3. 23 0
      src/common/steady_clock.h

+ 2 - 0
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

+ 56 - 0
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

+ 23 - 0
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