summaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
authorliamwhite <liamwhite@users.noreply.github.com>2023-03-07 16:54:13 +0100
committerGitHub <noreply@github.com>2023-03-07 16:54:13 +0100
commita7792e5ff83523142230951ac7eacbd7685dc40b (patch)
treea7531c65e2ddec1122fc071d44c8106c48352ce3 /src/common
parentMerge pull request #9890 from Kelebek1/reverb_fix (diff)
parentnative_clock: Round RDTSC frequency to the nearest 1000 (diff)
downloadyuzu-a7792e5ff83523142230951ac7eacbd7685dc40b.tar
yuzu-a7792e5ff83523142230951ac7eacbd7685dc40b.tar.gz
yuzu-a7792e5ff83523142230951ac7eacbd7685dc40b.tar.bz2
yuzu-a7792e5ff83523142230951ac7eacbd7685dc40b.tar.lz
yuzu-a7792e5ff83523142230951ac7eacbd7685dc40b.tar.xz
yuzu-a7792e5ff83523142230951ac7eacbd7685dc40b.tar.zst
yuzu-a7792e5ff83523142230951ac7eacbd7685dc40b.zip
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt10
-rw-r--r--src/common/steady_clock.cpp56
-rw-r--r--src/common/steady_clock.h23
-rw-r--r--src/common/wall_clock.cpp39
-rw-r--r--src/common/wall_clock.h3
-rw-r--r--src/common/windows/timer_resolution.cpp109
-rw-r--r--src/common/windows/timer_resolution.h38
-rw-r--r--src/common/x64/native_clock.cpp17
8 files changed, 267 insertions, 28 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 56b247ac4..58ff5f2f3 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
@@ -142,6 +144,14 @@ add_library(common STATIC
zstd_compression.h
)
+if (WIN32)
+ target_sources(common PRIVATE
+ windows/timer_resolution.cpp
+ windows/timer_resolution.h
+ )
+ target_link_libraries(common PRIVATE ntdll)
+endif()
+
if(ARCHITECTURE_x86_64)
target_sources(common
PRIVATE
diff --git a/src/common/steady_clock.cpp b/src/common/steady_clock.cpp
new file mode 100644
index 000000000..0d5908aa7
--- /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 000000000..9497cf865
--- /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
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index ae07f2811..817e71d52 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/steady_clock.h"
#include "common/uint128.h"
#include "common/wall_clock.h"
@@ -11,45 +12,32 @@
namespace Common {
-using base_timer = std::chrono::steady_clock;
-using base_time_point = std::chrono::time_point<base_timer>;
-
class StandardWallClock final : public WallClock {
public:
explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_)
- : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, false) {
- start_time = base_timer::now();
- }
+ : WallClock{emulated_cpu_frequency_, emulated_clock_frequency_, false},
+ start_time{SteadyClock::Now()} {}
std::chrono::nanoseconds GetTimeNS() override {
- base_time_point current = base_timer::now();
- auto elapsed = current - start_time;
- return std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed);
+ return SteadyClock::Now() - start_time;
}
std::chrono::microseconds GetTimeUS() override {
- base_time_point current = base_timer::now();
- auto elapsed = current - start_time;
- return std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
+ return std::chrono::duration_cast<std::chrono::microseconds>(GetTimeNS());
}
std::chrono::milliseconds GetTimeMS() override {
- base_time_point current = base_timer::now();
- auto elapsed = current - start_time;
- return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
+ return std::chrono::duration_cast<std::chrono::milliseconds>(GetTimeNS());
}
u64 GetClockCycles() override {
- std::chrono::nanoseconds time_now = GetTimeNS();
- const u128 temporary =
- Common::Multiply64Into128(time_now.count(), emulated_clock_frequency);
- return Common::Divide128On32(temporary, 1000000000).first;
+ const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_clock_frequency);
+ return Common::Divide128On32(temp, NS_RATIO).first;
}
u64 GetCPUCycles() override {
- std::chrono::nanoseconds time_now = GetTimeNS();
- const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency);
- return Common::Divide128On32(temporary, 1000000000).first;
+ const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_cpu_frequency);
+ return Common::Divide128On32(temp, NS_RATIO).first;
}
void Pause([[maybe_unused]] bool is_paused) override {
@@ -57,7 +45,7 @@ public:
}
private:
- base_time_point start_time;
+ SteadyClock::time_point start_time;
};
#ifdef ARCHITECTURE_x86_64
@@ -93,4 +81,9 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
#endif
+std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency,
+ u64 emulated_clock_frequency) {
+ return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
+}
+
} // namespace Common
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
index 828a523a8..157ec5eae 100644
--- a/src/common/wall_clock.h
+++ b/src/common/wall_clock.h
@@ -55,4 +55,7 @@ private:
[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
u64 emulated_clock_frequency);
+[[nodiscard]] std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency,
+ u64 emulated_clock_frequency);
+
} // namespace Common
diff --git a/src/common/windows/timer_resolution.cpp b/src/common/windows/timer_resolution.cpp
new file mode 100644
index 000000000..29c6e5c7e
--- /dev/null
+++ b/src/common/windows/timer_resolution.cpp
@@ -0,0 +1,109 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <windows.h>
+
+#include "common/windows/timer_resolution.h"
+
+extern "C" {
+// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtQueryTimerResolution.html
+NTSYSAPI LONG NTAPI NtQueryTimerResolution(PULONG MinimumResolution, PULONG MaximumResolution,
+ PULONG CurrentResolution);
+
+// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtSetTimerResolution.html
+NTSYSAPI LONG NTAPI NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution,
+ PULONG CurrentResolution);
+
+// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FThread%2FNtDelayExecution.html
+NTSYSAPI LONG NTAPI NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval);
+}
+
+// Defines for compatibility with older Windows 10 SDKs.
+
+#ifndef PROCESS_POWER_THROTTLING_EXECUTION_SPEED
+#define PROCESS_POWER_THROTTLING_EXECUTION_SPEED 0x1
+#endif
+#ifndef PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION
+#define PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION 0x4
+#endif
+
+namespace Common::Windows {
+
+namespace {
+
+using namespace std::chrono;
+
+constexpr nanoseconds ToNS(ULONG hundred_ns) {
+ return nanoseconds{hundred_ns * 100};
+}
+
+constexpr ULONG ToHundredNS(nanoseconds ns) {
+ return static_cast<ULONG>(ns.count()) / 100;
+}
+
+struct TimerResolution {
+ std::chrono::nanoseconds minimum;
+ std::chrono::nanoseconds maximum;
+ std::chrono::nanoseconds current;
+};
+
+TimerResolution GetTimerResolution() {
+ ULONG MinimumTimerResolution;
+ ULONG MaximumTimerResolution;
+ ULONG CurrentTimerResolution;
+ NtQueryTimerResolution(&MinimumTimerResolution, &MaximumTimerResolution,
+ &CurrentTimerResolution);
+ return {
+ .minimum{ToNS(MinimumTimerResolution)},
+ .maximum{ToNS(MaximumTimerResolution)},
+ .current{ToNS(CurrentTimerResolution)},
+ };
+}
+
+void SetHighQoS() {
+ // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service
+ PROCESS_POWER_THROTTLING_STATE PowerThrottling{
+ .Version{PROCESS_POWER_THROTTLING_CURRENT_VERSION},
+ .ControlMask{PROCESS_POWER_THROTTLING_EXECUTION_SPEED |
+ PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION},
+ .StateMask{},
+ };
+ SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottling,
+ sizeof(PROCESS_POWER_THROTTLING_STATE));
+}
+
+} // Anonymous namespace
+
+nanoseconds GetMinimumTimerResolution() {
+ return GetTimerResolution().minimum;
+}
+
+nanoseconds GetMaximumTimerResolution() {
+ return GetTimerResolution().maximum;
+}
+
+nanoseconds GetCurrentTimerResolution() {
+ return GetTimerResolution().current;
+}
+
+nanoseconds SetCurrentTimerResolution(nanoseconds timer_resolution) {
+ // Set the timer resolution, and return the current timer resolution.
+ const auto DesiredTimerResolution = ToHundredNS(timer_resolution);
+ ULONG CurrentTimerResolution;
+ NtSetTimerResolution(DesiredTimerResolution, TRUE, &CurrentTimerResolution);
+ return ToNS(CurrentTimerResolution);
+}
+
+nanoseconds SetCurrentTimerResolutionToMaximum() {
+ SetHighQoS();
+ return SetCurrentTimerResolution(GetMaximumTimerResolution());
+}
+
+void SleepForOneTick() {
+ LARGE_INTEGER DelayInterval{
+ .QuadPart{-1},
+ };
+ NtDelayExecution(FALSE, &DelayInterval);
+}
+
+} // namespace Common::Windows
diff --git a/src/common/windows/timer_resolution.h b/src/common/windows/timer_resolution.h
new file mode 100644
index 000000000..e1e50a62d
--- /dev/null
+++ b/src/common/windows/timer_resolution.h
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <chrono>
+
+namespace Common::Windows {
+
+/// Returns the minimum (least precise) supported timer resolution in nanoseconds.
+std::chrono::nanoseconds GetMinimumTimerResolution();
+
+/// Returns the maximum (most precise) supported timer resolution in nanoseconds.
+std::chrono::nanoseconds GetMaximumTimerResolution();
+
+/// Returns the current timer resolution in nanoseconds.
+std::chrono::nanoseconds GetCurrentTimerResolution();
+
+/**
+ * Sets the current timer resolution.
+ *
+ * @param timer_resolution Timer resolution in nanoseconds.
+ *
+ * @returns The current timer resolution.
+ */
+std::chrono::nanoseconds SetCurrentTimerResolution(std::chrono::nanoseconds timer_resolution);
+
+/**
+ * Sets the current timer resolution to the maximum supported timer resolution.
+ *
+ * @returns The current timer resolution.
+ */
+std::chrono::nanoseconds SetCurrentTimerResolutionToMaximum();
+
+/// Sleep for one tick of the current timer resolution.
+void SleepForOneTick();
+
+} // namespace Common::Windows
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 8b08332ab..bc1a973b0 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -6,6 +6,7 @@
#include <thread>
#include "common/atomic_ops.h"
+#include "common/steady_clock.h"
#include "common/uint128.h"
#include "common/x64/native_clock.h"
@@ -39,6 +40,12 @@ static u64 FencedRDTSC() {
}
#endif
+template <u64 Nearest>
+static u64 RoundToNearest(u64 value) {
+ const auto mod = value % Nearest;
+ return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
+}
+
u64 EstimateRDTSCFrequency() {
// Discard the first result measuring the rdtsc.
FencedRDTSC();
@@ -46,18 +53,18 @@ u64 EstimateRDTSCFrequency() {
FencedRDTSC();
// Get the current time.
- const auto start_time = std::chrono::steady_clock::now();
+ const auto start_time = Common::SteadyClock::Now();
const u64 tsc_start = FencedRDTSC();
- // Wait for 200 milliseconds.
- std::this_thread::sleep_for(std::chrono::milliseconds{200});
- const auto end_time = std::chrono::steady_clock::now();
+ // Wait for 250 milliseconds.
+ std::this_thread::sleep_for(std::chrono::milliseconds{250});
+ const auto end_time = Common::SteadyClock::Now();
const u64 tsc_end = FencedRDTSC();
// Calculate differences.
const u64 timer_diff = static_cast<u64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
const u64 tsc_diff = tsc_end - tsc_start;
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
- return tsc_freq;
+ return RoundToNearest<1000>(tsc_freq);
}
namespace X64 {