From fd5e29f9d911bf4144ed5cb6b300a91b01f47b0c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 27 Feb 2025 06:06:38 +0100 Subject: [PATCH] windows: Coalesce wake-up events --- winit-core/src/event_loop/mod.rs | 6 ------ winit-win32/src/event_loop.rs | 30 +++++++++++++++++++--------- winit-win32/src/event_loop/runner.rs | 8 ++++++-- winit/src/changelog/unreleased.md | 1 + 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/winit-core/src/event_loop/mod.rs b/winit-core/src/event_loop/mod.rs index 71307ee7c..9422a9bbf 100644 --- a/winit-core/src/event_loop/mod.rs +++ b/winit-core/src/event_loop/mod.rs @@ -140,12 +140,6 @@ impl EventLoopProxy { /// /// [`proxy_wake_up`]: crate::application::ApplicationHandler::proxy_wake_up /// [`ApplicationHandler::proxy_wake_up()`]: crate::application::ApplicationHandler::proxy_wake_up - /// - /// # Platform-specific - /// - /// - **Windows**: The wake-up may be ignored under high contention, see [#3687]. - /// - /// [#3687]: https://github.com/rust-windowing/winit/pull/3687 pub fn wake_up(&self) { self.proxy.wake_up(); } diff --git a/winit-win32/src/event_loop.rs b/winit-win32/src/event_loop.rs index 974170a5f..639b5cb42 100644 --- a/winit-win32/src/event_loop.rs +++ b/winit-win32/src/event_loop.rs @@ -416,7 +416,10 @@ impl ActiveEventLoop { impl RootActiveEventLoop for ActiveEventLoop { fn create_proxy(&self) -> RootEventLoopProxy { - let event_loop_proxy = EventLoopProxy { target_window: self.0.thread_msg_target }; + let event_loop_proxy = EventLoopProxy { + has_sent_wakeup_msg: self.0.has_sent_wakeup_msg.clone(), + target_window: self.0.thread_msg_target, + }; RootEventLoopProxy::new(Arc::new(event_loop_proxy)) } @@ -766,6 +769,7 @@ type ThreadExecFn = Box>; #[derive(Debug)] pub struct EventLoopProxy { + has_sent_wakeup_msg: Arc, target_window: HWND, } @@ -774,7 +778,18 @@ unsafe impl Sync for EventLoopProxy {} impl EventLoopProxyProvider for EventLoopProxy { fn wake_up(&self) { - unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) }; + if self.has_sent_wakeup_msg.swap(true, Ordering::AcqRel) { + // Do not send a wakeup event if one has already been sent, but hasn't been processed + // yet. This prevents errors when the internal message queue fills up, and effectively + // coalesces wakeups. + tracing::trace!("avoided sending wake up, previous wake-up has yet to be processed"); + return; + } + if unsafe { PostMessageW(self.target_window, PROXY_WAKEUP_MSG_ID.get(), 0, 0) } == 0 { + // _can_ technically fail, but realistically won't, since we've prevented the most + // common case (queue full) above. + tracing::error!("failed waking event loop: {}", std::io::Error::last_os_error()); + } } } @@ -830,7 +845,7 @@ impl LazyMessageId { // Message sent by the `EventLoopProxy` when we want to wake up the thread. // WPARAM and LPARAM are unused. -static USER_EVENT_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::WakeupMsg\0"); +static PROXY_WAKEUP_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::WakeupMsg\0"); // Message sent when we want to execute a closure in the thread. // WPARAM contains a Box> that must be retrieved with `Box::from_raw`, // and LPARAM is unused. @@ -2471,12 +2486,9 @@ unsafe extern "system" fn thread_event_target_callback( unsafe { DefWindowProcW(window, msg, wparam, lparam) } }, - _ if msg == USER_EVENT_MSG_ID.get() => { - // synthesis a placeholder UserEvent, so that if the callback is - // re-entered it can be buffered for later delivery. the real - // user event is still in the mpsc channel and will be pulled - // once the placeholder event is delivered to the wrapper - // `event_handler` + _ if msg == PROXY_WAKEUP_MSG_ID.get() => { + // Reset the sent state, allowing new `PROXY_WAKEUP_MSG_ID` messages to be sent. + userdata.event_loop_runner.has_sent_wakeup_msg.store(false, Ordering::Release); userdata.send_wakeup(); 0 }, diff --git a/winit-win32/src/event_loop/runner.rs b/winit-win32/src/event_loop/runner.rs index d128d9744..896827a8e 100644 --- a/winit-win32/src/event_loop/runner.rs +++ b/winit-win32/src/event_loop/runner.rs @@ -2,6 +2,7 @@ use std::any::Any; use std::cell::{Cell, RefCell}; use std::collections::VecDeque; use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Instant; use std::{fmt, mem, panic}; @@ -30,6 +31,8 @@ pub(crate) struct EventLoopRunner { // can't stall an external loop beyond a frame pub(super) interrupt_msg_dispatch: Cell, + pub(super) has_sent_wakeup_msg: Arc, + control_flow: Cell, exit: Cell>, runner_state: Cell, @@ -69,8 +72,6 @@ pub(crate) enum Event { Device { device_id: DeviceId, event: DeviceEvent }, Window { window_id: WindowId, event: WindowEvent }, BufferedScaleFactorChanged(HWND, f64, PhysicalSize), - // FIXME(madsmtm): Coalesce these into a flag (or similar) instead of handling them as events. - // https://github.com/rust-windowing/winit/pull/3687 WakeUp, } @@ -80,6 +81,7 @@ impl EventLoopRunner { thread_id, thread_msg_target, interrupt_msg_dispatch: Cell::new(false), + has_sent_wakeup_msg: Arc::new(AtomicBool::new(false)), runner_state: Cell::new(RunnerState::Uninitialized), control_flow: Cell::new(ControlFlow::default()), exit: Cell::new(None), @@ -131,6 +133,7 @@ impl EventLoopRunner { thread_id: _, thread_msg_target: _, interrupt_msg_dispatch, + has_sent_wakeup_msg, runner_state, panic_error, control_flow: _, @@ -140,6 +143,7 @@ impl EventLoopRunner { event_buffer: _, } = self; interrupt_msg_dispatch.set(false); + has_sent_wakeup_msg.store(false, Ordering::Release); runner_state.set(RunnerState::Uninitialized); panic_error.set(None); exit.set(None); diff --git a/winit/src/changelog/unreleased.md b/winit/src/changelog/unreleased.md index 6c7cbbac7..2d3104580 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -260,3 +260,4 @@ changelog entry. - On Windows, `Window::theme` will return the correct theme after setting it through `Window::set_theme`. - On Windows, `Window::set_theme` will change the title bar color immediately now. - On Windows 11, prevent incorrect shifting when dragging window onto a monitor with different DPI. +- On Windows, coalesce wake-up events to avoid filling the message queue.