3#include <ossia/detail/yield.hpp>
8#elif defined(__APPLE__)
10#include <mach/mach_time.h>
11#include <mach/thread_policy.h>
18#include <sys/resource.h>
19#include <sys/syscall.h>
33static const LARGE_INTEGER g_performance_frequency = []() {
35 QueryPerformanceFrequency(&f);
38#elif defined(__APPLE__)
39static const mach_timebase_info_data_t g_timebase_info = []() {
40 mach_timebase_info_data_t tb;
41 mach_timebase_info(&tb);
48inline uint64_t now_ns()
52 QueryPerformanceCounter(&now);
53 return static_cast<uint64_t
>(now.QuadPart * 1'000'000'000ULL / detail::g_performance_frequency.QuadPart);
54#elif defined(__APPLE__)
55 return mach_absolute_time() * detail::g_timebase_info.numer / detail::g_timebase_info.denom;
58 clock_gettime(CLOCK_MONOTONIC, &ts);
59 return static_cast<uint64_t
>(ts.tv_sec) * 1'000'000'000ULL + ts.tv_nsec;
63static void coarse_sleep(int64_t ns)
66 Sleep(
static_cast<DWORD
>(ns / 1'000'000));
67#elif defined(__APPLE__)
68 uint64_t now = mach_absolute_time();
69 uint64_t deltaMach =
static_cast<uint64_t
>(ns) * detail::g_timebase_info.denom / detail::g_timebase_info.numer;
70 mach_wait_until(now + deltaMach);
73 ts.tv_sec = ns / 1'000'000'000LL;
74 ts.tv_nsec = ns % 1'000'000'000LL;
75 clock_nanosleep(CLOCK_MONOTONIC, 0, &ts,
nullptr);
81 static constexpr int64_t max_count = 1000;
82 static constexpr int64_t min_sleep_ns = 1'000'000;
85 void sleep_until(uint64_t target_ns)
87 const uint64_t current = now_ns();
88 if (target_ns <= current)
91 const int64_t remaining =
static_cast<int64_t
>(target_ns - current);
92 const int64_t to_sleep = remaining -
static_cast<int64_t
>(m_margin_ns);
94 if (to_sleep > min_sleep_ns)
96 const uint64_t start_ns = now_ns();
97 coarse_sleep(to_sleep);
98 const uint64_t end_ns = now_ns();
101 const double overshoot =
static_cast<double>(
static_cast<int64_t
>(end_ns - start_ns) - to_sleep);
106 while (now_ns() < target_ns)
108 ossia_rwlock_pause();
113 void update(
double overshoot)
115 if (m_count >= max_count)
117 m_count = max_count / 2;
122 const double delta = overshoot - m_mean_overshoot_ns;
123 m_mean_overshoot_ns += delta /
static_cast<double>(m_count);
124 m_m2 += delta * (overshoot - m_mean_overshoot_ns);
126 const double stddev = (m_count > 1) ? std::sqrt(m_m2 /
static_cast<double>(m_count - 1)) : 0.0;
129 m_margin_ns = std::max(1'000'000.0, m_mean_overshoot_ns + 2.0 * stddev);
132 double m_margin_ns = 2'000'000.0;
133 double m_mean_overshoot_ns = 0.0;
138class frame_pacing_sleep
140 static constexpr int64_t max_count = 500;
141 static constexpr double min_margin_ns = 500'000.0;
142 static constexpr double max_margin_ns = 5'000'000.0;
143 static constexpr double target_error_ns = -500'000.0;
144 static constexpr double correction_rate = 0.1;
147 void sleep_until(uint64_t target_ns)
149 const int64_t to_sleep =
static_cast<int64_t
>(target_ns - now_ns()) -
static_cast<int64_t
>(m_margin_ns);
152 coarse_sleep(to_sleep);
155 while (now_ns() < target_ns)
157 ossia_rwlock_pause();
161 const int64_t error_ns =
static_cast<int64_t
>(now_ns() - target_ns);
162 update(
static_cast<double>(error_ns));
166 void update(
double error_ns)
168 if (m_count >= max_count)
170 m_count = max_count / 2;
175 const double delta = error_ns - m_mean_error_ns;
176 m_mean_error_ns += delta /
static_cast<double>(m_count);
177 m_m2 += delta * (error_ns - m_mean_error_ns);
180 const double correction = (m_mean_error_ns - target_error_ns) * correction_rate;
181 m_margin_ns = std::clamp(m_margin_ns + correction, min_margin_ns, max_margin_ns);
184 double m_margin_ns = 2'000'000.0;
185 double m_mean_error_ns = 0.0;
191class windows_timer_sleep
193 static constexpr int64_t max_count = 500;
194 static constexpr double min_margin_ns = 100'000.0;
195 static constexpr double max_margin_ns = 2'000'000.0;
196 static constexpr double target_error_ns = -100'000.0;
197 static constexpr double correction_rate = 0.1;
200 windows_timer_sleep()
206 m_timer = CreateWaitableTimerExW(
208 CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
214 m_high_resolution =
true;
219 m_timer = CreateWaitableTimerW(
nullptr, TRUE,
nullptr);
220 m_high_resolution =
false;
221 m_margin_ns = 1'500'000.0;
225 ~windows_timer_sleep()
228 CloseHandle(m_timer);
232 windows_timer_sleep(
const windows_timer_sleep&) =
delete;
233 windows_timer_sleep& operator=(
const windows_timer_sleep&) =
delete;
235 void sleep_until(uint64_t target_ns)
237 const uint64_t now = ossia::now_ns();
238 if (target_ns <= now)
241 const int64_t remaining_ns =
static_cast<int64_t
>(target_ns - now);
242 const int64_t sleep_ns = remaining_ns -
static_cast<int64_t
>(m_margin_ns);
247 LARGE_INTEGER due_time;
248 due_time.QuadPart = -(sleep_ns / 100);
250 SetWaitableTimerEx(m_timer, &due_time, 0,
nullptr,
nullptr,
nullptr, 0);
251 WaitForSingleObject(m_timer, INFINITE);
255 while (ossia::now_ns() < target_ns)
261 const int64_t error_ns =
static_cast<int64_t
>(ossia::now_ns() - target_ns);
262 update(
static_cast<double>(error_ns));
266 void update(
double error_ns)
268 if (m_count >= max_count)
270 m_count = max_count / 2;
275 const double delta = error_ns - m_mean_error_ns;
276 m_mean_error_ns += delta /
static_cast<double>(m_count);
277 m_m2 += delta * (error_ns - m_mean_error_ns);
279 const double correction = (m_mean_error_ns - target_error_ns) * correction_rate;
280 m_margin_ns = std::clamp(m_margin_ns + correction, min_margin_ns, max_margin_ns);
283 HANDLE m_timer =
nullptr;
284 bool m_high_resolution =
false;
285 double m_margin_ns = 500'000.0;
286 double m_mean_error_ns = 0.0;