Compare commits

...

1 Commits

Author SHA1 Message Date
Mads Marquart
fd5e29f9d9 windows: Coalesce wake-up events 2025-09-05 01:39:54 +02:00
4 changed files with 28 additions and 17 deletions

View File

@@ -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();
}

View File

@@ -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<Box<dyn FnMut()>>;
#[derive(Debug)]
pub struct EventLoopProxy {
has_sent_wakeup_msg: Arc<AtomicBool>,
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<Box<dyn FnMut()>> 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
},

View File

@@ -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<bool>,
pub(super) has_sent_wakeup_msg: Arc<AtomicBool>,
control_flow: Cell<ControlFlow>,
exit: Cell<Option<i32>>,
runner_state: Cell<RunnerState>,
@@ -69,8 +72,6 @@ pub(crate) enum Event {
Device { device_id: DeviceId, event: DeviceEvent },
Window { window_id: WindowId, event: WindowEvent },
BufferedScaleFactorChanged(HWND, f64, PhysicalSize<u32>),
// 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);

View File

@@ -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.