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. const auto milli_10 = std::chrono::milliseconds{10};
  15. // get current time
  16. _mm_mfence();
  17. const u64 tscStart = __rdtsc();
  18. const auto startTime = std::chrono::high_resolution_clock::now();
  19. // wait roughly 3 seconds
  20. while (true) {
  21. auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
  22. std::chrono::high_resolution_clock::now() - startTime);
  23. if (milli.count() >= 3000)
  24. break;
  25. std::this_thread::sleep_for(milli_10);
  26. }
  27. const auto endTime = std::chrono::high_resolution_clock::now();
  28. _mm_mfence();
  29. const u64 tscEnd = __rdtsc();
  30. // calculate difference
  31. const u64 timer_diff =
  32. std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
  33. const u64 tsc_diff = tscEnd - tscStart;
  34. const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
  35. return tsc_freq;
  36. }
  37. namespace X64 {
  38. NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_,
  39. u64 rtsc_frequency_)
  40. : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
  41. rtsc_frequency_} {
  42. _mm_mfence();
  43. time_point.inner.last_measure = __rdtsc();
  44. time_point.inner.accumulated_ticks = 0U;
  45. ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency);
  46. us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency);
  47. ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency);
  48. clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
  49. cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
  50. }
  51. u64 NativeClock::GetRTSC() {
  52. TimePoint new_time_point{};
  53. TimePoint current_time_point{};
  54. do {
  55. current_time_point.pack = time_point.pack;
  56. _mm_mfence();
  57. const u64 current_measure = __rdtsc();
  58. u64 diff = current_measure - current_time_point.inner.last_measure;
  59. diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
  60. new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
  61. ? current_measure
  62. : current_time_point.inner.last_measure;
  63. new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
  64. } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
  65. current_time_point.pack));
  66. /// The clock cannot be more precise than the guest timer, remove the lower bits
  67. return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
  68. }
  69. void NativeClock::Pause(bool is_paused) {
  70. if (!is_paused) {
  71. TimePoint current_time_point{};
  72. TimePoint new_time_point{};
  73. do {
  74. current_time_point.pack = time_point.pack;
  75. new_time_point.pack = current_time_point.pack;
  76. _mm_mfence();
  77. new_time_point.inner.last_measure = __rdtsc();
  78. } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
  79. current_time_point.pack));
  80. }
  81. }
  82. std::chrono::nanoseconds NativeClock::GetTimeNS() {
  83. const u64 rtsc_value = GetRTSC();
  84. return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
  85. }
  86. std::chrono::microseconds NativeClock::GetTimeUS() {
  87. const u64 rtsc_value = GetRTSC();
  88. return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
  89. }
  90. std::chrono::milliseconds NativeClock::GetTimeMS() {
  91. const u64 rtsc_value = GetRTSC();
  92. return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
  93. }
  94. u64 NativeClock::GetClockCycles() {
  95. const u64 rtsc_value = GetRTSC();
  96. return MultiplyHigh(rtsc_value, clock_rtsc_factor);
  97. }
  98. u64 NativeClock::GetCPUCycles() {
  99. const u64 rtsc_value = GetRTSC();
  100. return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
  101. }
  102. } // namespace X64
  103. } // namespace Common