native_clock.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. // Copyright 2020 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <array>
  5. #include <chrono>
  6. #include <limits>
  7. #include <mutex>
  8. #include <thread>
  9. #include "common/atomic_ops.h"
  10. #include "common/uint128.h"
  11. #include "common/x64/native_clock.h"
  12. namespace Common {
  13. u64 EstimateRDTSCFrequency() {
  14. // Discard the first result measuring the rdtsc.
  15. _mm_mfence();
  16. __rdtsc();
  17. std::this_thread::sleep_for(std::chrono::milliseconds{1});
  18. _mm_mfence();
  19. __rdtsc();
  20. // Get the current time.
  21. const auto start_time = std::chrono::steady_clock::now();
  22. _mm_mfence();
  23. const u64 tsc_start = __rdtsc();
  24. // Wait for 200 milliseconds.
  25. std::this_thread::sleep_for(std::chrono::milliseconds{200});
  26. const auto end_time = std::chrono::steady_clock::now();
  27. _mm_mfence();
  28. const u64 tsc_end = __rdtsc();
  29. // Calculate differences.
  30. const u64 timer_diff = static_cast<u64>(
  31. std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
  32. const u64 tsc_diff = tsc_end - tsc_start;
  33. const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
  34. return tsc_freq;
  35. }
  36. namespace X64 {
  37. NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_,
  38. u64 rtsc_frequency_)
  39. : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
  40. rtsc_frequency_} {
  41. _mm_mfence();
  42. time_point.inner.last_measure = __rdtsc();
  43. time_point.inner.accumulated_ticks = 0U;
  44. ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency);
  45. us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency);
  46. ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency);
  47. clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
  48. cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
  49. }
  50. u64 NativeClock::GetRTSC() {
  51. TimePoint new_time_point{};
  52. TimePoint current_time_point{};
  53. do {
  54. current_time_point.pack = time_point.pack;
  55. _mm_mfence();
  56. const u64 current_measure = __rdtsc();
  57. u64 diff = current_measure - current_time_point.inner.last_measure;
  58. diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
  59. new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
  60. ? current_measure
  61. : current_time_point.inner.last_measure;
  62. new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
  63. } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
  64. current_time_point.pack));
  65. /// The clock cannot be more precise than the guest timer, remove the lower bits
  66. return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
  67. }
  68. void NativeClock::Pause(bool is_paused) {
  69. if (!is_paused) {
  70. TimePoint current_time_point{};
  71. TimePoint new_time_point{};
  72. do {
  73. current_time_point.pack = time_point.pack;
  74. new_time_point.pack = current_time_point.pack;
  75. _mm_mfence();
  76. new_time_point.inner.last_measure = __rdtsc();
  77. } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
  78. current_time_point.pack));
  79. }
  80. }
  81. std::chrono::nanoseconds NativeClock::GetTimeNS() {
  82. const u64 rtsc_value = GetRTSC();
  83. return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
  84. }
  85. std::chrono::microseconds NativeClock::GetTimeUS() {
  86. const u64 rtsc_value = GetRTSC();
  87. return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
  88. }
  89. std::chrono::milliseconds NativeClock::GetTimeMS() {
  90. const u64 rtsc_value = GetRTSC();
  91. return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
  92. }
  93. u64 NativeClock::GetClockCycles() {
  94. const u64 rtsc_value = GetRTSC();
  95. return MultiplyHigh(rtsc_value, clock_rtsc_factor);
  96. }
  97. u64 NativeClock::GetCPUCycles() {
  98. const u64 rtsc_value = GetRTSC();
  99. return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
  100. }
  101. } // namespace X64
  102. } // namespace Common