diff --git a/Cargo.lock b/Cargo.lock index aec3b9902..de7d1c3d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1188,7 +1188,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1275,7 +1275,7 @@ checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ "bitflags 2.11.0", "bytemuck", - "drm-ffi 0.9.0", + "drm-ffi 0.9.1", "drm-fourcc", "libc", "rustix 0.38.44", @@ -1293,12 +1293,12 @@ dependencies = [ [[package]] name = "drm-ffi" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b" +checksum = "51a91c9b32ac4e8105dec255e849e0d66e27d7c34d184364fb93e469db08f690" dependencies = [ - "drm-sys 0.8.0", - "rustix 0.38.44", + "drm-sys 0.8.1", + "rustix 1.1.4", ] [[package]] @@ -1319,12 +1319,12 @@ dependencies = [ [[package]] name = "drm-sys" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c" +checksum = "ecc8e1361066d91f5ffccff060a3c3be9c3ecde15be2959c1937595f7a82a9f8" dependencies = [ "libc", - "linux-raw-sys 0.6.5", + "linux-raw-sys 0.9.4", ] [[package]] @@ -1502,7 +1502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -2689,7 +2689,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -2812,7 +2812,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3060,6 +3060,12 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -3422,7 +3428,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4160,20 +4166,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", + "rand_chacha", "rand_core 0.6.4", ] -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - [[package]] name = "rand" version = "0.10.0" @@ -4195,16 +4191,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -4214,15 +4200,6 @@ dependencies = [ "getrandom 0.2.17", ] -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - [[package]] name = "rand_core" version = "0.10.0" @@ -4518,7 +4495,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -4813,7 +4790,7 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=cdc03f7#cdc03f73ce57d3352c0c57a69971e3e16544051c" +source = "git+https://github.com/smithay/smithay.git?rev=347b2b3#347b2b30225b0425f0b44dc4c590e768f7dbe73e" dependencies = [ "aliasable", "appendlist", @@ -4824,8 +4801,9 @@ dependencies = [ "cursor-icon", "downcast-rs", "drm 0.14.1", - "drm-ffi 0.9.0", + "drm-ffi 0.9.1", "drm-fourcc", + "drm-sys 0.8.1", "encoding_rs", "errno", "gbm", @@ -4841,7 +4819,7 @@ dependencies = [ "pkg-config", "portable-atomic", "profiling", - "rand 0.9.2", + "rand 0.10.0", "reis", "rustix 1.1.4", "scopeguard", @@ -5107,7 +5085,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -5406,7 +5384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f7c95348f20c1c913d72157b3c6dee6ea3e30b3d19502c5a7f6d3f160dacbf" dependencies = [ "cc", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fb9fa0bcb..620c3c07b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,4 +144,4 @@ lto = "fat" cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "cdc03f7" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "347b2b3" } diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index 418a16f3b..bad30910d 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -27,7 +27,7 @@ use smithay::{ output::{DrmOutputManager, LockedDrmOutputManager}, }, egl::{EGLContext, EGLDevice, EGLDisplay, context::ContextPriority}, - renderer::glow::GlowRenderer, + renderer::{Renderer, glow::GlowRenderer}, session::{Session, libseat::LibSeatSession}, }, desktop::utils::OutputPresentationFeedback, @@ -45,7 +45,7 @@ use smithay::{ drm_syncobj::supports_syncobj_eventfd, }, }; -use tracing::{error, info, warn}; +use tracing::{debug, error, info, warn}; use wayland_backend::server::ClientId; use std::{ @@ -322,7 +322,7 @@ impl State { .find_map(|(crtc, surface)| (surface.connector == conn).then_some(crtc)) .cloned() { - device.inner.surfaces.remove(&crtc).unwrap(); + device.inner.surfaces.remove(&crtc).unwrap().drop_and_join(); } if !changes.added.iter().any(|(c, _)| c == &conn) { @@ -929,6 +929,12 @@ impl LockedDevice<'_> { return Err(err.into()); } } + + // This renderer draws only infrequently; drop the imports it just + // cached so they don't pin client buffers in VRAM until its next draw. + if let Err(err) = renderer.invalidate_caches() { + debug!(?err, "Failed to invalidate main-thread renderer caches"); + } } Ok(()) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index df7113872..a629a3f16 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -79,6 +79,7 @@ pub struct KmsState { pub syncobj_state: Option, pub dmabuf_global: Option, + pending_renderer_cleanup: bool, } pub struct KmsGuard<'a> { @@ -137,6 +138,7 @@ pub fn init_backend( syncobj_state: None, dmabuf_global: None, + pending_renderer_cleanup: false, }); // manually add already present gpus @@ -700,6 +702,27 @@ impl KmsState { Ok(node) } + /// Request a drain of the main-thread renderers' destruction queues on the next refresh. + /// + /// Destroying a surface or buffer drops the textures these renderers imported from it, + /// which only *queues* the GL deletions on their contexts; the queues are flushed by + /// rendering or an explicit drain. Those renderers may not draw again for a long time, + /// so until the drain runs the dead client's buffers stay pinned in VRAM. + pub fn schedule_renderer_cleanup(&mut self) { + self.pending_renderer_cleanup = true; + } + + /// Drain the GL destruction queues of the main-thread renderers, if scheduled. + pub fn run_scheduled_renderer_cleanup(&mut self) { + if !self.pending_renderer_cleanup || !self.session.is_active() { + return; + } + self.pending_renderer_cleanup = false; + if let Err(err) = self.api.cleanup_texture_cache() { + debug!(?err, "Failed to drain main-thread renderer cleanup queue"); + } + } + pub fn schedule_render(&mut self, output: &Output) { for surface in self .drm_devices @@ -1285,6 +1308,25 @@ impl KmsGuard<'_> { } } + self.invalidate_renderer_caches(); + Ok(()) } + + /// Drop all cached imports held by the main-thread renderers, along with the + /// framebuffers cached for copying between a render and a target node. + /// + /// Unlike the per-output render threads (which draw every frame), these renderers + /// only draw during output (re-)configuration. Between those infrequent draws their + /// import caches provide no benefit yet keep live clients' buffers pinned in VRAM; + /// the next render re-imports what it needs. Imports belonging to clients that have + /// already exited are released by the destruction-scheduled drain. + fn invalidate_renderer_caches(&mut self) { + if !self.session.is_active() { + return; + } + if let Err(err) = self.api.invalidate_caches() { + debug!(?err, "Failed to invalidate main-thread renderer caches"); + } + } } diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index cbc893813..0489c183a 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -54,7 +54,7 @@ use smithay::{ GlesRenderbuffer, GlesRenderer, GlesTexture, Uniform, element::TextureShaderElement, }, glow::GlowRenderer, - multigpu::{ApiDevice, Error as MultiError, GpuManager}, + multigpu::{Error as MultiError, GpuManager}, sync::SyncPoint, utils::with_renderer_surface_state, }, @@ -739,6 +739,7 @@ impl SurfaceThreadState { fn node_removed(&mut self, node: DrmNode) { self.api.as_mut().remove_node(&node); + self.postprocess_textures.remove(&node); // force enumeration let _ = self.api.devices(); } @@ -1408,9 +1409,7 @@ impl SurfaceThreadState { } } - for device in self.api.devices_mut()? { - device.renderer_mut().cleanup_texture_cache()?; - } + self.api.cleanup_texture_cache()?; Ok(()) } diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index da01b270a..ff48f3ff7 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -266,6 +266,7 @@ pub struct CursorStateInner { cursors: HashMap, current_image: Option, image_cache: Vec<(Image, MemoryRenderBuffer)>, + last_cursor_icon: Option, hidden: bool, idle_timer: Option, @@ -320,6 +321,7 @@ impl Default for CursorStateInner { cursors: HashMap::new(), current_image: None, image_cache: Vec::new(), + last_cursor_icon: None, hidden: false, idle_timer: None, @@ -363,6 +365,11 @@ pub fn draw_cursor( return; } + if state.last_cursor_icon != Some(current_cursor) { + state.image_cache.clear(); + state.last_cursor_icon = Some(current_cursor); + } + let integer_scale = (scale.x.max(scale.y) * buffer_scale).ceil() as u32; let frame = state .get_named_cursor(current_cursor) diff --git a/src/input/mod.rs b/src/input/mod.rs index 39bff9dc0..89026b490 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -56,6 +56,7 @@ use smithay::{ GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, PointerGrab, PointerHandle, RelativeMotionEvent, }, + tablet::{self, TabletDescriptor, TabletSeatTrait}, touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent}, }, output::Output, @@ -70,7 +71,6 @@ use smithay::{ keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, pointer_constraints::{PointerConstraint, with_pointer_constraint}, seat::WaylandFocus, - tablet_manager::{TabletDescriptor, TabletSeatTrait}, }, }; use tracing::{error, trace}; @@ -179,7 +179,7 @@ impl State { let led_state = seat.get_keyboard().unwrap().led_state(); seat.devices().add_device(&device, led_state); if device.has_capability(DeviceCapability::TabletTool) { - seat.tablet_seat().add_tablet::( + seat.tablet_seat().add_wp_tablet( &self.common.display_handle, &TabletDescriptor::from(&device), ); @@ -1441,37 +1441,21 @@ impl State { let tablet_seat = seat.tablet_seat(); - let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device())); - let tool = tablet_seat.get_tool(&event.tool()); - - if let (Some(tablet), Some(tool)) = (tablet, tool) { - if event.pressure_has_changed() { - tool.pressure(event.pressure()); - } - if event.distance_has_changed() { - tool.distance(event.distance()); - } - if event.tilt_has_changed() { - tool.tilt(event.tilt()); - } - if event.slider_has_changed() { - tool.slider_position(event.slider_position()); - } - if event.rotation_has_changed() { - tool.rotation(event.rotation()); - } - if event.wheel_has_changed() { - tool.wheel(event.wheel_delta(), event.wheel_delta_discrete()); - } + if let Some(tool) = tablet_seat.get_tool(&event.tool()) { + tool.axis(self, axis_frame(&event)); tool.motion( - position.as_logical(), + self, under .and_then(|(f, loc)| f.wl_surface().map(|s| (s.into_owned(), loc))), - &tablet, - SERIAL_COUNTER.next_serial(), - event.time_msec(), + &tablet::tool::MotionEvent { + location: position.as_logical(), + serial: SERIAL_COUNTER.next_serial(), + time: event.time_msec(), + }, ); + + tool.frame(self, event.time_msec()); } } } @@ -1509,25 +1493,35 @@ impl State { let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device())); let dh = self.common.display_handle.clone(); - let tool = tablet_seat.add_tool::(self, &dh, &event.tool()); + let tool = tablet_seat + .get_tool(&event.tool()) + .unwrap_or_else(|| tablet_seat.add_wp_tool(self, &dh, &event.tool())); if let Some(tablet) = tablet { match event.state() { - ProximityState::In => { - if let Some(under) = under.and_then(|(f, loc)| { + ProximityState::In => tool.proximity_in( + self, + under.and_then(|(f, loc)| { f.wl_surface().map(|s| (s.into_owned(), loc)) - }) { - tool.proximity_in( - position.as_logical(), - under, - &tablet, - SERIAL_COUNTER.next_serial(), - event.time_msec(), - ) - } - } - ProximityState::Out => tool.proximity_out(event.time_msec()), + }), + tablet, + &tablet::tool::ProximityInEvent { + location: position.as_logical(), + axis: Some(axis_frame(&event)), + serial: SERIAL_COUNTER.next_serial(), + time: event.time_msec(), + }, + ), + ProximityState::Out => tool.proximity_out( + self, + &tablet::tool::ProximityOutEvent { + serial: SERIAL_COUNTER.next_serial(), + time: event.time_msec(), + }, + ), } + + tool.frame(self, event.time_msec()); } } } @@ -1544,13 +1538,23 @@ impl State { notify_cursor_activity(self, &seat); if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) { match event.tip_state() { - TabletToolTipState::Down => { - tool.tip_down(SERIAL_COUNTER.next_serial(), event.time_msec()); - } - TabletToolTipState::Up => { - tool.tip_up(event.time_msec()); - } + TabletToolTipState::Down => tool.down( + self, + &tablet::tool::DownEvent { + serial: SERIAL_COUNTER.next_serial(), + time: event.time_msec(), + }, + ), + TabletToolTipState::Up => tool.up( + self, + &tablet::tool::UpEvent { + serial: SERIAL_COUNTER.next_serial(), + time: event.time_msec(), + }, + ), } + + tool.frame(self, event.time_msec()); } } } @@ -1567,11 +1571,16 @@ impl State { notify_cursor_activity(self, &seat); if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) { tool.button( - event.button(), - event.button_state(), - SERIAL_COUNTER.next_serial(), - event.time_msec(), + self, + &tablet::tool::ButtonEvent { + serial: SERIAL_COUNTER.next_serial(), + button: event.button(), + state: event.button_state(), + time: event.time_msec(), + }, ); + + tool.frame(self, event.time_msec()); } } } @@ -2506,6 +2515,23 @@ where .as_global() } +fn axis_frame(event: &E) -> tablet::tool::AxisFrame +where + B: InputBackend, + E: TabletToolEvent, +{ + tablet::tool::AxisFrame { + pressure: event.pressure_has_changed().then(|| event.pressure()), + distance: event.distance_has_changed().then(|| event.distance()), + tilt: event.tilt_has_changed().then(|| event.tilt()), + rotation: event.rotation_has_changed().then(|| event.rotation()), + slider: event.slider_has_changed().then(|| event.slider_position()), + wheel: event + .wheel_has_changed() + .then(|| (event.wheel_delta(), event.wheel_delta_discrete())), + } +} + // TODO Is it possible to determine mapping for external touchscreen? // Support map_to_region like sway? fn mapped_output_for_device<'a, D: Device + 'static>( diff --git a/src/lib.rs b/src/lib.rs index f7282942b..8ceb309c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -358,5 +358,8 @@ fn refresh(state: &mut State) { OverlapNotifyState::refresh(state); state.common.update_x11_stacking_order(); KeyboardLayoutState::refresh(state); + if let state::BackendData::Kms(kms) = &mut state.backend { + kms.run_scheduled_renderer_cleanup(); + } state.last_refresh = LastRefresh::At(Instant::now()); } diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index cab37a858..70a7f8b1d 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -376,7 +376,7 @@ fn update_focus_state( { with_pointer_constraint(&surface, &pointer, |constraint| { if let Some(constraint) = constraint { - constraint.deactivate(); + constraint.deactivate(state, &surface, &pointer); } }); } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index d9f959768..b0987e223 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -611,6 +611,7 @@ impl WorkspaceSet { self.workspaces[self.active].refresh(); } self.sticky_layer.refresh(); + self.minimized_windows.retain(|w| w.alive()); } fn add_empty_workspace(&mut self, state: &mut WorkspaceUpdateGuard) { @@ -929,6 +930,9 @@ impl Workspaces { return; } + if let Some(zoom_state) = output.user_data().get::>() { + zoom_state.lock().unwrap().output_leave(output); + } if let Some(set) = self.sets.shift_remove(output) { { let map = layer_map_for_output(output); @@ -2594,6 +2598,16 @@ impl Shell { .retain(|pending| pending.surface.alive()); self.pending_windows .retain(|pending| pending.surface.alive()); + self.pending_activations.retain(|key, _| match key { + ActivationKey::Wayland(surface) => { + use smithay::reexports::wayland_server::Resource; + surface.is_alive() + } + ActivationKey::X11(_) => self + .pending_windows + .iter() + .any(|p| &ActivationKey::from(&p.surface) == key), + }); } pub fn update_pointer_position(&mut self, location: Point, output: &Output) { diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 84a020ff1..022b0869e 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -208,6 +208,17 @@ impl MinimizedWindow { } } +impl IsAlive for MinimizedWindow { + fn alive(&self) -> bool { + match self { + MinimizedWindow::Fullscreen { surface, .. } => surface.alive(), + MinimizedWindow::Floating { window, .. } | MinimizedWindow::Tiling { window, .. } => { + window.alive() + } + } + } +} + #[derive(Debug, Clone)] pub struct FullscreenSurface { pub surface: CosmicSurface, @@ -472,10 +483,8 @@ impl Workspace { #[profiling::function] pub fn refresh(&mut self) { - // seems it removes dead windows - // self.fullscreen.take_if(|w| !w.alive()); self.fullscreen_surfaces.retain(|w| w.alive()); - + self.minimized_windows.retain(|w| w.alive()); self.floating_layer.refresh(); self.tiling_layer.refresh(); } diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index ef81053fa..dd61ecc7a 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -119,6 +119,10 @@ impl OutputZoomState { } } + pub fn output_leave(&self, output: &Output) { + SpaceElement::output_leave(&self.element, output); + } + pub fn current_focal_point(&mut self) -> Point { self.focal_point } diff --git a/src/state.rs b/src/state.rs index 8cfee1a96..8cdb6296b 100644 --- a/src/state.rs +++ b/src/state.rs @@ -177,18 +177,20 @@ impl ClientData for ClientState { fn disconnected(&self, client_id: ClientId, _reason: DisconnectReason) { self.evlh.insert_idle(move |state| { if let BackendData::Kms(kms_state) = &mut state.backend { + let primary = *kms_state.primary_node.read().unwrap(); + // A multi-GPU client can be active on several nodes, so remove its + // id from every device it imported on + let mut freed_device = false; for device in kms_state.drm_devices.values_mut() { if device.inner.active_clients.remove(&client_id) - && !device - .inner - .in_use(kms_state.primary_node.read().unwrap().as_ref()) + && !device.inner.in_use(primary.as_ref()) { - if let Err(err) = kms_state.refresh_used_devices() { - warn!(?err, "Failed to init devices."); - }; - break; + freed_device = true; } } + if freed_device && let Err(err) = kms_state.refresh_used_devices() { + warn!(?err, "Failed to init devices."); + } } }); self.evls.wakeup(); @@ -567,7 +569,10 @@ impl LockedBackend<'_> { shell.seats.iter(), workspace_state, xdg_activation_state, - ) + ); + if let Some(session_lock) = &mut shell.session_lock { + session_lock.surfaces.remove(output); + } } } diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index f1f1f5f45..6148468e2 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -105,8 +105,19 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { }) .with_context(|| "Failed to get renderer for screenshot") .and_then(|renderer| match renderer { + // No invalidation for Glow: the winit/X11 dev backends redraw every + // frame, so their caches stay useful, and the KMS software fallback + // holds no VRAM. RendererRef::Glow(renderer) => render_window(renderer, surface), - RendererRef::GlMulti(mut renderer) => render_window(&mut renderer, surface), + RendererRef::GlMulti(mut renderer) => { + let render_result = render_window(&mut renderer, surface); + // This main-thread renderer draws only on demand; its cached imports + // would pin client buffers in VRAM until it next draws. + if let Err(err) = renderer.invalidate_caches() { + warn!(?err, "Failed to invalidate renderer caches"); + } + render_result + } }); if let Err(err) = res { warn!(?err, "Failed to take screenshot") diff --git a/src/wayland/handlers/buffer.rs b/src/wayland/handlers/buffer.rs index 184eee4d6..e60f04cfc 100644 --- a/src/wayland/handlers/buffer.rs +++ b/src/wayland/handlers/buffer.rs @@ -1,10 +1,14 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::state::State; +use crate::state::{BackendData, State}; use smithay::{ reexports::wayland_server::protocol::wl_buffer::WlBuffer, wayland::buffer::BufferHandler, }; impl BufferHandler for State { - fn buffer_destroyed(&mut self, _buffer: &WlBuffer) {} + fn buffer_destroyed(&mut self, _buffer: &WlBuffer) { + if let BackendData::Kms(kms) = &mut self.backend { + kms.schedule_renderer_cleanup(); + } + } } diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index 12bc2e565..cbd67af1f 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{shell::grabs::SeatMoveGrabState, state::ClientState, utils::prelude::*}; +use crate::{ + shell::grabs::SeatMoveGrabState, + state::{BackendData, ClientState}, + utils::prelude::*, +}; use calloop::Interest; use smithay::{ backend::renderer::{ @@ -370,6 +374,12 @@ impl CompositorHandler for State { } } } + + fn destroyed(&mut self, _surface: &WlSurface) { + if let BackendData::Kms(kms) = &mut self.backend { + kms.schedule_renderer_cleanup(); + } + } } impl State { diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index f8b308e29..985466fda 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -273,25 +273,36 @@ where .as_mut() .map(|(_, tex)| renderer.bind(tex).map_err(DTError::Rendering)) .transpose()?; - let (result, buffers) = render_fn( + let submit_result = match render_fn( &frame.buffer(), renderer, fb.as_mut(), dt, age, frame.damage(), - )?; + ) { + Ok((result, buffers)) => submit_buffer( + frame, + renderer, + fb.as_mut(), + transform, + result.damage.map(|x| x.as_slice()), + result.sync, + buffers, + ) + .map_err(DTError::Rendering), + Err(err) => Err(err), + }; - submit_buffer( - frame, - renderer, - fb.as_mut(), - transform, - result.damage.map(|x| x.as_slice()), - result.sync, - buffers, - ) - .map_err(DTError::Rendering) + std::mem::drop(fb); + // Rendering flushes only the device it drew on, so drain the rest here: this + // main-thread renderer may not run again for a long time, and until it does the + // imports dropped since the last capture stay pinned in VRAM. + if let Err(err) = renderer.cleanup_texture_cache() { + warn!(?err, "Failed to drain renderer cleanup queue"); + } + + submit_result } pub fn render_workspace_to_buffer( diff --git a/src/wayland/handlers/pointer_constraints.rs b/src/wayland/handlers/pointer_constraints.rs index 9ce5f63a8..ad9f309cd 100644 --- a/src/wayland/handlers/pointer_constraints.rs +++ b/src/wayland/handlers/pointer_constraints.rs @@ -5,7 +5,10 @@ use smithay::{ input::pointer::PointerHandle, reexports::wayland_server::protocol::wl_surface::WlSurface, utils::{Logical, Point}, - wayland::{pointer_constraints::PointerConstraintsHandler, seat::WaylandFocus}, + wayland::{ + pointer_constraints::{PointerConstraint, PointerConstraintsHandler}, + seat::WaylandFocus, + }, }; pub use smithay::wayland::pointer_constraints::{PointerConstraintRef, with_pointer_constraint}; @@ -76,25 +79,35 @@ impl PointerConstraintsHandler for State { } } - fn remove_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle) { - if with_pointer_constraint(surface, pointer, |constraint| constraint.is_none()) { - let seat = self + fn remove_constraint( + &mut self, + surface: &WlSurface, + pointer: &PointerHandle, + _constraint: Option<&PointerConstraint>, + ) { + // Smithay calls this with the constraint mutex held and, on the pointer-leave path, + // from inside pointer dispatch. `apply_cursor_hint` retakes that mutex and drives the + // pointer itself, so the warp has to happen once dispatch has unwound. + let surface = surface.clone(); + let pointer = pointer.clone(); + self.common.event_loop_handle.insert_idle(move |state| { + let seat = state .common .shell .read() .seats .iter() - .find(|s| s.get_pointer().as_ref() == Some(pointer)) + .find(|s| s.get_pointer().as_ref() == Some(&pointer)) .cloned(); if let Some(seat) = seat && let Some((hint_surface, hint_location)) = seat.pointer_constraint_hint() - && hint_surface == *surface + && hint_surface == surface { - self.apply_cursor_hint(surface, pointer, hint_location); + state.apply_cursor_hint(&surface, &pointer, hint_location); seat.set_pointer_constraint_hint(None); } - } + }); } fn cursor_position_hint( diff --git a/src/wayland/handlers/tablet_manager.rs b/src/wayland/handlers/tablet_manager.rs index 60469ba99..3a2cfae47 100644 --- a/src/wayland/handlers/tablet_manager.rs +++ b/src/wayland/handlers/tablet_manager.rs @@ -2,11 +2,14 @@ use crate::state::State; use smithay::{ - backend::input::TabletToolDescriptor, input::pointer::CursorImageStatus, - wayland::tablet_manager::TabletSeatHandler, + backend::input::TabletToolDescriptor, + input::{pointer::CursorImageStatus, tablet::TabletSeatHandler}, + reexports::wayland_server::protocol::wl_surface::WlSurface, }; impl TabletSeatHandler for State { + type ToolFocus = WlSurface; + fn tablet_tool_image(&mut self, _tool: &TabletToolDescriptor, _image: CursorImageStatus) { // TODO display cursor for each tablet tool }