@@ -611,6 +611,19 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
#[doc(hidden)]
pub type RunnableVariant = Runnable<RunnableMeta>;
+#[doc(hidden)]
+pub struct TimerResolutionGuard {
+ cleanup: Option<Box<dyn FnOnce() + Send>>,
+}
+
+impl Drop for TimerResolutionGuard {
+ fn drop(&mut self) {
+ if let Some(cleanup) = self.cleanup.take() {
+ cleanup();
+ }
+ }
+}
+
/// This type is public so that our test macro can generate and use it, but it should not
/// be considered part of our public API.
#[doc(hidden)]
@@ -627,6 +640,10 @@ pub trait PlatformDispatcher: Send + Sync {
Instant::now()
}
+ fn increase_timer_resolution(&self) -> TimerResolutionGuard {
+ TimerResolutionGuard { cleanup: None }
+ }
+
#[cfg(any(test, feature = "test-support"))]
fn as_test(&self) -> Option<&TestDispatcher> {
None
@@ -12,6 +12,7 @@ use windows::{
},
Win32::{
Foundation::{LPARAM, WPARAM},
+ Media::{timeBeginPeriod, timeEndPeriod},
System::Threading::{
GetCurrentThread, HIGH_PRIORITY_CLASS, SetPriorityClass, SetThreadPriority,
THREAD_PRIORITY_TIME_CRITICAL,
@@ -22,7 +23,7 @@ use windows::{
use crate::{
GLOBAL_THREAD_TIMINGS, HWND, PlatformDispatcher, Priority, PriorityQueueSender,
- RunnableVariant, SafeHwnd, THREAD_TIMINGS, TaskTiming, ThreadTaskTimings,
+ RunnableVariant, SafeHwnd, THREAD_TIMINGS, TaskTiming, ThreadTaskTimings, TimerResolutionGuard,
WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD, profiler,
};
@@ -193,4 +194,15 @@ impl PlatformDispatcher for WindowsDispatcher {
f();
});
}
+
+ fn increase_timer_resolution(&self) -> TimerResolutionGuard {
+ unsafe {
+ timeBeginPeriod(1);
+ }
+ TimerResolutionGuard {
+ cleanup: Some(Box::new(|| unsafe {
+ timeEndPeriod(1);
+ })),
+ }
+ }
}
@@ -20,7 +20,6 @@ use windows::{
Win32::{
Foundation::*,
Graphics::{Direct3D11::ID3D11Device, Gdi::*},
- Media::{timeBeginPeriod, timeEndPeriod},
Security::Credentials::*,
System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*},
UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*},
@@ -98,10 +97,6 @@ impl WindowsPlatform {
pub(crate) fn new(headless: bool) -> Result<Self> {
unsafe {
OleInitialize(None).context("unable to initialize Windows OLE")?;
- // Set the system timer resolution to 1ms so that short timeouts
- // (e.g. in Scheduler::block) are not rounded up to the default
- // ~15.6ms tick interval.
- timeBeginPeriod(1);
}
let (directx_devices, text_system, direct_write_text_system) = if !headless {
let devices = DirectXDevices::new().context("Creating DirectX devices")?;
@@ -991,7 +986,6 @@ impl WindowsPlatformInner {
impl Drop for WindowsPlatform {
fn drop(&mut self) {
unsafe {
- timeEndPeriod(1);
DestroyWindow(self.handle)
.context("Destroying platform window")
.log_err();
@@ -56,9 +56,17 @@ impl Scheduler for PlatformScheduler {
if let Poll::Ready(()) = future.as_mut().poll(&mut cx) {
return true;
}
+
+ let park_deadline = |deadline: Instant| {
+ // Timer expirations are only delivered every ~15.6 milliseconds by default on Windows.
+ // We increase the resolution during this wait so that short timeouts stay reasonably short.
+ let _timer_guard = self.dispatcher.increase_timer_resolution();
+ parker.park_deadline(deadline)
+ };
+
loop {
match deadline {
- Some(deadline) if !parker.park_deadline(deadline) && deadline <= Instant::now() => {
+ Some(deadline) if !park_deadline(deadline) && deadline <= Instant::now() => {
return false;
}
Some(_) => (),