Common: Implement WallClock Interface and implement a native clock for x64
This commit is contained in:
parent
0f8e5a1465
commit
234b5ff6a9
|
@ -167,6 +167,8 @@ add_library(common STATIC
|
||||||
vector_math.h
|
vector_math.h
|
||||||
virtual_buffer.cpp
|
virtual_buffer.cpp
|
||||||
virtual_buffer.h
|
virtual_buffer.h
|
||||||
|
wall_clock.cpp
|
||||||
|
wall_clock.h
|
||||||
web_result.h
|
web_result.h
|
||||||
zstd_compression.cpp
|
zstd_compression.cpp
|
||||||
zstd_compression.h
|
zstd_compression.h
|
||||||
|
@ -177,6 +179,8 @@ if(ARCHITECTURE_x86_64)
|
||||||
PRIVATE
|
PRIVATE
|
||||||
x64/cpu_detect.cpp
|
x64/cpu_detect.cpp
|
||||||
x64/cpu_detect.h
|
x64/cpu_detect.h
|
||||||
|
x64/native_clock.cpp
|
||||||
|
x64/native_clock.h
|
||||||
x64/xbyak_abi.h
|
x64/xbyak_abi.h
|
||||||
x64/xbyak_util.h
|
x64/xbyak_util.h
|
||||||
)
|
)
|
||||||
|
|
90
src/common/wall_clock.cpp
Normal file
90
src/common/wall_clock.cpp
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#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"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
using base_timer = std::chrono::steady_clock;
|
||||||
|
using base_time_point = std::chrono::time_point<base_timer>;
|
||||||
|
|
||||||
|
class StandardWallClock : public WallClock {
|
||||||
|
public:
|
||||||
|
StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency)
|
||||||
|
: WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) {
|
||||||
|
start_time = base_timer::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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetClockCycles() override {
|
||||||
|
std::chrono::nanoseconds time_now = GetTimeNS();
|
||||||
|
const u128 temporal = Common::Multiply64Into128(time_now.count(), emulated_clock_frequency);
|
||||||
|
return Common::Divide128On32(temporal, 1000000000).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetCPUCycles() override {
|
||||||
|
std::chrono::nanoseconds time_now = GetTimeNS();
|
||||||
|
const u128 temporal = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency);
|
||||||
|
return Common::Divide128On32(temporal, 1000000000).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
base_time_point start_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef ARCHITECTURE_x86_64
|
||||||
|
|
||||||
|
WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) {
|
||||||
|
const auto& caps = GetCPUCaps();
|
||||||
|
u64 rtsc_frequency = 0;
|
||||||
|
if (caps.invariant_tsc) {
|
||||||
|
if (caps.base_frequency != 0) {
|
||||||
|
rtsc_frequency = static_cast<u64>(caps.base_frequency) * 1000000U;
|
||||||
|
}
|
||||||
|
if (rtsc_frequency == 0) {
|
||||||
|
rtsc_frequency = EstimateRDTSCFrequency();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rtsc_frequency == 0) {
|
||||||
|
return static_cast<WallClock*>(
|
||||||
|
new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency));
|
||||||
|
} else {
|
||||||
|
return static_cast<WallClock*>(
|
||||||
|
new X64::NativeClock(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) {
|
||||||
|
return static_cast<WallClock*>(
|
||||||
|
new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace Common
|
40
src/common/wall_clock.h
Normal file
40
src/common/wall_clock.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
class WallClock {
|
||||||
|
public:
|
||||||
|
virtual std::chrono::nanoseconds GetTimeNS() = 0;
|
||||||
|
virtual std::chrono::microseconds GetTimeUS() = 0;
|
||||||
|
virtual std::chrono::milliseconds GetTimeMS() = 0;
|
||||||
|
virtual u64 GetClockCycles() = 0;
|
||||||
|
virtual u64 GetCPUCycles() = 0;
|
||||||
|
|
||||||
|
/// Tells if the wall clock, uses the host CPU's hardware clock
|
||||||
|
bool IsNative() const {
|
||||||
|
return is_native;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, bool is_native)
|
||||||
|
: emulated_cpu_frequency{emulated_cpu_frequency},
|
||||||
|
emulated_clock_frequency{emulated_clock_frequency}, is_native{is_native} {}
|
||||||
|
|
||||||
|
u64 emulated_cpu_frequency;
|
||||||
|
u64 emulated_clock_frequency;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool is_native;
|
||||||
|
};
|
||||||
|
|
||||||
|
WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency);
|
||||||
|
|
||||||
|
} // namespace Common
|
|
@ -62,6 +62,17 @@ static CPUCaps Detect() {
|
||||||
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
|
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
|
||||||
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
|
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
|
||||||
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
|
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
|
||||||
|
if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69)
|
||||||
|
caps.manufacturer = Manufacturer::Intel;
|
||||||
|
else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65)
|
||||||
|
caps.manufacturer = Manufacturer::AMD;
|
||||||
|
else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e)
|
||||||
|
caps.manufacturer = Manufacturer::Hygon;
|
||||||
|
else
|
||||||
|
caps.manufacturer = Manufacturer::Unknown;
|
||||||
|
|
||||||
|
u32 family = {};
|
||||||
|
u32 model = {};
|
||||||
|
|
||||||
__cpuid(cpu_id, 0x80000000);
|
__cpuid(cpu_id, 0x80000000);
|
||||||
|
|
||||||
|
@ -73,6 +84,14 @@ static CPUCaps Detect() {
|
||||||
// Detect family and other miscellaneous features
|
// Detect family and other miscellaneous features
|
||||||
if (max_std_fn >= 1) {
|
if (max_std_fn >= 1) {
|
||||||
__cpuid(cpu_id, 0x00000001);
|
__cpuid(cpu_id, 0x00000001);
|
||||||
|
family = (cpu_id[0] >> 8) & 0xf;
|
||||||
|
model = (cpu_id[0] >> 4) & 0xf;
|
||||||
|
if (family == 0xf) {
|
||||||
|
family += (cpu_id[0] >> 20) & 0xff;
|
||||||
|
}
|
||||||
|
if (family >= 6) {
|
||||||
|
model += ((cpu_id[0] >> 16) & 0xf) << 4;
|
||||||
|
}
|
||||||
|
|
||||||
if ((cpu_id[3] >> 25) & 1)
|
if ((cpu_id[3] >> 25) & 1)
|
||||||
caps.sse = true;
|
caps.sse = true;
|
||||||
|
@ -130,6 +149,20 @@ static CPUCaps Detect() {
|
||||||
caps.fma4 = true;
|
caps.fma4 = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (max_ex_fn >= 0x80000007) {
|
||||||
|
__cpuid(cpu_id, 0x80000007);
|
||||||
|
if (cpu_id[3] & (1 << 8)) {
|
||||||
|
caps.invariant_tsc = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_std_fn >= 0x16) {
|
||||||
|
__cpuid(cpu_id, 0x16);
|
||||||
|
caps.base_frequency = cpu_id[0];
|
||||||
|
caps.max_frequency = cpu_id[1];
|
||||||
|
caps.bus_frequency = cpu_id[2];
|
||||||
|
}
|
||||||
|
|
||||||
return caps;
|
return caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,16 @@
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
enum class Manufacturer : u32 {
|
||||||
|
Intel = 0,
|
||||||
|
AMD = 1,
|
||||||
|
Hygon = 2,
|
||||||
|
Unknown = 3,
|
||||||
|
};
|
||||||
|
|
||||||
/// x86/x64 CPU capabilities that may be detected by this module
|
/// x86/x64 CPU capabilities that may be detected by this module
|
||||||
struct CPUCaps {
|
struct CPUCaps {
|
||||||
|
Manufacturer manufacturer;
|
||||||
char cpu_string[0x21];
|
char cpu_string[0x21];
|
||||||
char brand_string[0x41];
|
char brand_string[0x41];
|
||||||
bool sse;
|
bool sse;
|
||||||
|
@ -24,6 +32,10 @@ struct CPUCaps {
|
||||||
bool fma;
|
bool fma;
|
||||||
bool fma4;
|
bool fma4;
|
||||||
bool aes;
|
bool aes;
|
||||||
|
bool invariant_tsc;
|
||||||
|
u32 base_frequency;
|
||||||
|
u32 max_frequency;
|
||||||
|
u32 bus_frequency;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
128
src/common/x64/native_clock.cpp
Normal file
128
src/common/x64/native_clock.cpp
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
#else
|
||||||
|
#include <x86intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common/x64/native_clock.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct uint128 {
|
||||||
|
u64 low;
|
||||||
|
u64 high;
|
||||||
|
};
|
||||||
|
|
||||||
|
u64 umuldiv64(u64 a, u64 b, u64 d) {
|
||||||
|
uint128 r{};
|
||||||
|
r.low = _umul128(a, b, &r.high);
|
||||||
|
u64 remainder;
|
||||||
|
return _udiv128(r.high, r.low, d, &remainder);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
u64 umuldiv64(u64 a, u64 b, u64 d) {
|
||||||
|
const u64 diva = a / d;
|
||||||
|
const u64 moda = a % d;
|
||||||
|
const u64 divb = b / d;
|
||||||
|
const u64 modb = b % d;
|
||||||
|
return diva * b + moda * divb + moda * modb / d;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
u64 EstimateRDTSCFrequency() {
|
||||||
|
const auto milli_10 = std::chrono::milliseconds{10};
|
||||||
|
// get current time
|
||||||
|
_mm_mfence();
|
||||||
|
const u64 tscStart = __rdtsc();
|
||||||
|
const auto startTime = std::chrono::high_resolution_clock::now();
|
||||||
|
// wait roughly 3 seconds
|
||||||
|
while (true) {
|
||||||
|
auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::high_resolution_clock::now() - startTime);
|
||||||
|
if (milli.count() >= 3000)
|
||||||
|
break;
|
||||||
|
std::this_thread::sleep_for(milli_10);
|
||||||
|
}
|
||||||
|
const auto endTime = std::chrono::high_resolution_clock::now();
|
||||||
|
_mm_mfence();
|
||||||
|
const u64 tscEnd = __rdtsc();
|
||||||
|
// calculate difference
|
||||||
|
const u64 timer_diff =
|
||||||
|
std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
|
||||||
|
const u64 tsc_diff = tscEnd - tscStart;
|
||||||
|
const u64 tsc_freq = umuldiv64(tsc_diff, 1000000000ULL, timer_diff);
|
||||||
|
return tsc_freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace X64 {
|
||||||
|
NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency,
|
||||||
|
u64 rtsc_frequency)
|
||||||
|
: WallClock(emulated_cpu_frequency, emulated_clock_frequency, true), rtsc_frequency{
|
||||||
|
rtsc_frequency} {
|
||||||
|
_mm_mfence();
|
||||||
|
last_measure = __rdtsc();
|
||||||
|
accumulated_ticks = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NativeClock::GetRTSC() {
|
||||||
|
rtsc_serialize.lock();
|
||||||
|
_mm_mfence();
|
||||||
|
const u64 current_measure = __rdtsc();
|
||||||
|
u64 diff = current_measure - last_measure;
|
||||||
|
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
|
||||||
|
if (current_measure > last_measure) {
|
||||||
|
last_measure = current_measure;
|
||||||
|
}
|
||||||
|
accumulated_ticks += diff;
|
||||||
|
rtsc_serialize.unlock();
|
||||||
|
return accumulated_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::nanoseconds NativeClock::GetTimeNS() {
|
||||||
|
const u64 rtsc_value = GetRTSC();
|
||||||
|
return std::chrono::nanoseconds{umuldiv64(rtsc_value, 1000000000, rtsc_frequency)};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::microseconds NativeClock::GetTimeUS() {
|
||||||
|
const u64 rtsc_value = GetRTSC();
|
||||||
|
return std::chrono::microseconds{umuldiv64(rtsc_value, 1000000, rtsc_frequency)};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::milliseconds NativeClock::GetTimeMS() {
|
||||||
|
const u64 rtsc_value = GetRTSC();
|
||||||
|
return std::chrono::milliseconds{umuldiv64(rtsc_value, 1000, rtsc_frequency)};
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NativeClock::GetClockCycles() {
|
||||||
|
const u64 rtsc_value = GetRTSC();
|
||||||
|
return umuldiv64(rtsc_value, emulated_clock_frequency, rtsc_frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NativeClock::GetCPUCycles() {
|
||||||
|
const u64 rtsc_value = GetRTSC();
|
||||||
|
return umuldiv64(rtsc_value, emulated_cpu_frequency, rtsc_frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace X64
|
||||||
|
|
||||||
|
} // namespace Common
|
41
src/common/x64/native_clock.h
Normal file
41
src/common/x64/native_clock.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "common/spin_lock.h"
|
||||||
|
#include "common/wall_clock.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
namespace X64 {
|
||||||
|
class NativeClock : public WallClock {
|
||||||
|
public:
|
||||||
|
NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency);
|
||||||
|
|
||||||
|
std::chrono::nanoseconds GetTimeNS() override;
|
||||||
|
|
||||||
|
std::chrono::microseconds GetTimeUS() override;
|
||||||
|
|
||||||
|
std::chrono::milliseconds GetTimeMS() override;
|
||||||
|
|
||||||
|
u64 GetClockCycles() override;
|
||||||
|
|
||||||
|
u64 GetCPUCycles() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
u64 GetRTSC();
|
||||||
|
|
||||||
|
SpinLock rtsc_serialize{};
|
||||||
|
u64 last_measure{};
|
||||||
|
u64 accumulated_ticks{};
|
||||||
|
u64 rtsc_frequency;
|
||||||
|
};
|
||||||
|
} // namespace X64
|
||||||
|
|
||||||
|
u64 EstimateRDTSCFrequency();
|
||||||
|
|
||||||
|
} // namespace Common
|
|
@ -35,7 +35,11 @@ struct CoreTiming::Event {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CoreTiming::CoreTiming() = default;
|
CoreTiming::CoreTiming() {
|
||||||
|
Common::WallClock* wall = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ);
|
||||||
|
clock = std::unique_ptr<Common::WallClock>(wall);
|
||||||
|
}
|
||||||
|
|
||||||
CoreTiming::~CoreTiming() = default;
|
CoreTiming::~CoreTiming() = default;
|
||||||
|
|
||||||
void CoreTiming::ThreadEntry(CoreTiming& instance) {
|
void CoreTiming::ThreadEntry(CoreTiming& instance) {
|
||||||
|
@ -46,7 +50,6 @@ void CoreTiming::Initialize() {
|
||||||
event_fifo_id = 0;
|
event_fifo_id = 0;
|
||||||
const auto empty_timed_callback = [](u64, s64) {};
|
const auto empty_timed_callback = [](u64, s64) {};
|
||||||
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
|
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
|
||||||
start_time = std::chrono::steady_clock::now();
|
|
||||||
timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
|
timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,13 +111,11 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 CoreTiming::GetCPUTicks() const {
|
u64 CoreTiming::GetCPUTicks() const {
|
||||||
std::chrono::nanoseconds time_now = GetGlobalTimeNs();
|
return clock->GetCPUCycles();
|
||||||
return Core::Timing::nsToCycles(time_now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 CoreTiming::GetClockTicks() const {
|
u64 CoreTiming::GetClockTicks() const {
|
||||||
std::chrono::nanoseconds time_now = GetGlobalTimeNs();
|
return clock->GetClockCycles();
|
||||||
return Core::Timing::nsToClockCycles(time_now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::ClearPendingEvents() {
|
void CoreTiming::ClearPendingEvents() {
|
||||||
|
@ -174,15 +175,11 @@ void CoreTiming::Advance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
|
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
|
||||||
sys_time_point current = std::chrono::steady_clock::now();
|
return clock->GetTimeNS();
|
||||||
auto elapsed = current - start_time;
|
|
||||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
|
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
|
||||||
sys_time_point current = std::chrono::steady_clock::now();
|
return clock->GetTimeUS();
|
||||||
auto elapsed = current - start_time;
|
|
||||||
return std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Core::Timing
|
} // namespace Core::Timing
|
||||||
|
|
|
@ -17,12 +17,12 @@
|
||||||
#include "common/spin_lock.h"
|
#include "common/spin_lock.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "common/threadsafe_queue.h"
|
#include "common/threadsafe_queue.h"
|
||||||
|
#include "common/wall_clock.h"
|
||||||
|
|
||||||
namespace Core::HostTiming {
|
namespace Core::HostTiming {
|
||||||
|
|
||||||
/// A callback that may be scheduled for a particular core timing event.
|
/// A callback that may be scheduled for a particular core timing event.
|
||||||
using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>;
|
using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>;
|
||||||
using sys_time_point = std::chrono::time_point<std::chrono::steady_clock>;
|
|
||||||
|
|
||||||
/// Contains the characteristics of a particular event.
|
/// Contains the characteristics of a particular event.
|
||||||
struct EventType {
|
struct EventType {
|
||||||
|
@ -112,7 +112,7 @@ private:
|
||||||
static void ThreadEntry(CoreTiming& instance);
|
static void ThreadEntry(CoreTiming& instance);
|
||||||
void Advance();
|
void Advance();
|
||||||
|
|
||||||
sys_time_point start_time;
|
std::unique_ptr<Common::WallClock> clock;
|
||||||
|
|
||||||
u64 global_timer = 0;
|
u64 global_timer = 0;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
// Numbers are chosen randomly to make sure the correct one is given.
|
// Numbers are chosen randomly to make sure the correct one is given.
|
||||||
static constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
|
static constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
|
||||||
static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals
|
static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals
|
||||||
static constexpr std::array<u64, 5> calls_order{{2,0,1,4,3}};
|
static constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}};
|
||||||
static std::array<s64, 5> delays{};
|
static std::array<s64, 5> delays{};
|
||||||
|
|
||||||
static std::bitset<CB_IDS.size()> callbacks_ran_flags;
|
static std::bitset<CB_IDS.size()> callbacks_ran_flags;
|
||||||
|
@ -52,16 +52,11 @@ TEST_CASE("HostTiming[BasicOrder]", "[core]") {
|
||||||
auto& core_timing = guard.core_timing;
|
auto& core_timing = guard.core_timing;
|
||||||
std::vector<std::shared_ptr<Core::HostTiming::EventType>> events;
|
std::vector<std::shared_ptr<Core::HostTiming::EventType>> events;
|
||||||
events.resize(5);
|
events.resize(5);
|
||||||
events[0] =
|
events[0] = Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>);
|
||||||
Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>);
|
events[1] = Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>);
|
||||||
events[1] =
|
events[2] = Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>);
|
||||||
Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>);
|
events[3] = Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>);
|
||||||
events[2] =
|
events[4] = Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>);
|
||||||
Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>);
|
|
||||||
events[3] =
|
|
||||||
Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>);
|
|
||||||
events[4] =
|
|
||||||
Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>);
|
|
||||||
|
|
||||||
expected_callback = 0;
|
expected_callback = 0;
|
||||||
|
|
||||||
|
@ -70,14 +65,15 @@ TEST_CASE("HostTiming[BasicOrder]", "[core]") {
|
||||||
u64 one_micro = 1000U;
|
u64 one_micro = 1000U;
|
||||||
for (std::size_t i = 0; i < events.size(); i++) {
|
for (std::size_t i = 0; i < events.size(); i++) {
|
||||||
u64 order = calls_order[i];
|
u64 order = calls_order[i];
|
||||||
core_timing.ScheduleEvent(i*one_micro + 100U, events[order], CB_IDS[order]);
|
core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]);
|
||||||
}
|
}
|
||||||
/// test pause
|
/// test pause
|
||||||
REQUIRE(callbacks_ran_flags.none());
|
REQUIRE(callbacks_ran_flags.none());
|
||||||
|
|
||||||
core_timing.Pause(false); // No need to sync
|
core_timing.Pause(false); // No need to sync
|
||||||
|
|
||||||
while (core_timing.HasPendingEvents());
|
while (core_timing.HasPendingEvents())
|
||||||
|
;
|
||||||
|
|
||||||
REQUIRE(callbacks_ran_flags.all());
|
REQUIRE(callbacks_ran_flags.all());
|
||||||
|
|
||||||
|
@ -106,16 +102,11 @@ TEST_CASE("HostTiming[BasicOrderNoPausing]", "[core]") {
|
||||||
auto& core_timing = guard.core_timing;
|
auto& core_timing = guard.core_timing;
|
||||||
std::vector<std::shared_ptr<Core::HostTiming::EventType>> events;
|
std::vector<std::shared_ptr<Core::HostTiming::EventType>> events;
|
||||||
events.resize(5);
|
events.resize(5);
|
||||||
events[0] =
|
events[0] = Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>);
|
||||||
Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>);
|
events[1] = Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>);
|
||||||
events[1] =
|
events[2] = Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>);
|
||||||
Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>);
|
events[3] = Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>);
|
||||||
events[2] =
|
events[4] = Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>);
|
||||||
Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>);
|
|
||||||
events[3] =
|
|
||||||
Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>);
|
|
||||||
events[4] =
|
|
||||||
Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>);
|
|
||||||
|
|
||||||
core_timing.SyncPause(true);
|
core_timing.SyncPause(true);
|
||||||
core_timing.SyncPause(false);
|
core_timing.SyncPause(false);
|
||||||
|
@ -126,13 +117,14 @@ TEST_CASE("HostTiming[BasicOrderNoPausing]", "[core]") {
|
||||||
u64 one_micro = 1000U;
|
u64 one_micro = 1000U;
|
||||||
for (std::size_t i = 0; i < events.size(); i++) {
|
for (std::size_t i = 0; i < events.size(); i++) {
|
||||||
u64 order = calls_order[i];
|
u64 order = calls_order[i];
|
||||||
core_timing.ScheduleEvent(i*one_micro + 100U, events[order], CB_IDS[order]);
|
core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]);
|
||||||
}
|
}
|
||||||
u64 end = core_timing.GetGlobalTimeNs().count();
|
u64 end = core_timing.GetGlobalTimeNs().count();
|
||||||
const double scheduling_time = static_cast<double>(end - start);
|
const double scheduling_time = static_cast<double>(end - start);
|
||||||
const double timer_time = static_cast<double>(TestTimerSpeed(core_timing));
|
const double timer_time = static_cast<double>(TestTimerSpeed(core_timing));
|
||||||
|
|
||||||
while (core_timing.HasPendingEvents());
|
while (core_timing.HasPendingEvents())
|
||||||
|
;
|
||||||
|
|
||||||
REQUIRE(callbacks_ran_flags.all());
|
REQUIRE(callbacks_ran_flags.all());
|
||||||
|
|
||||||
|
@ -146,5 +138,6 @@ TEST_CASE("HostTiming[BasicOrderNoPausing]", "[core]") {
|
||||||
const double micro = scheduling_time / 1000.0f;
|
const double micro = scheduling_time / 1000.0f;
|
||||||
const double mili = micro / 1000.0f;
|
const double mili = micro / 1000.0f;
|
||||||
printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili);
|
printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili);
|
||||||
printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f, timer_time / 1000000.f);
|
printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f,
|
||||||
|
timer_time / 1000000.f);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue