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
|
||||
|
||||
- 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.
|
||||
- On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name.
|
||||
- **Breaking:** Remove lifetime parameter from `Event` and `WindowEvent`.
|
||||
|
|
|
@ -32,6 +32,7 @@ pub use proxy::EventLoopProxy;
|
|||
use sink::EventSink;
|
||||
|
||||
use super::state::{WindowCompositorUpdate, WinitState};
|
||||
use super::window::state::FrameCallbackState;
|
||||
use super::{DeviceId, WindowId};
|
||||
|
||||
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));
|
||||
false
|
||||
} else {
|
||||
let mut redraw_requested = window_requests
|
||||
.get(&window_id)
|
||||
.unwrap()
|
||||
.take_redraw_requested();
|
||||
|
||||
// Redraw the frames while at it.
|
||||
redraw_requested |= state
|
||||
let mut window = state
|
||||
.windows
|
||||
.get_mut()
|
||||
.get_mut(&window_id)
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.refresh_frame();
|
||||
.unwrap();
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -38,7 +38,7 @@ use super::state::WinitState;
|
|||
use super::types::xdg_activation::XdgActivationTokenData;
|
||||
use super::{EventLoopWindowTarget, WindowId};
|
||||
|
||||
mod state;
|
||||
pub(crate) mod state;
|
||||
|
||||
pub use state::WindowState;
|
||||
|
||||
|
@ -295,7 +295,7 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn pre_present_notify(&self) {
|
||||
// TODO
|
||||
self.window_state.lock().unwrap().request_frame_callback();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -125,6 +125,9 @@ pub struct WindowState {
|
|||
/// sends `None` for the new size in the configure.
|
||||
stateless_size: LogicalSize<u32>,
|
||||
|
||||
/// The state of the frame callback.
|
||||
frame_callback_state: FrameCallbackState,
|
||||
|
||||
viewport: Option<WpViewport>,
|
||||
fractional_scale: Option<WpFractionalScaleV1>,
|
||||
|
||||
|
@ -134,26 +137,62 @@ pub struct WindowState {
|
|||
has_pending_move: Option<u32>,
|
||||
}
|
||||
|
||||
/// The state of the cursor grabs.
|
||||
#[derive(Clone, Copy)]
|
||||
struct GrabState {
|
||||
/// The grab mode requested by the user.
|
||||
user_grab_mode: CursorGrabMode,
|
||||
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));
|
||||
|
||||
/// The current grab mode.
|
||||
current_grab_mode: CursorGrabMode,
|
||||
}
|
||||
|
||||
impl GrabState {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
user_grab_mode: CursorGrabMode::None,
|
||||
current_grab_mode: CursorGrabMode::None,
|
||||
compositor,
|
||||
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.
|
||||
fn apply_on_poiner<F: Fn(&ThemedPointer<WinitPointerData>, &WinitPointerData)>(
|
||||
&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(
|
||||
&mut self,
|
||||
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.
|
||||
#[inline]
|
||||
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 {
|
||||
fn from(value: ResizeDirection) -> Self {
|
||||
match value {
|
||||
|
|
|
@ -524,11 +524,12 @@ impl Window {
|
|||
self.window.scale_factor()
|
||||
}
|
||||
|
||||
/// Requests a future [`Event::RedrawRequested`] event to be emitted in a way that is
|
||||
/// synchronized and / or throttled by the windowing system.
|
||||
/// Queues a [`Event::RedrawRequested`] event to be emitted that aligns with the windowing
|
||||
/// system drawing loop.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
|
@ -536,11 +537,16 @@ impl Window {
|
|||
/// with respect to other events, since the requirements can vary significantly between
|
||||
/// 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
|
||||
///
|
||||
/// - **Windows** This API uses `RedrawWindow` to request a `WM_PAINT` message and `RedrawRequested`
|
||||
/// is emitted in sync with any `WM_PAINT` messages
|
||||
/// - **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
|
||||
#[inline]
|
||||
|
|
Loading…
Reference in a new issue