duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

timer.cpp (9162B)


      1 // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "timer.h"
      5 #include "types.h"
      6 #include <cstdio>
      7 #include <cstdlib>
      8 
      9 #ifdef _WIN32
     10 #include "windows_headers.h"
     11 #else
     12 #include <errno.h>
     13 #include <sys/time.h>
     14 #include <time.h>
     15 #include <unistd.h>
     16 #endif
     17 
     18 namespace Common {
     19 
     20 #ifdef _WIN32
     21 
     22 static double s_counter_frequency;
     23 static bool s_counter_initialized = false;
     24 
     25 // This gets leaked... oh well.
     26 static thread_local HANDLE s_sleep_timer;
     27 static thread_local bool s_sleep_timer_created = false;
     28 
     29 static HANDLE GetSleepTimer()
     30 {
     31   if (s_sleep_timer_created)
     32     return s_sleep_timer;
     33 
     34   s_sleep_timer_created = true;
     35   s_sleep_timer = CreateWaitableTimerEx(nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
     36   if (!s_sleep_timer)
     37   {
     38     s_sleep_timer = CreateWaitableTimer(nullptr, TRUE, nullptr);
     39     if (!s_sleep_timer)
     40       std::fprintf(stderr, "CreateWaitableTimer() failed, falling back to Sleep()\n");
     41   }
     42 
     43   return s_sleep_timer;
     44 }
     45 
     46 double Timer::GetFrequency()
     47 {
     48   // even if this races, it should still result in the same value..
     49   if (!s_counter_initialized)
     50   {
     51     LARGE_INTEGER Freq;
     52     QueryPerformanceFrequency(&Freq);
     53     s_counter_frequency = static_cast<double>(Freq.QuadPart) / 1000000000.0;
     54     s_counter_initialized = true;
     55   }
     56 
     57   return s_counter_frequency;
     58 }
     59 
     60 Timer::Value Timer::GetCurrentValue()
     61 {
     62   Timer::Value ReturnValue;
     63   QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&ReturnValue));
     64   return ReturnValue;
     65 }
     66 
     67 double Timer::ConvertValueToNanoseconds(Timer::Value value)
     68 {
     69   return (static_cast<double>(value) / GetFrequency());
     70 }
     71 
     72 double Timer::ConvertValueToMilliseconds(Timer::Value value)
     73 {
     74   return ((static_cast<double>(value) / GetFrequency()) / 1000000.0);
     75 }
     76 
     77 double Timer::ConvertValueToSeconds(Timer::Value value)
     78 {
     79   return ((static_cast<double>(value) / GetFrequency()) / 1000000000.0);
     80 }
     81 
     82 Timer::Value Timer::ConvertSecondsToValue(double s)
     83 {
     84   return static_cast<Value>((s * 1000000000.0) * GetFrequency());
     85 }
     86 
     87 Timer::Value Timer::ConvertMillisecondsToValue(double ms)
     88 {
     89   return static_cast<Value>((ms * 1000000.0) * GetFrequency());
     90 }
     91 
     92 Timer::Value Timer::ConvertNanosecondsToValue(double ns)
     93 {
     94   return static_cast<Value>(ns * GetFrequency());
     95 }
     96 
     97 void Timer::SleepUntil(Value value, bool exact)
     98 {
     99   if (exact)
    100   {
    101     // Even with the high-precision timer, it's not precise enough to wake us up *exactly* when we want
    102     // to. Dropping off the last 0.5ms and spinning for it seems enough on my system (Win11 22H2).
    103     const Value wake_at = value - ConvertMillisecondsToValue(0.5);
    104     Value current = GetCurrentValue();
    105     if (wake_at > current)
    106       SleepUntil(wake_at, false);
    107 
    108     // And spin off whatever time is left.
    109     do
    110     {
    111       current = GetCurrentValue();
    112     } while (current < value);
    113   }
    114   else
    115   {
    116     const s64 diff = static_cast<s64>(value - GetCurrentValue());
    117     if (diff <= 0)
    118       return;
    119 
    120     HANDLE timer = GetSleepTimer();
    121     if (timer)
    122     {
    123       const u64 one_hundred_nanos_diff = static_cast<u64>(ConvertValueToNanoseconds(diff) / 100.0);
    124       if (one_hundred_nanos_diff == 0)
    125         return;
    126 
    127       LARGE_INTEGER fti;
    128       fti.QuadPart = -static_cast<s64>(one_hundred_nanos_diff);
    129 
    130       if (SetWaitableTimer(timer, &fti, 0, nullptr, nullptr, FALSE))
    131       {
    132         WaitForSingleObject(timer, INFINITE);
    133         return;
    134       }
    135     }
    136 
    137     // falling back to sleep... bad.
    138     Sleep(static_cast<DWORD>(static_cast<u64>(diff) / 1000000));
    139   }
    140 }
    141 
    142 #else
    143 
    144 double Timer::GetFrequency()
    145 {
    146   return 1.0;
    147 }
    148 
    149 Timer::Value Timer::GetCurrentValue()
    150 {
    151   struct timespec tv;
    152   clock_gettime(CLOCK_MONOTONIC, &tv);
    153   return ((Value)tv.tv_nsec + (Value)tv.tv_sec * 1000000000);
    154 }
    155 
    156 double Timer::ConvertValueToNanoseconds(Timer::Value value)
    157 {
    158   return static_cast<double>(value);
    159 }
    160 
    161 double Timer::ConvertValueToMilliseconds(Timer::Value value)
    162 {
    163   return (static_cast<double>(value) / 1000000.0);
    164 }
    165 
    166 double Timer::ConvertValueToSeconds(Timer::Value value)
    167 {
    168   return (static_cast<double>(value) / 1000000000.0);
    169 }
    170 
    171 Timer::Value Timer::ConvertSecondsToValue(double s)
    172 {
    173   return static_cast<Value>(s * 1000000000.0);
    174 }
    175 
    176 Timer::Value Timer::ConvertMillisecondsToValue(double ms)
    177 {
    178   return static_cast<Value>(ms * 1000000.0);
    179 }
    180 
    181 Timer::Value Timer::ConvertNanosecondsToValue(double ns)
    182 {
    183   return static_cast<Value>(ns);
    184 }
    185 
    186 void Timer::SleepUntil(Value value, bool exact)
    187 {
    188   if (exact)
    189   {
    190     static constexpr Value min_sleep_time = static_cast<Value>(0.5 * 1000000);
    191     const Value wake_at = value - min_sleep_time;
    192     Value current = GetCurrentValue();
    193     if (wake_at > current)
    194       SleepUntil(wake_at, false);
    195 
    196     // And spin off whatever time is left.
    197     do
    198     {
    199       current = GetCurrentValue();
    200     } while (current < value);
    201   }
    202   else
    203   {
    204     // Apple doesn't have TIMER_ABSTIME, so fall back to nanosleep in such a case.
    205 #ifdef __APPLE__
    206     for (;;)
    207     {
    208       const Value current_time = GetCurrentValue();
    209       if (value <= current_time)
    210         return;
    211 
    212       const Value diff = value - current_time;
    213       struct timespec ts;
    214       ts.tv_sec = diff / UINT64_C(1000000000);
    215       ts.tv_nsec = diff % UINT64_C(1000000000);
    216 
    217       // nanosleep() can return EINTR if interrupted by a signal.
    218       if (nanosleep(&ts, nullptr) == EINTR)
    219         continue;
    220       else
    221         break;
    222     }
    223 #else
    224     struct timespec ts;
    225     ts.tv_sec = value / UINT64_C(1000000000);
    226     ts.tv_nsec = value % UINT64_C(1000000000);
    227 
    228     for (;;)
    229     {
    230       // clock_nanosleep() can return EINTR if interrupted by a signal.
    231       if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr) == EINTR)
    232         continue;
    233       else
    234         break;
    235     }
    236 #endif
    237   }
    238 }
    239 
    240 #endif
    241 
    242 Timer::Timer()
    243 {
    244   Reset();
    245 }
    246 
    247 void Timer::Reset()
    248 {
    249   m_tvStartValue = GetCurrentValue();
    250 }
    251 
    252 double Timer::GetTimeSeconds() const
    253 {
    254   return ConvertValueToSeconds(GetCurrentValue() - m_tvStartValue);
    255 }
    256 
    257 double Timer::GetTimeMilliseconds() const
    258 {
    259   return ConvertValueToMilliseconds(GetCurrentValue() - m_tvStartValue);
    260 }
    261 
    262 double Timer::GetTimeNanoseconds() const
    263 {
    264   return ConvertValueToNanoseconds(GetCurrentValue() - m_tvStartValue);
    265 }
    266 
    267 double Timer::GetTimeSecondsAndReset()
    268 {
    269   const Value value = GetCurrentValue();
    270   const double ret = ConvertValueToSeconds(value - m_tvStartValue);
    271   m_tvStartValue = value;
    272   return ret;
    273 }
    274 
    275 double Timer::GetTimeMillisecondsAndReset()
    276 {
    277   const Value value = GetCurrentValue();
    278   const double ret = ConvertValueToMilliseconds(value - m_tvStartValue);
    279   m_tvStartValue = value;
    280   return ret;
    281 }
    282 
    283 double Timer::GetTimeNanosecondsAndReset()
    284 {
    285   const Value value = GetCurrentValue();
    286   const double ret = ConvertValueToNanoseconds(value - m_tvStartValue);
    287   m_tvStartValue = value;
    288   return ret;
    289 }
    290 
    291 bool Timer::ResetIfSecondsPassed(double s)
    292 {
    293   const Value value = GetCurrentValue();
    294   const double ret = ConvertValueToSeconds(value - m_tvStartValue);
    295   if (ret < s)
    296     return false;
    297 
    298   m_tvStartValue = value;
    299   return true;
    300 }
    301 
    302 bool Timer::ResetIfMillisecondsPassed(double s)
    303 {
    304   const Value value = GetCurrentValue();
    305   const double ret = ConvertValueToMilliseconds(value - m_tvStartValue);
    306   if (ret < s)
    307     return false;
    308 
    309   m_tvStartValue = value;
    310   return true;
    311 }
    312 
    313 bool Timer::ResetIfNanosecondsPassed(double s)
    314 {
    315   const Value value = GetCurrentValue();
    316   const double ret = ConvertValueToNanoseconds(value - m_tvStartValue);
    317   if (ret < s)
    318     return false;
    319 
    320   m_tvStartValue = value;
    321   return true;
    322 }
    323 
    324 void Timer::BusyWait(std::uint64_t ns)
    325 {
    326   const Value start = GetCurrentValue();
    327   const Value end = start + ConvertNanosecondsToValue(static_cast<double>(ns));
    328   if (end < start)
    329   {
    330     // overflow, unlikely
    331     while (GetCurrentValue() > end)
    332       ;
    333   }
    334 
    335   while (GetCurrentValue() < end)
    336     ;
    337 }
    338 
    339 void Timer::HybridSleep(std::uint64_t ns, std::uint64_t min_sleep_time)
    340 {
    341   const std::uint64_t start = GetCurrentValue();
    342   const std::uint64_t end = start + ConvertNanosecondsToValue(static_cast<double>(ns));
    343   if (end < start)
    344   {
    345     // overflow, unlikely
    346     while (GetCurrentValue() > end)
    347       ;
    348   }
    349 
    350   std::uint64_t current = GetCurrentValue();
    351   while (current < end)
    352   {
    353     const std::uint64_t remaining = end - current;
    354     if (remaining >= min_sleep_time)
    355       NanoSleep(min_sleep_time);
    356 
    357     current = GetCurrentValue();
    358   }
    359 }
    360 
    361 void Timer::NanoSleep(std::uint64_t ns)
    362 {
    363 #if defined(_WIN32)
    364   HANDLE timer = GetSleepTimer();
    365   if (timer)
    366   {
    367     LARGE_INTEGER due_time;
    368     due_time.QuadPart = -static_cast<std::int64_t>(static_cast<std::uint64_t>(ns) / 100u);
    369     if (SetWaitableTimer(timer, &due_time, 0, nullptr, nullptr, FALSE))
    370       WaitForSingleObject(timer, INFINITE);
    371     else
    372       std::fprintf(stderr, "SetWaitableTimer() failed: %08X\n", static_cast<unsigned>(GetLastError()));
    373   }
    374   else
    375   {
    376     Sleep(static_cast<std::uint32_t>(ns / 1000000));
    377   }
    378 #elif defined(__ANDROID__)
    379   // Round down to the next millisecond.
    380   usleep(static_cast<useconds_t>((ns / 1000000) * 1000));
    381 #else
    382   const struct timespec ts = {0, static_cast<long>(ns)};
    383   nanosleep(&ts, nullptr);
    384 #endif
    385 }
    386 
    387 } // namespace Common