mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	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 New Issue
	
	Block a user
	 Fernando Sahmkow
						Fernando Sahmkow