native_clock.cpp 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // Copyright 2020 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <chrono>
  5. #include <mutex>
  6. #include <thread>
  7. #ifdef _MSC_VER
  8. #include <intrin.h>
  9. #else
  10. #include <x86intrin.h>
  11. #endif
  12. #include "common/uint128.h"
  13. #include "common/x64/native_clock.h"
  14. namespace Common {
  15. u64 EstimateRDTSCFrequency() {
  16. const auto milli_10 = std::chrono::milliseconds{10};
  17. // get current time
  18. _mm_mfence();
  19. const u64 tscStart = __rdtsc();
  20. const auto startTime = std::chrono::high_resolution_clock::now();
  21. // wait roughly 3 seconds
  22. while (true) {
  23. auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
  24. std::chrono::high_resolution_clock::now() - startTime);
  25. if (milli.count() >= 3000)
  26. break;
  27. std::this_thread::sleep_for(milli_10);
  28. }
  29. const auto endTime = std::chrono::high_resolution_clock::now();
  30. _mm_mfence();
  31. const u64 tscEnd = __rdtsc();
  32. // calculate difference
  33. const u64 timer_diff =
  34. std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
  35. const u64 tsc_diff = tscEnd - tscStart;
  36. const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
  37. return tsc_freq;
  38. }
  39. namespace X64 {
  40. NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_,
  41. u64 rtsc_frequency_)
  42. : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
  43. rtsc_frequency_} {
  44. _mm_mfence();
  45. last_measure = __rdtsc();
  46. accumulated_ticks = 0U;
  47. }
  48. u64 NativeClock::GetRTSC() {
  49. std::scoped_lock scope{rtsc_serialize};
  50. _mm_mfence();
  51. const u64 current_measure = __rdtsc();
  52. u64 diff = current_measure - last_measure;
  53. diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
  54. if (current_measure > last_measure) {
  55. last_measure = current_measure;
  56. }
  57. accumulated_ticks += diff;
  58. /// The clock cannot be more precise than the guest timer, remove the lower bits
  59. return accumulated_ticks & inaccuracy_mask;
  60. }
  61. void NativeClock::Pause(bool is_paused) {
  62. if (!is_paused) {
  63. _mm_mfence();
  64. last_measure = __rdtsc();
  65. }
  66. }
  67. std::chrono::nanoseconds NativeClock::GetTimeNS() {
  68. const u64 rtsc_value = GetRTSC();
  69. return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)};
  70. }
  71. std::chrono::microseconds NativeClock::GetTimeUS() {
  72. const u64 rtsc_value = GetRTSC();
  73. return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)};
  74. }
  75. std::chrono::milliseconds NativeClock::GetTimeMS() {
  76. const u64 rtsc_value = GetRTSC();
  77. return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)};
  78. }
  79. u64 NativeClock::GetClockCycles() {
  80. const u64 rtsc_value = GetRTSC();
  81. return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency);
  82. }
  83. u64 NativeClock::GetCPUCycles() {
  84. const u64 rtsc_value = GetRTSC();
  85. return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency);
  86. }
  87. } // namespace X64
  88. } // namespace Common