WM: Switch to high-resolution timers on Windows for sleeping
This PR introduces a more precise sleep function `BLI_time_sleep_duration` using high-resolution timers on Windows. By default, Windows only has a resolution of 15.25ms for the regular `Sleep` function. Using more precise timers makes sure that Blender can wake from sleep quicker and improves performance at high frame rates. High-resolution timers may have better energy efficiency than using `timeBeginPeriod`/`timeEndPeriod` that change the timer resolution globally on some versions of Windows. Pull Request: https://projects.blender.org/blender/blender/pulls/140221
This commit is contained in:
committed by
Sergey Sharybin
parent
f62422884b
commit
a889aa9e38
@@ -23,3 +23,11 @@ extern long int BLI_time_now_seconds_i(void);
|
||||
* \param ms: Number of milliseconds to sleep
|
||||
*/
|
||||
void BLI_time_sleep_ms(int ms);
|
||||
|
||||
/**
|
||||
* Platform-independent high-resolution sleep function.
|
||||
* Using this function can have advantages over \see BLI_time_sleep_ms on Windows due to a default
|
||||
* non-precise sleep resolution of 15.25ms.
|
||||
* \param us: Number of microseconds to sleep
|
||||
*/
|
||||
void BLI_time_sleep_precise_us(int us);
|
||||
|
||||
@@ -9,9 +9,16 @@
|
||||
#include "BLI_time.h"
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
# include <cmath>
|
||||
# include <cstdio>
|
||||
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
|
||||
/* timeapi.h needs to be included after windows.h. */
|
||||
# include <timeapi.h>
|
||||
|
||||
double BLI_time_now_seconds(void)
|
||||
{
|
||||
static int hasperfcounter = -1; /* (-1 == unknown) */
|
||||
@@ -57,8 +64,49 @@ void BLI_time_sleep_ms(int ms)
|
||||
Sleep(ms);
|
||||
}
|
||||
|
||||
void BLI_time_sleep_precise_us(int us)
|
||||
{
|
||||
/* Prefer thread-safety over caching the timer with a static variable. According to
|
||||
* https://github.com/rust-lang/rust/pull/116461/files, this costs only approximately 2000ns. */
|
||||
HANDLE timerHandle = CreateWaitableTimerExW(
|
||||
nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
|
||||
if (!timerHandle) {
|
||||
if (GetLastError() == ERROR_INVALID_PARAMETER) {
|
||||
/* CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is only supported since Windows 10, version 1803. */
|
||||
DWORD duration_ms = DWORD(std::ceil(double(us) / 1000.0));
|
||||
Sleep(duration_ms);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr,
|
||||
"BLI_time_sleep_precise_us: CreateWaitableTimerExW failed: %d\n",
|
||||
GetLastError());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wait time is specified in 100 nanosecond intervals. */
|
||||
LARGE_INTEGER wait_time;
|
||||
wait_time.QuadPart = -us * 10;
|
||||
if (!SetWaitableTimer(timerHandle, &wait_time, 0, nullptr, nullptr, 0)) {
|
||||
fprintf(stderr, "BLI_time_sleep_precise_us: SetWaitableTimer failed: %d\n", GetLastError());
|
||||
CloseHandle(timerHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(timerHandle, INFINITE) != WAIT_OBJECT_0) {
|
||||
fprintf(stderr, "BLI_time_sleep_precise_us: WaitForSingleObject failed: %d\n", GetLastError());
|
||||
CloseHandle(timerHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
CloseHandle(timerHandle);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
# include <chrono>
|
||||
# include <thread>
|
||||
|
||||
# include <sys/time.h>
|
||||
# include <unistd.h>
|
||||
|
||||
@@ -92,4 +140,9 @@ void BLI_time_sleep_ms(int ms)
|
||||
usleep(ms * 1000);
|
||||
}
|
||||
|
||||
void BLI_time_sleep_precise_us(int us)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(us));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2003,21 +2003,7 @@ void wm_window_events_process(const bContext *C)
|
||||
/* Skip sleeping when simulating events so tests don't idle unnecessarily as simulated
|
||||
* events are typically generated from a timer that runs in the main loop. */
|
||||
if ((has_event == false) && (sleep_us != 0) && !(G.f & G_FLAG_EVENT_SIMULATE)) {
|
||||
if (sleep_us == sleep_us_default) {
|
||||
/* NOTE(@ideasman42): prefer #BLI_time_sleep_ms over `sleep_for(..)` in the common case
|
||||
* because this function uses lower resolution (millisecond) resolution sleep timers
|
||||
* which are tried & true for the idle loop. We could move to C++ `sleep_for(..)`
|
||||
* if this works well on all platforms but this needs further testing. */
|
||||
BLI_time_sleep_ms(sleep_us_default / 1000);
|
||||
}
|
||||
else {
|
||||
/* The time was shortened to resume for the upcoming timer, use a high resolution sleep.
|
||||
* Mainly happens during animation playback but could happen immediately before any timer.
|
||||
*
|
||||
* NOTE(@ideasman42): At time of writing Windows-10-22H2 doesn't give higher precision sleep.
|
||||
* Keep the functionality as it doesn't have noticeable down sides either. */
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(sleep_us));
|
||||
}
|
||||
BLI_time_sleep_precise_us(sleep_us);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user