| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | // Copyright 2020 yuzu Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-01 23:28:55 +01:00
										 |  |  | #include <array>
 | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | #include <chrono>
 | 
					
						
							| 
									
										
										
										
											2021-01-01 23:28:55 +01:00
										 |  |  | #include <limits>
 | 
					
						
							| 
									
										
										
										
											2020-06-27 18:20:06 -04:00
										 |  |  | #include <mutex>
 | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | #include <thread>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-02 02:24:49 +01:00
										 |  |  | #include "common/atomic_ops.h"
 | 
					
						
							| 
									
										
										
										
											2020-02-10 11:20:40 -04:00
										 |  |  | #include "common/uint128.h"
 | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | #include "common/x64/native_clock.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Common { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							| 
									
										
										
										
											2020-02-10 11:20:40 -04:00
										 |  |  |     const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  |     return tsc_freq; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace X64 { | 
					
						
							| 
									
										
										
										
											2020-11-25 15:21:03 -05:00
										 |  |  | 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_} { | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  |     _mm_mfence(); | 
					
						
							| 
									
										
										
										
											2021-01-02 02:24:49 +01:00
										 |  |  |     time_point.inner.last_measure = __rdtsc(); | 
					
						
							|  |  |  |     time_point.inner.accumulated_ticks = 0U; | 
					
						
							| 
									
										
										
										
											2021-01-01 23:28:55 +01:00
										 |  |  |     ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency); | 
					
						
							|  |  |  |     us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency); | 
					
						
							|  |  |  |     ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency); | 
					
						
							|  |  |  |     clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency); | 
					
						
							|  |  |  |     cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency); | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u64 NativeClock::GetRTSC() { | 
					
						
							| 
									
										
										
										
											2021-01-02 02:24:49 +01:00
										 |  |  |     TimePoint new_time_point{}; | 
					
						
							|  |  |  |     TimePoint current_time_point{}; | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |         current_time_point.pack = time_point.pack; | 
					
						
							|  |  |  |         _mm_mfence(); | 
					
						
							|  |  |  |         const u64 current_measure = __rdtsc(); | 
					
						
							|  |  |  |         u64 diff = current_measure - current_time_point.inner.last_measure; | 
					
						
							|  |  |  |         diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
 | 
					
						
							|  |  |  |         new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure | 
					
						
							|  |  |  |                                                 ? current_measure | 
					
						
							|  |  |  |                                                 : current_time_point.inner.last_measure; | 
					
						
							|  |  |  |         new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; | 
					
						
							|  |  |  |     } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | 
					
						
							|  |  |  |                                            current_time_point.pack)); | 
					
						
							| 
									
										
										
										
											2020-03-21 12:23:13 -04:00
										 |  |  |     /// The clock cannot be more precise than the guest timer, remove the lower bits
 | 
					
						
							| 
									
										
										
										
											2021-01-02 02:24:49 +01:00
										 |  |  |     return new_time_point.inner.accumulated_ticks & inaccuracy_mask; | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 12:28:55 -04:00
										 |  |  | void NativeClock::Pause(bool is_paused) { | 
					
						
							|  |  |  |     if (!is_paused) { | 
					
						
							| 
									
										
										
										
											2021-01-02 02:24:49 +01:00
										 |  |  |         TimePoint current_time_point{}; | 
					
						
							|  |  |  |         TimePoint new_time_point{}; | 
					
						
							|  |  |  |         do { | 
					
						
							|  |  |  |             current_time_point.pack = time_point.pack; | 
					
						
							|  |  |  |             new_time_point.pack = current_time_point.pack; | 
					
						
							|  |  |  |             _mm_mfence(); | 
					
						
							|  |  |  |             new_time_point.inner.last_measure = __rdtsc(); | 
					
						
							|  |  |  |         } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | 
					
						
							|  |  |  |                                                current_time_point.pack)); | 
					
						
							| 
									
										
										
										
											2020-02-25 12:28:55 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | std::chrono::nanoseconds NativeClock::GetTimeNS() { | 
					
						
							|  |  |  |     const u64 rtsc_value = GetRTSC(); | 
					
						
							| 
									
										
										
										
											2021-01-01 23:28:55 +01:00
										 |  |  |     return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)}; | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::chrono::microseconds NativeClock::GetTimeUS() { | 
					
						
							|  |  |  |     const u64 rtsc_value = GetRTSC(); | 
					
						
							| 
									
										
										
										
											2021-01-01 23:28:55 +01:00
										 |  |  |     return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)}; | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::chrono::milliseconds NativeClock::GetTimeMS() { | 
					
						
							|  |  |  |     const u64 rtsc_value = GetRTSC(); | 
					
						
							| 
									
										
										
										
											2021-01-01 23:28:55 +01:00
										 |  |  |     return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)}; | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u64 NativeClock::GetClockCycles() { | 
					
						
							|  |  |  |     const u64 rtsc_value = GetRTSC(); | 
					
						
							| 
									
										
										
										
											2021-01-01 23:28:55 +01:00
										 |  |  |     return MultiplyHigh(rtsc_value, clock_rtsc_factor); | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u64 NativeClock::GetCPUCycles() { | 
					
						
							|  |  |  |     const u64 rtsc_value = GetRTSC(); | 
					
						
							| 
									
										
										
										
											2021-01-01 23:28:55 +01:00
										 |  |  |     return MultiplyHigh(rtsc_value, cpu_rtsc_factor); | 
					
						
							| 
									
										
										
										
											2020-02-09 16:53:22 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // namespace X64
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // namespace Common
 |