common: add arm64 native clock

This commit is contained in:
Liam 2023-10-08 11:32:32 -04:00
parent bd42bba71c
commit 21bc2c14bc
4 changed files with 134 additions and 1 deletions

View file

@ -189,6 +189,14 @@ if(ARCHITECTURE_x86_64)
target_link_libraries(common PRIVATE xbyak::xbyak) target_link_libraries(common PRIVATE xbyak::xbyak)
endif() endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX))
target_sources(common
PRIVATE
arm64/native_clock.cpp
arm64/native_clock.h
)
endif()
if (MSVC) if (MSVC)
target_compile_definitions(common PRIVATE target_compile_definitions(common PRIVATE
# The standard library doesn't provide any replacement for codecvt yet # The standard library doesn't provide any replacement for codecvt yet

View file

@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/arm64/native_clock.h"
namespace Common::Arm64 {
namespace {
NativeClock::FactorType GetFixedPointFactor(u64 num, u64 den) {
return (static_cast<NativeClock::FactorType>(num) << 64) / den;
}
u64 MultiplyHigh(u64 m, NativeClock::FactorType factor) {
return static_cast<u64>((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(GetHostTicksElapsed(), ns_cntfrq_factor)};
}
std::chrono::microseconds NativeClock::GetTimeUS() const {
return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_cntfrq_factor)};
}
std::chrono::milliseconds NativeClock::GetTimeMS() const {
return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_cntfrq_factor)};
}
u64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetHostTicksElapsed(), guest_cntfrq_factor);
}
u64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetHostTicksElapsed(), gputick_cntfrq_factor);
}
u64 NativeClock::GetHostTicksNow() const {
u64 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;
}
u64 NativeClock::GetHostTicksElapsed() const {
return GetHostTicksNow();
}
bool NativeClock::IsNative() const {
return true;
}
u64 NativeClock::GetHostCNTFRQ() {
u64 cntfrq_el0 = 0;
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
return cntfrq_el0;
}
} // namespace Common::Arm64

View file

@ -0,0 +1,47 @@
// 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;
u64 GetCNTPCT() const override;
u64 GetGPUTick() const override;
u64 GetHostTicksNow() const override;
u64 GetHostTicksElapsed() const override;
bool IsNative() const override;
static u64 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

View file

@ -10,6 +10,10 @@
#include "common/x64/rdtsc.h" #include "common/x64/rdtsc.h"
#endif #endif
#if defined(ARCHITECTURE_arm64) && defined(__linux__)
#include "common/arm64/native_clock.h"
#endif
namespace Common { namespace Common {
class StandardWallClock final : public WallClock { class StandardWallClock final : public WallClock {
@ -53,7 +57,7 @@ private:
}; };
std::unique_ptr<WallClock> CreateOptimalClock() { std::unique_ptr<WallClock> CreateOptimalClock() {
#ifdef ARCHITECTURE_x86_64 #if defined(ARCHITECTURE_x86_64)
const auto& caps = GetCPUCaps(); const auto& caps = GetCPUCaps();
if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) { if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) {
@ -64,6 +68,8 @@ std::unique_ptr<WallClock> CreateOptimalClock() {
// - Is not more precise than 1 GHz (1ns resolution) // - Is not more precise than 1 GHz (1ns resolution)
return std::make_unique<StandardWallClock>(); return std::make_unique<StandardWallClock>();
} }
#elif defined(ARCHITECTURE_arm64) && defined(__linux__)
return std::make_unique<Arm64::NativeClock>();
#else #else
return std::make_unique<StandardWallClock>(); return std::make_unique<StandardWallClock>();
#endif #endif