Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throttle RedrawRequested #2896

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ And please only add new entries to the top of this list, right below the `# Unre

# Unreleased

- On Web, use `Window.requestAnimationFrame()` to throttle `RedrawRequested` events.
- 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`.
- **Breaking:** `ScaleFactorChanged` now contains a writer instead of a reference to update inner size.
Expand Down
2 changes: 2 additions & 0 deletions examples/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ fn main() -> Result<(), impl std::error::Error> {
window.request_redraw();
}
Event::RedrawRequested(_) => {
// Notify the windowing system that we'll be presenting to the window.
window.pre_present_notify();
fill::fill_window(&window);
}
_ => (),
Expand Down
2 changes: 2 additions & 0 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,8 @@ impl Window {
self.redraw_requester.request_redraw()
}

pub fn pre_present_notify(&self) {}

pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
Err(error::NotSupportedError::new())
}
Expand Down
2 changes: 2 additions & 0 deletions src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ impl Inner {
}
}

pub fn pre_present_notify(&self) {}

pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
unsafe {
let safe_area = self.safe_area_screen_space();
Expand Down
12 changes: 6 additions & 6 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,19 +555,19 @@ impl Window {
}
}
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
match self {
#[cfg(x11_platform)]
Window::X(ref w) => w.request_user_attention(request_type),
#[cfg(wayland_platform)]
Window::Wayland(ref w) => w.request_user_attention(request_type),
}
x11_or_wayland!(match self; Window(w) => w.request_user_attention(request_type))
}

#[inline]
pub fn request_redraw(&self) {
x11_or_wayland!(match self; Window(w) => w.request_redraw())
}

#[inline]
pub fn pre_present_notify(&self) {
x11_or_wayland!(match self; Window(w) => w.pre_present_notify())
}

#[inline]
pub fn current_monitor(&self) -> Option<MonitorHandle> {
match self {
Expand Down
30 changes: 19 additions & 11 deletions src/platform_impl/linux/wayland/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -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();

redraw_requested
.unwrap();

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

Expand Down
10 changes: 9 additions & 1 deletion src/platform_impl/linux/wayland/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 6 additions & 1 deletion src/platform_impl/linux/wayland/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -293,6 +293,11 @@ impl Window {
self.event_loop_awakener.ping();
}

#[inline]
pub fn pre_present_notify(&self) {
self.window_state.lock().unwrap().request_frame_callback();
}

#[inline]
pub fn outer_size(&self) -> PhysicalSize<u32> {
let window_state = self.window_state.lock().unwrap();
Expand Down
181 changes: 112 additions & 69 deletions src/platform_impl/linux/wayland/window/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,

Expand All @@ -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,

/// The current grab mode.
current_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));

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,
Expand All @@ -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,
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions src/platform_impl/linux/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1731,6 +1731,11 @@ impl UnownedWindow {
.unwrap();
}

#[inline]
pub fn pre_present_notify(&self) {
// TODO timer
}

#[inline]
pub fn raw_window_handle(&self) -> RawWindowHandle {
let mut window_handle = XlibWindowHandle::empty();
Expand Down
3 changes: 3 additions & 0 deletions src/platform_impl/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,9 @@ impl WinitWindow {
AppState::queue_redraw(RootWindowId(self.id()));
}

#[inline]
pub fn pre_present_notify(&self) {}

pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let frame_rect = self.frame();
let position = LogicalPosition::new(
Expand Down
Loading
Loading