From 507da9e69c3d29b43694218268ac4007afe6141d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:11:50 +0000 Subject: [PATCH 1/2] perf(wayland): Optimize `RefCell` usage by using `Cell` for `WaylandBackend` In `WaylandBackend` (`src/backend/wayland/mod.rs`), several fields were wrapped in `RefCell`, requiring runtime borrow checks for each read and write operation, which are frequently called in hot paths. This commit replaces `RefCell` with `Cell` across `WaylandBackend`'s fields (`pending_ops`, `cached_outputs`, etc.). Since these fields are either `Copy` types or cheap to clone (`Option`, `Vec`), using `Cell` with the `.take()`, modify, `.set()` pattern entirely eliminates runtime borrow-checking overhead. Note: * Other instances of `Rc` in `src/wayland/render/drm/cursor.rs` are kept as `Rc` (non-atomic reference counting) because switching to `Arc` would decrease performance due to atomic operations. * The `Mutex` on `CursorImageAttributes` in `src/wayland/common.rs` is preserved since it's stored in a Smithay `DataMap`, which strictly requires types to be `Send + Sync`. Co-authored-by: paperbenni <15818888+paperbenni@users.noreply.github.com> --- src/backend/wayland/mod.rs | 163 +++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 89 deletions(-) diff --git a/src/backend/wayland/mod.rs b/src/backend/wayland/mod.rs index f26f48bd..8670fe2e 100644 --- a/src/backend/wayland/mod.rs +++ b/src/backend/wayland/mod.rs @@ -55,7 +55,7 @@ pub mod compositor; use crate::backend::BackendOps; use crate::backend::BackendOutputInfo; use crate::types::{Rect, WindowId}; -use std::cell::RefCell; +use std::cell::Cell; use std::collections::HashMap; use crate::backend::wayland::compositor::WaylandState; @@ -94,52 +94,57 @@ pub enum WmCommand { /// unsafe code in the backend bridge. pub struct WaylandBackend { /// Pending operations from WM logic - pending_ops: RefCell>, + pending_ops: Cell>, /// Cached pointer location, updated by sync_cache each event loop tick - cached_pointer_location: RefCell>, + cached_pointer_location: Cell>, /// Cached xdisplay, updated by sync_cache - cached_xdisplay: RefCell>, + cached_xdisplay: Cell>, /// Cached output info, updated by sync_cache - cached_outputs: RefCell>, + cached_outputs: Cell>, /// Cached input device list, updated by sync_cache - cached_input_devices: RefCell>, + cached_input_devices: Cell>, /// Cached display names, updated by sync_cache - cached_displays: RefCell>, + cached_displays: Cell>, /// Cached display modes per display name, updated by sync_cache - cached_display_modes: RefCell>>, + cached_display_modes: Cell>>, /// Cached keyboard focus window, updated by sync_cache - cached_keyboard_focus: RefCell>, + cached_keyboard_focus: Cell>, } impl WaylandBackend { pub fn new() -> Self { Self { - pending_ops: RefCell::new(Vec::new()), - cached_pointer_location: RefCell::new(None), - cached_xdisplay: RefCell::new(None), - cached_outputs: RefCell::new(Vec::new()), - cached_input_devices: RefCell::new(Vec::new()), - cached_displays: RefCell::new(Vec::new()), - cached_display_modes: RefCell::new(HashMap::new()), - cached_keyboard_focus: RefCell::new(None), + pending_ops: Cell::new(Vec::new()), + cached_pointer_location: Cell::new(None), + cached_xdisplay: Cell::new(None), + cached_outputs: Cell::new(Vec::new()), + cached_input_devices: Cell::new(Vec::new()), + cached_displays: Cell::new(Vec::new()), + cached_display_modes: Cell::new(HashMap::new()), + cached_keyboard_focus: Cell::new(None), } } + fn push_op(&self, cmd: WmCommand) { + let mut ops = self.pending_ops.take(); + ops.push(cmd); + self.pending_ops.set(ops); + } + /// Drain all pending commands. Called from the event loop after WM logic. pub fn drain_ops(&self) -> Vec { - std::mem::take(&mut *self.pending_ops.borrow_mut()) + self.pending_ops.take() } /// Update cached values from WaylandState. Called after WM logic completes. pub fn sync_cache(&self, state: &WaylandState) { let loc = state.pointer.current_location(); - *self.cached_pointer_location.borrow_mut() = - Some((loc.x.round() as i32, loc.y.round() as i32)); - *self.cached_xdisplay.borrow_mut() = state.xdisplay; - *self.cached_keyboard_focus.borrow_mut() = state + self.cached_pointer_location.set(Some((loc.x.round() as i32, loc.y.round() as i32))); + self.cached_xdisplay.set(state.xdisplay); + self.cached_keyboard_focus.set(state .keyboard .current_focus() - .and_then(|focus| state.keyboard_focus_to_window_id(&focus)); + .and_then(|focus| state.keyboard_focus_to_window_id(&focus))); // Update cached outputs let outputs: Vec<_> = state @@ -158,7 +163,7 @@ impl WaylandBackend { }, }) .collect(); - *self.cached_outputs.borrow_mut() = outputs; + self.cached_outputs.set(outputs); // Update cached input devices let devices: Vec<_> = state @@ -191,7 +196,7 @@ impl WaylandBackend { format!("{} (capabilities: {})", d.name(), caps.join(", ")) }) .collect(); - *self.cached_input_devices.borrow_mut() = devices; + self.cached_input_devices.set(devices); // Update cached display names and modes let displays = state.list_displays(); @@ -199,32 +204,34 @@ impl WaylandBackend { for display in &displays { display_modes.insert(display.clone(), state.list_display_modes(display)); } - *self.cached_displays.borrow_mut() = displays; - *self.cached_display_modes.borrow_mut() = display_modes; + self.cached_displays.set(displays); + self.cached_display_modes.set(display_modes); } // -- Public query methods (use cached values) -- /// List all connected display names (cached). pub fn list_displays(&self) -> Vec { - self.cached_displays.borrow().clone() + let displays = self.cached_displays.take(); + let clone = displays.clone(); + self.cached_displays.set(displays); + clone } /// List available display modes for a display (cached). pub fn list_display_modes(&self, display: &str) -> Vec { - self.cached_display_modes - .borrow() - .get(display) - .cloned() - .unwrap_or_default() + let modes = self.cached_display_modes.take(); + let res = modes.get(display).cloned().unwrap_or_default(); + self.cached_display_modes.set(modes); + res } pub fn xdisplay(&self) -> Option { - *self.cached_xdisplay.borrow() + self.cached_xdisplay.get() } pub fn pointer_location(&self) -> Option<(i32, i32)> { - *self.cached_pointer_location.borrow() + self.cached_pointer_location.get() } pub fn window_title(&self, _window: WindowId) -> Option { @@ -233,34 +240,24 @@ impl WaylandBackend { } pub fn is_keyboard_focused_on(&self, window: WindowId) -> bool { - self.cached_keyboard_focus - .borrow() - .is_some_and(|focus| focus == window) + self.cached_keyboard_focus.get().is_some_and(|focus| focus == window) } pub fn close_window(&self, window: WindowId) -> bool { - self.pending_ops - .borrow_mut() - .push(WmCommand::CloseWindow(window)); + self.push_op(WmCommand::CloseWindow(window)); true // optimistic — actual close happens during flush } pub fn clear_keyboard_focus(&self) { - self.pending_ops - .borrow_mut() - .push(WmCommand::ClearKeyboardFocus); + self.push_op(WmCommand::ClearKeyboardFocus); } pub fn set_cursor_icon_override(&self, icon: Option) { - self.pending_ops - .borrow_mut() - .push(WmCommand::SetCursorIconOverride(icon)); + self.push_op(WmCommand::SetCursorIconOverride(icon)); } pub fn warp_pointer(&self, x: f64, y: f64) { - self.pending_ops - .borrow_mut() - .push(WmCommand::WarpPointer(x, y)); + self.push_op(WmCommand::WarpPointer(x, y)); } } @@ -272,39 +269,27 @@ impl Default for WaylandBackend { impl BackendOps for WaylandBackend { fn resize_window(&self, window: WindowId, rect: Rect) { - self.pending_ops - .borrow_mut() - .push(WmCommand::ResizeWindow(window, rect)); + self.push_op(WmCommand::ResizeWindow(window, rect)); } fn raise_window(&self, window: WindowId) { - self.pending_ops - .borrow_mut() - .push(WmCommand::RaiseWindow(window)); + self.push_op(WmCommand::RaiseWindow(window)); } fn restack(&self, windows: &[WindowId]) { - self.pending_ops - .borrow_mut() - .push(WmCommand::Restack(windows.to_vec())); + self.push_op(WmCommand::Restack(windows.to_vec())); } fn set_focus(&self, window: WindowId) { - self.pending_ops - .borrow_mut() - .push(WmCommand::SetFocus(window)); + self.push_op(WmCommand::SetFocus(window)); } fn map_window(&self, window: WindowId) { - self.pending_ops - .borrow_mut() - .push(WmCommand::MapWindow(window)); + self.push_op(WmCommand::MapWindow(window)); } fn unmap_window(&self, window: WindowId) { - self.pending_ops - .borrow_mut() - .push(WmCommand::UnmapWindow(window)); + self.push_op(WmCommand::UnmapWindow(window)); } fn window_exists(&self, _window: WindowId) -> bool { @@ -314,17 +299,15 @@ impl BackendOps for WaylandBackend { } fn flush(&self) { - self.pending_ops.borrow_mut().push(WmCommand::Flush); + self.push_op(WmCommand::Flush); } fn pointer_location(&self) -> Option<(i32, i32)> { - *self.cached_pointer_location.borrow() + self.cached_pointer_location.get() } fn warp_pointer(&self, x: f64, y: f64) { - self.pending_ops - .borrow_mut() - .push(WmCommand::WarpPointer(x, y)); + self.push_op(WmCommand::WarpPointer(x, y)); } fn window_title(&self, _window: WindowId) -> Option { @@ -339,30 +322,32 @@ impl BackendOps for WaylandBackend { options: Option<&str>, model: Option<&str>, ) { - self.pending_ops - .borrow_mut() - .push(WmCommand::SetKeyboardLayout { - layout: layout.to_owned(), - variant: variant.to_owned(), - options: options.map(|s| s.to_owned()), - model: model.map(|s| s.to_owned()), - }); + self.push_op(WmCommand::SetKeyboardLayout { + layout: layout.to_owned(), + variant: variant.to_owned(), + options: options.map(|s| s.to_owned()), + model: model.map(|s| s.to_owned()), + }); } fn set_monitor_config(&self, name: &str, config: &crate::config::config_toml::MonitorConfig) { - self.pending_ops - .borrow_mut() - .push(WmCommand::SetMonitorConfig { - name: name.to_owned(), - config: config.clone(), - }); + self.push_op(WmCommand::SetMonitorConfig { + name: name.to_owned(), + config: config.clone(), + }); } fn get_outputs(&self) -> Vec { - self.cached_outputs.borrow().clone() + let outputs = self.cached_outputs.take(); + let clone = outputs.clone(); + self.cached_outputs.set(outputs); + clone } fn get_input_devices(&self) -> Vec { - self.cached_input_devices.borrow().clone() + let devices = self.cached_input_devices.take(); + let clone = devices.clone(); + self.cached_input_devices.set(devices); + clone } } From 1454be52b96edb1eb3cd7c36ccc9cc13a0a92e22 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:59:31 +0000 Subject: [PATCH 2/2] perf(wayland): Optimize `RefCell` usage by using `Cell` for `WaylandBackend` primitive types In `WaylandBackend` (`src/backend/wayland/mod.rs`), several fields were wrapped in `RefCell`, requiring runtime borrow checks for each read and write operation, which are frequently called in hot paths. This commit replaces `RefCell` with `Cell` across `WaylandBackend`'s fields that are primitive or `Copy` types (`cached_pointer_location`, `cached_xdisplay`, `cached_keyboard_focus`). This entirely eliminates runtime borrow-checking overhead for these fields without introducing any `take`/`clone` overhead. `Vec` and `HashMap` fields remain `RefCell` since using `Cell` would require an expensive clone on each read. The `push_op` helper was introduced to encapsulate `borrow_mut().push()` on `pending_ops`. Note: * Other instances of `Rc` in `src/wayland/render/drm/cursor.rs` are kept as `Rc` (non-atomic reference counting) because switching to `Arc` would decrease performance due to atomic operations. * The `Mutex` on `CursorImageAttributes` in `src/wayland/common.rs` is preserved since it's stored in a Smithay `DataMap`, which strictly requires types to be `Send + Sync`. Co-authored-by: paperbenni <15818888+paperbenni@users.noreply.github.com> --- src/backend/wayland/mod.rs | 60 ++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/src/backend/wayland/mod.rs b/src/backend/wayland/mod.rs index 8670fe2e..eb2cbf50 100644 --- a/src/backend/wayland/mod.rs +++ b/src/backend/wayland/mod.rs @@ -55,7 +55,7 @@ pub mod compositor; use crate::backend::BackendOps; use crate::backend::BackendOutputInfo; use crate::types::{Rect, WindowId}; -use std::cell::Cell; +use std::cell::{Cell, RefCell}; use std::collections::HashMap; use crate::backend::wayland::compositor::WaylandState; @@ -94,19 +94,19 @@ pub enum WmCommand { /// unsafe code in the backend bridge. pub struct WaylandBackend { /// Pending operations from WM logic - pending_ops: Cell>, + pending_ops: RefCell>, /// Cached pointer location, updated by sync_cache each event loop tick cached_pointer_location: Cell>, /// Cached xdisplay, updated by sync_cache cached_xdisplay: Cell>, /// Cached output info, updated by sync_cache - cached_outputs: Cell>, + cached_outputs: RefCell>, /// Cached input device list, updated by sync_cache - cached_input_devices: Cell>, + cached_input_devices: RefCell>, /// Cached display names, updated by sync_cache - cached_displays: Cell>, + cached_displays: RefCell>, /// Cached display modes per display name, updated by sync_cache - cached_display_modes: Cell>>, + cached_display_modes: RefCell>>, /// Cached keyboard focus window, updated by sync_cache cached_keyboard_focus: Cell>, } @@ -114,26 +114,24 @@ pub struct WaylandBackend { impl WaylandBackend { pub fn new() -> Self { Self { - pending_ops: Cell::new(Vec::new()), + pending_ops: RefCell::new(Vec::new()), cached_pointer_location: Cell::new(None), cached_xdisplay: Cell::new(None), - cached_outputs: Cell::new(Vec::new()), - cached_input_devices: Cell::new(Vec::new()), - cached_displays: Cell::new(Vec::new()), - cached_display_modes: Cell::new(HashMap::new()), + cached_outputs: RefCell::new(Vec::new()), + cached_input_devices: RefCell::new(Vec::new()), + cached_displays: RefCell::new(Vec::new()), + cached_display_modes: RefCell::new(HashMap::new()), cached_keyboard_focus: Cell::new(None), } } fn push_op(&self, cmd: WmCommand) { - let mut ops = self.pending_ops.take(); - ops.push(cmd); - self.pending_ops.set(ops); + self.pending_ops.borrow_mut().push(cmd); } /// Drain all pending commands. Called from the event loop after WM logic. pub fn drain_ops(&self) -> Vec { - self.pending_ops.take() + std::mem::take(&mut *self.pending_ops.borrow_mut()) } /// Update cached values from WaylandState. Called after WM logic completes. @@ -163,7 +161,7 @@ impl WaylandBackend { }, }) .collect(); - self.cached_outputs.set(outputs); + *self.cached_outputs.borrow_mut() = outputs; // Update cached input devices let devices: Vec<_> = state @@ -196,7 +194,7 @@ impl WaylandBackend { format!("{} (capabilities: {})", d.name(), caps.join(", ")) }) .collect(); - self.cached_input_devices.set(devices); + *self.cached_input_devices.borrow_mut() = devices; // Update cached display names and modes let displays = state.list_displays(); @@ -204,26 +202,24 @@ impl WaylandBackend { for display in &displays { display_modes.insert(display.clone(), state.list_display_modes(display)); } - self.cached_displays.set(displays); - self.cached_display_modes.set(display_modes); + *self.cached_displays.borrow_mut() = displays; + *self.cached_display_modes.borrow_mut() = display_modes; } // -- Public query methods (use cached values) -- /// List all connected display names (cached). pub fn list_displays(&self) -> Vec { - let displays = self.cached_displays.take(); - let clone = displays.clone(); - self.cached_displays.set(displays); - clone + self.cached_displays.borrow().clone() } /// List available display modes for a display (cached). pub fn list_display_modes(&self, display: &str) -> Vec { - let modes = self.cached_display_modes.take(); - let res = modes.get(display).cloned().unwrap_or_default(); - self.cached_display_modes.set(modes); - res + self.cached_display_modes + .borrow() + .get(display) + .cloned() + .unwrap_or_default() } pub fn xdisplay(&self) -> Option { @@ -338,16 +334,10 @@ impl BackendOps for WaylandBackend { } fn get_outputs(&self) -> Vec { - let outputs = self.cached_outputs.take(); - let clone = outputs.clone(); - self.cached_outputs.set(outputs); - clone + self.cached_outputs.borrow().clone() } fn get_input_devices(&self) -> Vec { - let devices = self.cached_input_devices.take(); - let clone = devices.clone(); - self.cached_input_devices.set(devices); - clone + self.cached_input_devices.borrow().clone() } }