From e3dd527a2ced292099011bd5755b71ed044b8b36 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 18 Apr 2026 21:28:10 +0000 Subject: [PATCH] [common] remove ptr indirection on WallClock Signed-off-by: lizzie --- src/common/arm64/native_clock.cpp | 87 ------- src/common/arm64/native_clock.h | 45 ---- src/common/wall_clock.cpp | 224 +++++++++++++----- src/common/wall_clock.h | 45 +++- src/common/x64/native_clock.cpp | 46 ---- src/common/x64/native_clock.h | 38 --- src/core/arm/nce/patcher.cpp | 2 +- src/core/core_timing.cpp | 155 ++++++------ src/core/core_timing.h | 18 +- .../nvnflinger/buffer_queue_producer.cpp | 9 +- .../nvnflinger/buffer_queue_producer.h | 3 +- 11 files changed, 285 insertions(+), 387 deletions(-) delete mode 100644 src/common/arm64/native_clock.cpp delete mode 100644 src/common/arm64/native_clock.h delete mode 100644 src/common/x64/native_clock.cpp delete mode 100644 src/common/x64/native_clock.h diff --git a/src/common/arm64/native_clock.cpp b/src/common/arm64/native_clock.cpp deleted file mode 100644 index 76ffb74bab..0000000000 --- a/src/common/arm64/native_clock.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#ifdef ANDROID -#include -#endif -#include "common/arm64/native_clock.h" - -namespace Common::Arm64 { - -namespace { - -NativeClock::FactorType GetFixedPointFactor(u64 num, u64 den) { - return (static_cast(num) << 64) / den; -} - -u64 MultiplyHigh(u64 m, NativeClock::FactorType factor) { - return static_cast((m * factor) >> 64); -} - -} // namespace - -NativeClock::NativeClock() { - const u64 host_cntfrq = GetHostCNTFRQ(); - ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq); - us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq); - ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq); - guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq); - gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq); -} - -std::chrono::nanoseconds NativeClock::GetTimeNS() const { - return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)}; -} - -std::chrono::microseconds NativeClock::GetTimeUS() const { - return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)}; -} - -std::chrono::milliseconds NativeClock::GetTimeMS() const { - return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)}; -} - -s64 NativeClock::GetCNTPCT() const { - return MultiplyHigh(GetUptime(), guest_cntfrq_factor); -} - -s64 NativeClock::GetGPUTick() const { - return MultiplyHigh(GetUptime(), gputick_cntfrq_factor); -} - -s64 NativeClock::GetUptime() const { - s64 cntvct_el0 = 0; - asm volatile("dsb ish\n\t" - "mrs %[cntvct_el0], cntvct_el0\n\t" - "dsb ish\n\t" - : [cntvct_el0] "=r"(cntvct_el0)); - return cntvct_el0; -} - -bool NativeClock::IsNative() const { - return true; -} - -s64 NativeClock::GetHostCNTFRQ() { - u64 cntfrq_el0 = 0; - std::string_view board{""}; -#ifdef ANDROID - char buffer[PROP_VALUE_MAX]; - int len{__system_property_get("ro.product.board", buffer)}; - board = std::string_view(buffer, static_cast(len)); -#endif - if (board == "s5e9925") { // Exynos 2200 - cntfrq_el0 = 25600000; - } else if (board == "exynos2100") { // Exynos 2100 - cntfrq_el0 = 26000000; - } else if (board == "exynos9810") { // Exynos 9810 - cntfrq_el0 = 26000000; - } else if (board == "s5e8825") { // Exynos 1280 - cntfrq_el0 = 26000000; - } else { - asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); - } - return cntfrq_el0; -} - -} // namespace Common::Arm64 diff --git a/src/common/arm64/native_clock.h b/src/common/arm64/native_clock.h deleted file mode 100644 index 94bc1882e0..0000000000 --- a/src/common/arm64/native_clock.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/wall_clock.h" - -namespace Common::Arm64 { - -class NativeClock final : public WallClock { -public: - explicit NativeClock(); - - std::chrono::nanoseconds GetTimeNS() const override; - - std::chrono::microseconds GetTimeUS() const override; - - std::chrono::milliseconds GetTimeMS() const override; - - s64 GetCNTPCT() const override; - - s64 GetGPUTick() const override; - - s64 GetUptime() const override; - - bool IsNative() const override; - - static s64 GetHostCNTFRQ(); - -public: - using FactorType = unsigned __int128; - - FactorType GetGuestCNTFRQFactor() const { - return guest_cntfrq_factor; - } - -private: - FactorType ns_cntfrq_factor; - FactorType us_cntfrq_factor; - FactorType ms_cntfrq_factor; - FactorType guest_cntfrq_factor; - FactorType gputick_cntfrq_factor; -}; - -} // namespace Common::Arm64 diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index 4f9c240905..d435375267 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -1,77 +1,191 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // 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" #ifdef ARCHITECTURE_x86_64 #include "common/x64/cpu_detect.h" -#include "common/x64/native_clock.h" #include "common/x64/rdtsc.h" #endif -#ifdef HAS_NCE -#include "common/arm64/native_clock.h" -#endif namespace Common { -class StandardWallClock final : public WallClock { -public: - explicit StandardWallClock() {} - - std::chrono::nanoseconds GetTimeNS() const override { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()); - } - - std::chrono::microseconds GetTimeUS() const override { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()); - } - - std::chrono::milliseconds GetTimeMS() const override { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()); - } - - s64 GetCNTPCT() const override { - return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; - } - - s64 GetGPUTick() const override { - return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; - } - - s64 GetUptime() const override { - return std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - } - - bool IsNative() const override { - return false; - } -}; - -std::unique_ptr CreateOptimalClock() { #if defined(ARCHITECTURE_x86_64) - const auto& caps = GetCPUCaps(); +WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept + : invariant{invariant_} + , rdtsc_frequency{rdtsc_frequency_} + , ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)} + , us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)} + , ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)} + , cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)} + , gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} +{} - if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) { - return std::make_unique(caps.tsc_frequency); - } else { - // Fallback to StandardWallClock if the hardware TSC - // - Is not invariant - // - Is not more precise than 1 GHz (1ns resolution) - return std::make_unique(); - } +std::chrono::nanoseconds WallClock::GetTimeNS() const { + if (invariant) + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)}; +} + +std::chrono::microseconds WallClock::GetTimeUS() const { + if (invariant) + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)}; +} + +std::chrono::milliseconds WallClock::GetTimeMS() const { + if (invariant) + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)}; +} + +s64 WallClock::GetCNTPCT() const { + if (invariant) + return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; + return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor); +} + +s64 WallClock::GetGPUTick() const { + if (invariant) + return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; + return MultiplyHigh(GetUptime(), gputick_rdtsc_factor); +} + +s64 WallClock::GetUptime() const { + if (invariant) + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + return s64(Common::X64::FencedRDTSC()); +} + +bool WallClock::IsNative() const { + if (invariant) + return false; + return true; +} #elif defined(HAS_NCE) - return std::make_unique(); +namespace { + +[[nodiscard]] WallClock::FactorType GetFixedPointFactor(u64 num, u64 den) noexcept { + return (WallClock::FactorType(num) << 64) / den; +} + +[[nodiscard]] u64 MultiplyHigh(u64 m, WallClock::FactorType factor) noexcept { + return static_cast((m * factor) >> 64); +} + +[[nodiscard]] s64 GetHostCNTFRQ() noexcept { + u64 cntfrq_el0 = 0; +#ifdef ANDROID + std::string_view board{""}; + char buffer[PROP_VALUE_MAX]; + int len{__system_property_get("ro.product.board", buffer)}; + board = std::string_view(buffer, static_cast(len)); + if (board == "s5e9925") { // Exynos 2200 + cntfrq_el0 = 25600000; + } else if (board == "exynos2100") { // Exynos 2100 + cntfrq_el0 = 26000000; + } else if (board == "exynos9810") { // Exynos 9810 + cntfrq_el0 = 26000000; + } else if (board == "s5e8825") { // Exynos 1280 + cntfrq_el0 = 26000000; + } else { + asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); + } + return cntfrq_el0; #else - return std::make_unique(); + asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); + return cntfrq_el0; +#endif +} + +} // namespace + +WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept { + const u64 host_cntfrq = GetHostCNTFRQ(); + ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq); + us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq); + ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq); + guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq); + gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq); +} + +std::chrono::nanoseconds WallClock::GetTimeNS() const { + return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)}; +} + +std::chrono::microseconds WallClock::GetTimeUS() const { + return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)}; +} + +std::chrono::milliseconds WallClock::GetTimeMS() const { + return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)}; +} + +s64 WallClock::GetCNTPCT() const { + return MultiplyHigh(GetUptime(), guest_cntfrq_factor); +} + +s64 WallClock::GetGPUTick() const { + return MultiplyHigh(GetUptime(), gputick_cntfrq_factor); +} + +s64 WallClock::GetUptime() const { + s64 cntvct_el0 = 0; + asm volatile("dsb ish\n\t" + "mrs %[cntvct_el0], cntvct_el0\n\t" + "dsb ish\n\t" + : [cntvct_el0] "=r"(cntvct_el0)); + return cntvct_el0; +} + +bool WallClock::IsNative() const { + return true; +} +#else +WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {} + +std::chrono::nanoseconds WallClock::GetTimeNS() const { + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); +} + +std::chrono::microseconds WallClock::GetTimeUS() const { + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); +} + +std::chrono::milliseconds WallClock::GetTimeMS() const { + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); +} + +s64 WallClock::GetCNTPCT() const { + return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; +} + +s64 WallClock::GetGPUTick() const { + return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; +} + +s64 WallClock::GetUptime() const { + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); +} + +bool WallClock::IsNative() const { + return false; +} +#endif + +WallClock CreateOptimalClock() noexcept { +#if defined(ARCHITECTURE_x86_64) + auto const& caps = GetCPUCaps(); + return WallClock(!(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den), std::max(caps.tsc_frequency, 1)); +#elif defined(HAS_NCE) + return WallClock(false, 1); +#else + return WallClock(true, 1); #endif } diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index 7ad6536930..6a6d56a610 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -20,28 +20,28 @@ public: static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz - virtual ~WallClock() = default; + explicit WallClock(bool invariant, u64 rdtsc_frequency_) noexcept; /// @returns The time in nanoseconds since the construction of this clock. - virtual std::chrono::nanoseconds GetTimeNS() const = 0; + std::chrono::nanoseconds GetTimeNS() const; /// @returns The time in microseconds since the construction of this clock. - virtual std::chrono::microseconds GetTimeUS() const = 0; + std::chrono::microseconds GetTimeUS() const; /// @returns The time in milliseconds since the construction of this clock. - virtual std::chrono::milliseconds GetTimeMS() const = 0; + std::chrono::milliseconds GetTimeMS() const; /// @returns The guest CNTPCT ticks since the construction of this clock. - virtual s64 GetCNTPCT() const = 0; + s64 GetCNTPCT() const; /// @returns The guest GPU ticks since the construction of this clock. - virtual s64 GetGPUTick() const = 0; + s64 GetGPUTick() const; /// @returns The raw host timer ticks since an indeterminate epoch. - virtual s64 GetUptime() const = 0; + s64 GetUptime() const; /// @returns Whether the clock directly uses the host's hardware clock. - virtual bool IsNative() const = 0; + bool IsNative() const; static inline u64 NSToCNTPCT(u64 ns) { return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; @@ -85,8 +85,33 @@ protected: using CPUTickToUsRatio = std::ratio; using CPUTickToCNTPCTRatio = std::ratio; using CPUTickToGPUTickRatio = std::ratio; + +#if defined(ARCHITECTURE_x86_64) + bool invariant; + u64 rdtsc_frequency; + u64 ns_rdtsc_factor; + u64 us_rdtsc_factor; + u64 ms_rdtsc_factor; + u64 cntpct_rdtsc_factor; + u64 gputick_rdtsc_factor; +#elif defined(HAS_NCE) +public: + using FactorType = unsigned __int128; + + FactorType GetGuestCNTFRQFactor() const { + return guest_cntfrq_factor; + } +protected: + FactorType ns_cntfrq_factor; + FactorType us_cntfrq_factor; + FactorType ms_cntfrq_factor; + FactorType guest_cntfrq_factor; + FactorType gputick_cntfrq_factor; +#else + +#endif }; -[[nodiscard]] std::unique_ptr CreateOptimalClock(); +[[nodiscard]] WallClock CreateOptimalClock() noexcept; } // namespace Common diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp deleted file mode 100644 index d2d27fafea..0000000000 --- a/src/common/x64/native_clock.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/uint128.h" -#include "common/x64/native_clock.h" -#include "common/x64/rdtsc.h" - -namespace Common::X64 { - -NativeClock::NativeClock(u64 rdtsc_frequency_) - : rdtsc_frequency{rdtsc_frequency_}, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, - rdtsc_frequency)}, - us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)}, - ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)}, - cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)}, - gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {} - -std::chrono::nanoseconds NativeClock::GetTimeNS() const { - return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)}; -} - -std::chrono::microseconds NativeClock::GetTimeUS() const { - return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)}; -} - -std::chrono::milliseconds NativeClock::GetTimeMS() const { - return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)}; -} - -s64 NativeClock::GetCNTPCT() const { - return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor); -} - -s64 NativeClock::GetGPUTick() const { - return MultiplyHigh(GetUptime(), gputick_rdtsc_factor); -} - -s64 NativeClock::GetUptime() const { - return static_cast(FencedRDTSC()); -} - -bool NativeClock::IsNative() const { - return true; -} - -} // namespace Common::X64 diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h deleted file mode 100644 index b2629b0311..0000000000 --- a/src/common/x64/native_clock.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/wall_clock.h" - -namespace Common::X64 { - -class NativeClock final : public WallClock { -public: - explicit NativeClock(u64 rdtsc_frequency_); - - std::chrono::nanoseconds GetTimeNS() const override; - - std::chrono::microseconds GetTimeUS() const override; - - std::chrono::milliseconds GetTimeMS() const override; - - s64 GetCNTPCT() const override; - - s64 GetGPUTick() const override; - - s64 GetUptime() const override; - - bool IsNative() const override; - -private: - u64 rdtsc_frequency; - - u64 ns_rdtsc_factor; - u64 us_rdtsc_factor; - u64 ms_rdtsc_factor; - u64 cntpct_rdtsc_factor; - u64 gputick_rdtsc_factor; -}; - -} // namespace Common::X64 diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp index ea77166645..37b0688da6 100644 --- a/src/core/arm/nce/patcher.cpp +++ b/src/core/arm/nce/patcher.cpp @@ -578,7 +578,7 @@ void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg, } void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) { - static Common::Arm64::NativeClock clock{}; + static Common::WallClock clock{}; const auto factor = clock.GetGuestCNTFRQFactor(); const auto raw_factor = std::bit_cast>(factor); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index b216dc2094..6738a0d73d 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -57,15 +57,51 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { Reset(); on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; - shutting_down = false; cpu_ticks = 0; if (is_multicore) { - timer_thread.emplace([](CoreTiming& instance) { + timer_thread = std::jthread([this](std::stop_token stop_token) { Common::SetCurrentThreadName("HostTiming"); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); - instance.on_thread_init(); - instance.ThreadLoop(); - }, std::ref(*this)); + on_thread_init(); + has_started = true; + while (!stop_token.stop_requested()) { + while (!paused && !stop_token.stop_requested()) { + paused_set = false; + if (auto const next_time = Advance(); next_time) { + // There are more events left in the queue, wait until the next event. + auto wait_time = *next_time - GetGlobalTimeNs().count(); + if (wait_time > 0) { +#ifdef _WIN32 + while (!paused && !event.IsSet() && wait_time > 0) { + wait_time = *next_time - GetGlobalTimeNs().count(); + if (wait_time >= timer_resolution_ns) { + Common::Windows::SleepForOneTick(); + } else { +#ifdef ARCHITECTURE_x86_64 + Common::X64::MicroSleep(); +#else + std::this_thread::yield(); +#endif + } + } + if (event.IsSet()) + event.Reset(); +#else + event.WaitFor(std::chrono::nanoseconds(wait_time)); +#endif + } + } else { + // Queue is empty, wait until another event is scheduled and signals us to + // continue. + wait_set = true; + event.Wait(); + } + wait_set = false; + } + paused_set = true; + pause_event.Wait(); + } + }); } } @@ -90,7 +126,7 @@ void CoreTiming::SyncPause(bool is_paused) { } Pause(is_paused); - if (timer_thread) { + if (timer_thread.joinable()) { if (!is_paused) { pause_event.Set(); } @@ -190,33 +226,22 @@ void CoreTiming::ResetTicks() { } u64 CoreTiming::GetClockTicks() const { - u64 fres; - if (is_multicore) [[likely]] { - fres = clock->GetCNTPCT(); - } else { - fres = Common::WallClock::CPUTickToCNTPCT(cpu_ticks); + u64 fres = is_multicore ? clock.GetCNTPCT() : Common::WallClock::CPUTickToCNTPCT(cpu_ticks); + if (auto const overclock = Settings::values.fast_cpu_time.GetValue(); overclock != Settings::CpuClock::Off) { + fres = u64(f64(fres) * (1.7 + 0.3 * u32(overclock))); } - - const auto overclock = Settings::values.fast_cpu_time.GetValue(); - - if (overclock != Settings::CpuClock::Off) { - fres = (u64) ((double) fres * (1.7 + 0.3 * u32(overclock))); - } - - if (Settings::values.sync_core_speed.GetValue()) { - const auto ticks = double(fres); - const auto speed_limit = double(Settings::SpeedLimit())*0.01; - return u64(ticks/speed_limit); - } else { - return fres; - } + if (::Settings::values.sync_core_speed.GetValue()) { + auto const ticks = f64(fres); + auto const speed_limit = f64(Settings::SpeedLimit()) * 0.01; + return u64(ticks / speed_limit); + } + return fres; } u64 CoreTiming::GetGPUTicks() const { - if (is_multicore) [[likely]] { - return clock->GetGPUTick(); - } - return Common::WallClock::CPUTickToGPUTick(cpu_ticks); + return is_multicore + ? clock.GetGPUTick() + : Common::WallClock::CPUTickToGPUTick(cpu_ticks); } std::optional CoreTiming::Advance() { @@ -278,75 +303,29 @@ std::optional CoreTiming::Advance() { } } -void CoreTiming::ThreadLoop() { - has_started = true; - while (!shutting_down) { - while (!paused) { - paused_set = false; - const auto next_time = Advance(); - if (next_time) { - // There are more events left in the queue, wait until the next event. - auto wait_time = *next_time - GetGlobalTimeNs().count(); - if (wait_time > 0) { -#ifdef _WIN32 - while (!paused && !event.IsSet() && wait_time > 0) { - wait_time = *next_time - GetGlobalTimeNs().count(); - if (wait_time >= timer_resolution_ns) { - Common::Windows::SleepForOneTick(); - } else { -#ifdef ARCHITECTURE_x86_64 - Common::X64::MicroSleep(); -#else - std::this_thread::yield(); -#endif - } - } - - if (event.IsSet()) { - event.Reset(); - } -#else - event.WaitFor(std::chrono::nanoseconds(wait_time)); -#endif - } - } else { - // Queue is empty, wait until another event is scheduled and signals us to - // continue. - wait_set = true; - event.Wait(); - } - wait_set = false; - } - - paused_set = true; - pause_event.Wait(); - } -} - void CoreTiming::Reset() { paused = true; - shutting_down = true; pause_event.Set(); event.Set(); - if (timer_thread) { - timer_thread->join(); + if (timer_thread.joinable()) { + timer_thread.request_stop(); + timer_thread.join(); } - timer_thread.reset(); has_started = false; } -std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { - if (is_multicore) [[likely]] { - return clock->GetTimeNS(); - } - return std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)}; +/// @brief Returns current time in nanoseconds. +std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const noexcept { + return is_multicore + ? clock.GetTimeNS() + : std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)}; } -std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { - if (is_multicore) [[likely]] { - return clock->GetTimeUS(); - } - return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)}; +/// @brief Returns current time in microseconds. +std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const noexcept { + return is_multicore + ? clock.GetTimeUS() + : std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)}; } #ifdef _WIN32 diff --git a/src/core/core_timing.h b/src/core/core_timing.h index ae9f56d519..5967c83b57 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -118,7 +118,7 @@ public: void Idle(); - s64 GetDowncount() const { + s64 GetDowncount() const noexcept { return downcount; } @@ -128,11 +128,8 @@ public: /// Returns the current GPU tick value. u64 GetGPUTicks() const; - /// Returns current time in microseconds. - std::chrono::microseconds GetGlobalTimeUs() const; - - /// Returns current time in nanoseconds. - std::chrono::nanoseconds GetGlobalTimeNs() const; + [[nodiscard]] std::chrono::microseconds GetGlobalTimeUs() const noexcept; + [[nodiscard]] std::chrono::nanoseconds GetGlobalTimeNs() const noexcept; /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. std::optional Advance(); @@ -141,13 +138,11 @@ public: void SetTimerResolutionNs(std::chrono::nanoseconds ns); #endif -private: struct Event; - void ThreadLoop(); void Reset(); - std::unique_ptr clock; + Common::WallClock clock; s64 global_timer = 0; @@ -165,11 +160,10 @@ private: Common::Event pause_event{}; mutable std::mutex basic_lock; std::mutex advance_lock; - std::optional timer_thread; + std::jthread timer_thread; std::atomic paused{}; std::atomic paused_set{}; std::atomic wait_set{}; - std::atomic shutting_down{}; std::atomic has_started{}; std::function on_thread_init{}; diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index 436b0f4b05..bb0433e1d0 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -26,8 +26,11 @@ namespace Service::android { BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, std::shared_ptr buffer_queue_core_, Service::Nvidia::NvCore::NvMap& nvmap_) - : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), - clock{Common::CreateOptimalClock()}, nvmap(nvmap_) { + : service_context{service_context_}, core{std::move(buffer_queue_core_)} + , slots(core->slots) + , clock{Common::CreateOptimalClock()} + , nvmap(nvmap_) +{ buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); } @@ -485,7 +488,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, slots[slot].buffer_state = BufferState::Queued; slots[slot].frame_number = core->frame_counter; slots[slot].queue_time = timestamp; - slots[slot].presentation_time = clock->GetTimeNS().count(); + slots[slot].presentation_time = clock.GetTimeNS().count(); slots[slot].fence = fence; item.slot = slot; diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h index 06cb49cbad..51c0801a0a 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h @@ -89,8 +89,7 @@ private: s32 next_callback_ticket{}; s32 current_callback_ticket{}; std::condition_variable_any callback_condition; - std::unique_ptr clock; - + Common::WallClock clock; Service::Nvidia::NvCore::NvMap& nvmap; };