diff --git a/source/blender/blenlib/BLI_time.h b/source/blender/blenlib/BLI_time.h index 75358d204ca..5bbe47a15c6 100644 --- a/source/blender/blenlib/BLI_time.h +++ b/source/blender/blenlib/BLI_time.h @@ -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); diff --git a/source/blender/blenlib/intern/time.cc b/source/blender/blenlib/intern/time.cc index 07d4bbd07b4..6629fed35ae 100644 --- a/source/blender/blenlib/intern/time.cc +++ b/source/blender/blenlib/intern/time.cc @@ -9,9 +9,16 @@ #include "BLI_time.h" #ifdef WIN32 + +# include +# include + # define WIN32_LEAN_AND_MEAN # include +/* timeapi.h needs to be included after windows.h. */ +# include + 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 +# include + # include # include @@ -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 diff --git a/source/blender/windowmanager/intern/wm_window.cc b/source/blender/windowmanager/intern/wm_window.cc index b6d05b1bb20..839fc87aade 100644 --- a/source/blender/windowmanager/intern/wm_window.cc +++ b/source/blender/windowmanager/intern/wm_window.cc @@ -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); } }