On Wayland, reduce amount of spurious wakeups

Mark it as breaking, since some clients relied on that behavior, simply
because dispatching clients queue always woke up a winit, meaning that
they won't be able to use user events for this sake.
This commit is contained in:
Kirill Chibisov 2023-08-06 01:09:59 +04:00 committed by GitHub
parent 3c3a863cc9
commit cad3277550
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 155 additions and 95 deletions

View file

@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre
# Unreleased
- **Breaking:** on Wayland, dispatching user created wayland queue won't wake up the loop unless winit has event to send back.
- Removed platform-specific extensions that should be retrieved through `raw-window-handle` trait implementations instead:
- `platform::windows::HINSTANCE`.
- `WindowExtWindows::hinstance`.

View file

@ -90,8 +90,15 @@ impl<T: 'static> EventLoop<T> {
// Register Wayland source.
let wayland_source = WaylandSource::new(event_queue)?;
let wayland_dispatcher =
calloop::Dispatcher::new(wayland_source, |_, queue, winit_state| {
queue.dispatch_pending(winit_state)
calloop::Dispatcher::new(wayland_source, |_, queue, winit_state: &mut WinitState| {
let result = queue.dispatch_pending(winit_state);
if result.is_ok()
&& (!winit_state.events_sink.is_empty()
|| !winit_state.window_compositor_updates.is_empty())
{
winit_state.dispatched_events = true;
}
result
});
event_loop
@ -102,21 +109,25 @@ impl<T: 'static> EventLoop<T> {
let pending_user_events = Rc::new(RefCell::new(Vec::new()));
let pending_user_events_clone = pending_user_events.clone();
let (user_events_sender, user_events_channel) = calloop::channel::channel();
event_loop
.handle()
.insert_source(user_events_channel, move |event, _, _| {
event_loop.handle().insert_source(
user_events_channel,
move |event, _, winit_state: &mut WinitState| {
if let calloop::channel::Event::Msg(msg) = event {
winit_state.dispatched_events = true;
pending_user_events_clone.borrow_mut().push(msg);
}
})?;
},
)?;
// An event's loop awakener to wake up for window events from winit's windows.
let (event_loop_awakener, event_loop_awakener_source) = calloop::ping::make_ping()?;
event_loop
.handle()
.insert_source(event_loop_awakener_source, move |_, _, _| {
event_loop.handle().insert_source(
event_loop_awakener_source,
move |_, _, winit_state: &mut WinitState| {
// No extra handling is required, we just need to wake-up.
})?;
winit_state.dispatched_events = true;
},
)?;
let window_target = EventLoopWindowTarget {
connection: connection.clone(),
@ -220,6 +231,7 @@ impl<T: 'static> EventLoop<T> {
where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{
let cause = loop {
let start = Instant::now();
// TODO(rib): remove this workaround and instead make sure that the calloop
@ -246,7 +258,11 @@ impl<T: 'static> EventLoop<T> {
};
match queue.dispatch_pending(state) {
Ok(dispatched) => dispatched > 0,
Ok(dispatched) => {
state.dispatched_events |= !state.events_sink.is_empty()
|| !state.window_compositor_updates.is_empty();
dispatched > 0
}
Err(error) => {
error!("Error dispatching wayland queue: {}", error);
self.control_flow = ControlFlow::ExitWithCode(1);
@ -318,6 +334,15 @@ impl<T: 'static> EventLoop<T> {
ControlFlow::ExitWithCode(_code) => unreachable!(),
};
// Reduce spurious wake-ups.
let dispatched_events = self.with_state(|state| state.dispatched_events);
if matches!(cause, StartCause::WaitCancelled { .. }) && !dispatched_events {
continue;
}
break cause;
};
self.single_iteration(&mut callback, cause);
}
@ -531,6 +556,11 @@ impl<T: 'static> EventLoop<T> {
}
}
// Reset the hint that we've dispatched events.
self.with_state(|state| {
state.dispatched_events = false;
});
// This is always the last event we dispatch before poll again
sticky_exit_callback(
Event::AboutToWait,

View file

@ -20,6 +20,12 @@ impl EventSink {
Default::default()
}
/// Return `true` if there're pending events.
#[inline]
pub fn is_empty(&self) -> bool {
self.window_events.is_empty()
}
/// Add new device event to a queue.
#[inline]
pub fn push_device_event(&mut self, event: DeviceEvent, device_id: DeviceId) {

View file

@ -1,5 +1,6 @@
use std::cell::RefCell;
use std::error::Error;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
use fnv::FnvHashMap;
@ -104,6 +105,10 @@ pub struct WinitState {
/// Loop handle to re-register event sources, such as keyboard repeat.
pub loop_handle: LoopHandle<'static, Self>,
/// Whether we have dispatched events to the user thus we want to
/// send `AboutToWait` and normally wakeup the user.
pub dispatched_events: bool,
}
impl WinitState {
@ -167,6 +172,8 @@ impl WinitState {
monitors: Arc::new(Mutex::new(monitors)),
events_sink: EventSink::new(),
loop_handle,
// Make it true by default.
dispatched_events: true,
})
}
@ -328,6 +335,18 @@ impl CompositorHandler for WinitState {
None => return,
};
// In case we have a redraw requested we must indicate the wake up.
if self
.window_requests
.get_mut()
.get(&window_id)
.unwrap()
.redraw_requested
.load(Ordering::Relaxed)
{
self.dispatched_events = true;
}
window.lock().unwrap().frame_callback_received();
}
}

View file

@ -287,11 +287,15 @@ impl Window {
#[inline]
pub fn request_redraw(&self) {
self.window_requests
if self
.window_requests
.redraw_requested
.store(true, Ordering::Relaxed);
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
self.event_loop_awakener.ping();
}
}
#[inline]
pub fn pre_present_notify(&self) {