mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 13:51:30 +11:00
On Wayland, use frame callbacks to throttle RedrawRequested
Throttle RedrawRequested events by the frame callbacks, so the users could render at the display refresh rate.
This commit is contained in:
parent
38f28d5836
commit
7a58fe58ce
|
@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||||
|
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
|
- On Wayland, use frame callbacks to throttle `RedrawRequested` events so redraws will align with compositor.
|
||||||
- Add `Window::pre_present_notify` to notify winit before presenting to the windowing system.
|
- Add `Window::pre_present_notify` to notify winit before presenting to the windowing system.
|
||||||
- On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name.
|
- On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name.
|
||||||
- **Breaking:** Remove lifetime parameter from `Event` and `WindowEvent`.
|
- **Breaking:** Remove lifetime parameter from `Event` and `WindowEvent`.
|
||||||
|
|
|
@ -32,6 +32,7 @@ pub use proxy::EventLoopProxy;
|
||||||
use sink::EventSink;
|
use sink::EventSink;
|
||||||
|
|
||||||
use super::state::{WindowCompositorUpdate, WinitState};
|
use super::state::{WindowCompositorUpdate, WinitState};
|
||||||
|
use super::window::state::FrameCallbackState;
|
||||||
use super::{DeviceId, WindowId};
|
use super::{DeviceId, WindowId};
|
||||||
|
|
||||||
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
|
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
|
||||||
|
@ -494,22 +495,29 @@ impl<T: 'static> EventLoop<T> {
|
||||||
mem::drop(state.windows.get_mut().remove(&window_id));
|
mem::drop(state.windows.get_mut().remove(&window_id));
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
let mut redraw_requested = window_requests
|
let mut window = state
|
||||||
.get(&window_id)
|
|
||||||
.unwrap()
|
|
||||||
.take_redraw_requested();
|
|
||||||
|
|
||||||
// Redraw the frames while at it.
|
|
||||||
redraw_requested |= state
|
|
||||||
.windows
|
.windows
|
||||||
.get_mut()
|
.get_mut()
|
||||||
.get_mut(&window_id)
|
.get_mut(&window_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.refresh_frame();
|
|
||||||
|
|
||||||
redraw_requested
|
if window.frame_callback_state() == FrameCallbackState::Requested {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
// Reset the frame callbacks state.
|
||||||
|
window.frame_callback_reset();
|
||||||
|
let mut redraw_requested = window_requests
|
||||||
|
.get(&window_id)
|
||||||
|
.unwrap()
|
||||||
|
.take_redraw_requested();
|
||||||
|
|
||||||
|
// Redraw the frame while at it.
|
||||||
|
redraw_requested |= window.refresh_frame();
|
||||||
|
|
||||||
|
redraw_requested
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -321,7 +321,15 @@ impl CompositorHandler for WinitState {
|
||||||
self.scale_factor_changed(surface, scale_factor as f64, true)
|
self.scale_factor_changed(surface, scale_factor as f64, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frame(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: u32) {}
|
fn frame(&mut self, _: &Connection, _: &QueueHandle<Self>, surface: &WlSurface, _: u32) {
|
||||||
|
let window_id = super::make_wid(surface);
|
||||||
|
let window = match self.windows.get_mut().get(&window_id) {
|
||||||
|
Some(window) => window,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
window.lock().unwrap().frame_callback_received();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProvidesRegistryState for WinitState {
|
impl ProvidesRegistryState for WinitState {
|
||||||
|
|
|
@ -38,7 +38,7 @@ use super::state::WinitState;
|
||||||
use super::types::xdg_activation::XdgActivationTokenData;
|
use super::types::xdg_activation::XdgActivationTokenData;
|
||||||
use super::{EventLoopWindowTarget, WindowId};
|
use super::{EventLoopWindowTarget, WindowId};
|
||||||
|
|
||||||
mod state;
|
pub(crate) mod state;
|
||||||
|
|
||||||
pub use state::WindowState;
|
pub use state::WindowState;
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ impl Window {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pre_present_notify(&self) {
|
pub fn pre_present_notify(&self) {
|
||||||
// TODO
|
self.window_state.lock().unwrap().request_frame_callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -125,6 +125,9 @@ pub struct WindowState {
|
||||||
/// sends `None` for the new size in the configure.
|
/// sends `None` for the new size in the configure.
|
||||||
stateless_size: LogicalSize<u32>,
|
stateless_size: LogicalSize<u32>,
|
||||||
|
|
||||||
|
/// The state of the frame callback.
|
||||||
|
frame_callback_state: FrameCallbackState,
|
||||||
|
|
||||||
viewport: Option<WpViewport>,
|
viewport: Option<WpViewport>,
|
||||||
fractional_scale: Option<WpFractionalScaleV1>,
|
fractional_scale: Option<WpFractionalScaleV1>,
|
||||||
|
|
||||||
|
@ -134,26 +137,62 @@ pub struct WindowState {
|
||||||
has_pending_move: Option<u32>,
|
has_pending_move: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state of the cursor grabs.
|
impl WindowState {
|
||||||
#[derive(Clone, Copy)]
|
/// Create new window state.
|
||||||
struct GrabState {
|
pub fn new(
|
||||||
/// The grab mode requested by the user.
|
connection: Connection,
|
||||||
user_grab_mode: CursorGrabMode,
|
queue_handle: &QueueHandle<WinitState>,
|
||||||
|
winit_state: &WinitState,
|
||||||
|
size: LogicalSize<u32>,
|
||||||
|
window: Window,
|
||||||
|
theme: Option<Theme>,
|
||||||
|
) -> Self {
|
||||||
|
let compositor = winit_state.compositor_state.clone();
|
||||||
|
let pointer_constraints = winit_state.pointer_constraints.clone();
|
||||||
|
let viewport = winit_state
|
||||||
|
.viewporter_state
|
||||||
|
.as_ref()
|
||||||
|
.map(|state| state.get_viewport(window.wl_surface(), queue_handle));
|
||||||
|
let fractional_scale = winit_state
|
||||||
|
.fractional_scaling_manager
|
||||||
|
.as_ref()
|
||||||
|
.map(|fsm| fsm.fractional_scaling(window.wl_surface(), queue_handle));
|
||||||
|
|
||||||
/// The current grab mode.
|
|
||||||
current_grab_mode: CursorGrabMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GrabState {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
user_grab_mode: CursorGrabMode::None,
|
compositor,
|
||||||
current_grab_mode: CursorGrabMode::None,
|
connection,
|
||||||
|
csd_fails: false,
|
||||||
|
cursor_grab_mode: GrabState::new(),
|
||||||
|
cursor_icon: CursorIcon::Default,
|
||||||
|
cursor_visible: true,
|
||||||
|
decorate: true,
|
||||||
|
fractional_scale,
|
||||||
|
frame: None,
|
||||||
|
frame_callback_state: FrameCallbackState::None,
|
||||||
|
has_focus: false,
|
||||||
|
has_pending_move: None,
|
||||||
|
ime_allowed: false,
|
||||||
|
ime_purpose: ImePurpose::Normal,
|
||||||
|
last_configure: None,
|
||||||
|
max_inner_size: None,
|
||||||
|
min_inner_size: MIN_WINDOW_SIZE,
|
||||||
|
pointer_constraints,
|
||||||
|
pointers: Default::default(),
|
||||||
|
queue_handle: queue_handle.clone(),
|
||||||
|
resizable: true,
|
||||||
|
scale_factor: 1.,
|
||||||
|
shm: winit_state.shm.wl_shm().clone(),
|
||||||
|
size,
|
||||||
|
stateless_size: size,
|
||||||
|
text_inputs: Vec::new(),
|
||||||
|
theme,
|
||||||
|
title: String::default(),
|
||||||
|
transparent: false,
|
||||||
|
viewport,
|
||||||
|
window: ManuallyDrop::new(window),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowState {
|
|
||||||
/// Apply closure on the given pointer.
|
/// Apply closure on the given pointer.
|
||||||
fn apply_on_poiner<F: Fn(&ThemedPointer<WinitPointerData>, &WinitPointerData)>(
|
fn apply_on_poiner<F: Fn(&ThemedPointer<WinitPointerData>, &WinitPointerData)>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -168,6 +207,33 @@ impl WindowState {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current state of the frame callback.
|
||||||
|
pub fn frame_callback_state(&self) -> FrameCallbackState {
|
||||||
|
self.frame_callback_state
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The frame callback was received, but not yet sent to the user.
|
||||||
|
pub fn frame_callback_received(&mut self) {
|
||||||
|
self.frame_callback_state = FrameCallbackState::Received;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the frame callbacks state.
|
||||||
|
pub fn frame_callback_reset(&mut self) {
|
||||||
|
self.frame_callback_state = FrameCallbackState::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request a frame callback if we don't have one for this window in flight.
|
||||||
|
pub fn request_frame_callback(&mut self) {
|
||||||
|
let surface = self.window.wl_surface();
|
||||||
|
match self.frame_callback_state {
|
||||||
|
FrameCallbackState::None | FrameCallbackState::Received => {
|
||||||
|
self.frame_callback_state = FrameCallbackState::Requested;
|
||||||
|
surface.frame(&self.queue_handle, surface.clone());
|
||||||
|
}
|
||||||
|
FrameCallbackState::Requested => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn configure(
|
pub fn configure(
|
||||||
&mut self,
|
&mut self,
|
||||||
configure: WindowConfigure,
|
configure: WindowConfigure,
|
||||||
|
@ -391,60 +457,6 @@ impl WindowState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new window state.
|
|
||||||
pub fn new(
|
|
||||||
connection: Connection,
|
|
||||||
queue_handle: &QueueHandle<WinitState>,
|
|
||||||
winit_state: &WinitState,
|
|
||||||
size: LogicalSize<u32>,
|
|
||||||
window: Window,
|
|
||||||
theme: Option<Theme>,
|
|
||||||
) -> Self {
|
|
||||||
let compositor = winit_state.compositor_state.clone();
|
|
||||||
let pointer_constraints = winit_state.pointer_constraints.clone();
|
|
||||||
let viewport = winit_state
|
|
||||||
.viewporter_state
|
|
||||||
.as_ref()
|
|
||||||
.map(|state| state.get_viewport(window.wl_surface(), queue_handle));
|
|
||||||
let fractional_scale = winit_state
|
|
||||||
.fractional_scaling_manager
|
|
||||||
.as_ref()
|
|
||||||
.map(|fsm| fsm.fractional_scaling(window.wl_surface(), queue_handle));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
compositor,
|
|
||||||
connection,
|
|
||||||
theme,
|
|
||||||
csd_fails: false,
|
|
||||||
decorate: true,
|
|
||||||
cursor_grab_mode: GrabState::new(),
|
|
||||||
cursor_icon: CursorIcon::Default,
|
|
||||||
cursor_visible: true,
|
|
||||||
fractional_scale,
|
|
||||||
frame: None,
|
|
||||||
has_focus: false,
|
|
||||||
ime_allowed: false,
|
|
||||||
ime_purpose: ImePurpose::Normal,
|
|
||||||
last_configure: None,
|
|
||||||
max_inner_size: None,
|
|
||||||
min_inner_size: MIN_WINDOW_SIZE,
|
|
||||||
pointer_constraints,
|
|
||||||
pointers: Default::default(),
|
|
||||||
queue_handle: queue_handle.clone(),
|
|
||||||
scale_factor: 1.,
|
|
||||||
shm: winit_state.shm.wl_shm().clone(),
|
|
||||||
size,
|
|
||||||
stateless_size: size,
|
|
||||||
text_inputs: Vec::new(),
|
|
||||||
title: String::default(),
|
|
||||||
transparent: false,
|
|
||||||
resizable: true,
|
|
||||||
viewport,
|
|
||||||
window: ManuallyDrop::new(window),
|
|
||||||
has_pending_move: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the outer size of the window.
|
/// Get the outer size of the window.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn outer_size(&self) -> LogicalSize<u32> {
|
pub fn outer_size(&self) -> LogicalSize<u32> {
|
||||||
|
@ -892,6 +904,37 @@ impl Drop for WindowState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The state of the cursor grabs.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct GrabState {
|
||||||
|
/// The grab mode requested by the user.
|
||||||
|
user_grab_mode: CursorGrabMode,
|
||||||
|
|
||||||
|
/// The current grab mode.
|
||||||
|
current_grab_mode: CursorGrabMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GrabState {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
user_grab_mode: CursorGrabMode::None,
|
||||||
|
current_grab_mode: CursorGrabMode::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The state of the frame callback.
|
||||||
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum FrameCallbackState {
|
||||||
|
/// No frame callback was requsted.
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
/// The frame callback was requested, but not yet arrived, the redraw events are throttled.
|
||||||
|
Requested,
|
||||||
|
/// The callback was marked as done, and user could receive redraw requested
|
||||||
|
Received,
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ResizeDirection> for ResizeEdge {
|
impl From<ResizeDirection> for ResizeEdge {
|
||||||
fn from(value: ResizeDirection) -> Self {
|
fn from(value: ResizeDirection) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
|
|
@ -524,11 +524,12 @@ impl Window {
|
||||||
self.window.scale_factor()
|
self.window.scale_factor()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requests a future [`Event::RedrawRequested`] event to be emitted in a way that is
|
/// Queues a [`Event::RedrawRequested`] event to be emitted that aligns with the windowing
|
||||||
/// synchronized and / or throttled by the windowing system.
|
/// system drawing loop.
|
||||||
///
|
///
|
||||||
/// This is the **strongly encouraged** method of redrawing windows, as it can integrate with
|
/// This is the **strongly encouraged** method of redrawing windows, as it can integrate with
|
||||||
/// OS-requested redraws (e.g. when a window gets resized).
|
/// OS-requested redraws (e.g. when a window gets resized). To improve the event delivery
|
||||||
|
/// consider using [`Window::pre_present_notify`] as described in docs.
|
||||||
///
|
///
|
||||||
/// Applications should always aim to redraw whenever they receive a `RedrawRequested` event.
|
/// Applications should always aim to redraw whenever they receive a `RedrawRequested` event.
|
||||||
///
|
///
|
||||||
|
@ -536,11 +537,16 @@ impl Window {
|
||||||
/// with respect to other events, since the requirements can vary significantly between
|
/// with respect to other events, since the requirements can vary significantly between
|
||||||
/// windowing systems.
|
/// windowing systems.
|
||||||
///
|
///
|
||||||
|
/// However as the event aligns with the windowing system drawing loop, it may not arrive in
|
||||||
|
/// same or even next event loop iteration.
|
||||||
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - **Windows** This API uses `RedrawWindow` to request a `WM_PAINT` message and `RedrawRequested`
|
/// - **Windows** This API uses `RedrawWindow` to request a `WM_PAINT` message and `RedrawRequested`
|
||||||
/// is emitted in sync with any `WM_PAINT` messages
|
/// is emitted in sync with any `WM_PAINT` messages
|
||||||
/// - **iOS:** Can only be called on the main thread.
|
/// - **iOS:** Can only be called on the main thread.
|
||||||
|
/// - **Wayland:** The events are aligned with the frame callbacks when [`Window::pre_present_notify`]
|
||||||
|
/// is used.
|
||||||
///
|
///
|
||||||
/// [`Event::RedrawRequested`]: crate::event::Event::RedrawRequested
|
/// [`Event::RedrawRequested`]: crate::event::Event::RedrawRequested
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
Loading…
Reference in a new issue