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 <thread>
  6. #ifdef _MSC_VER
  7. #include <intrin.h>
  8. #else
  9. #include <x86intrin.h>
  10. #endif
  11. #include "common/uint128.h"
  12. #include "common/x64/native_clock.h"
  13. namespace Common {
  14. u64 EstimateRDTSCFrequency() {
  15. const auto milli_10 = std::chrono::milliseconds{10};
  16. // get current time
  17. _mm_mfence();
  18. const u64 tscStart = __rdtsc();
  19. const auto startTime = std::chrono::high_resolution_clock::now();
  20. // wait roughly 3 seconds
  21. while (true) {
  22. auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
  23. std::chrono::high_resolution_clock::now() - startTime);
  24. if (milli.count() >= 3000)
  25. break;
  26. std::this_thread::sleep_for(milli_10);
  27. }
  28. const auto endTime = std::chrono::high_resolution_clock::now();
  29. _mm_mfence();
  30. const u64 tscEnd = __rdtsc();
  31. // calculate difference
  32. const u64 timer_diff =
  33. std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
  34. const u64 tsc_diff = tscEnd - tscStart;
  35. const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
  36. return tsc_freq;
  37. }
  38. namespace X64 {
  39. NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency,
  40. u64 rtsc_frequency)
  41. : WallClock(emulated_cpu_frequency, emulated_clock_frequency, true), rtsc_frequency{
  42. rtsc_frequency} {
  43. _mm_mfence();
  44. last_measure = __rdtsc();
  45. accumulated_ticks = 0U;
  46. }
  47. u64 NativeClock::GetRTSC() {
  48. rtsc_serialize.lock();
  49. _mm_mfence();
  50. const u64 current_measure = __rdtsc();
  51. u64 diff = current_measure - last_measure;
  52. diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
  53. if (current_measure > last_measure) {
  54. last_measure = current_measure;
  55. }
  56. accumulated_ticks += diff;
  57. rtsc_serialize.unlock();
  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