From 091583ac84abac02967ae358cf9570ddfef63b31 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 9 Jan 2026 13:47:31 +0100 Subject: [PATCH] backend: Support wl-dmabuf v6 --- Cargo.lock | 30 ++- Cargo.toml | 2 +- src/backend/kms/device.rs | 32 +-- src/backend/kms/mod.rs | 186 ++++++++++++++---- src/backend/kms/surface/mod.rs | 64 +++--- src/backend/winit.rs | 5 +- src/backend/x11.rs | 5 +- src/shell/element/surface.rs | 5 +- src/state.rs | 11 +- src/utils/global.rs | 28 +++ src/utils/mod.rs | 1 + src/wayland/handlers/dmabuf.rs | 19 +- src/wayland/protocols/drm.rs | 91 ++++++--- .../protocols/output_configuration/mod.rs | 29 +-- 14 files changed, 298 insertions(+), 210 deletions(-) create mode 100644 src/utils/global.rs diff --git a/Cargo.lock b/Cargo.lock index fd733056..aec3b990 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -312,12 +312,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atomic_float" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a" - [[package]] name = "atomicwrites" version = "0.4.2" @@ -1194,7 +1188,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1508,7 +1502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2695,7 +2689,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2818,7 +2812,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3428,7 +3422,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4524,7 +4518,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4819,12 +4813,11 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=1ed69cb#1ed69cb55e9d0e9f38887c3b7d53d24365c4b14f" +source = "git+https://github.com/smithay/smithay.git?rev=cdc03f7#cdc03f73ce57d3352c0c57a69971e3e16544051c" dependencies = [ "aliasable", "appendlist", "ash", - "atomic_float", "bitflags 2.11.0", "calloop", "cc", @@ -4846,6 +4839,7 @@ dependencies = [ "libseat", "pixman", "pkg-config", + "portable-atomic", "profiling", "rand 0.9.2", "reis", @@ -5113,7 +5107,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5887,9 +5881,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.12" +version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ "bitflags 2.11.0", "wayland-backend", @@ -6132,7 +6126,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3f8a9e73..fb9fa0bc 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 = "1ed69cb" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "cdc03f7" } diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index 44682dc5..418a16f3 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -59,7 +59,7 @@ use std::{ time::Duration, }; -use super::{drm_helpers, socket::Socket, surface::Surface}; +use super::{drm_helpers, surface::Surface}; #[derive(Debug)] pub struct EGLInternals { @@ -98,14 +98,12 @@ pub struct Device { pub texture_formats: FormatSet, event_token: Option, - pub socket: Option, } #[derive(Debug)] struct ReusableDevice { leasing_global: Option, active_clients: HashSet, - socket: Option, } #[derive(Debug)] @@ -593,7 +591,7 @@ impl State { } } - pub fn device_removed(&mut self, dev: dev_t, dh: &DisplayHandle) -> Result<()> { + pub fn device_removed(&mut self, dev: dev_t, _dh: &DisplayHandle) -> Result<()> { let backend = self.backend.kms(); // we can't use DrmNode::from_node_id, because that assumes the node is still on sysfs let drm_node = backend @@ -616,13 +614,6 @@ impl State { if let Some(token) = device.event_token.take() { self.common.event_loop_handle.remove(token); } - if let Some(socket) = device.socket.take() { - self.common.event_loop_handle.remove(socket.token); - self.common - .dmabuf_state - .destroy_global::(dh, socket.dmabuf_global); - dh.remove_global::(socket.drm_global); - } backend.api.as_mut().remove_node(&device.inner.render_node); backend .primary_node @@ -755,23 +746,7 @@ impl Device { let ReusableDevice { leasing_global, active_clients, - socket, } = reuse.unwrap_or_else(|| { - let socket = match (!is_software) - .then(|| common.create_socket(dh, render_node, texture_formats.clone())) - .transpose() - { - Ok(socket) => socket, - Err(err) => { - warn!( - ?err, - "Failed to initialize hardware-acceleration for clients on {}.", - render_node, - ); - None - } - }; - let leasing_global = match (!is_software) .then(|| DrmLeaseState::new::(dh, &dev_node)) .transpose() @@ -790,7 +765,6 @@ impl Device { ReusableDevice { leasing_global, active_clients: HashSet::new(), - socket, } }); @@ -831,7 +805,6 @@ impl Device { texture_formats, event_token: Some(token), - socket, }) } @@ -892,7 +865,6 @@ impl Device { let device = ReusableDevice { leasing_global: self.inner.leasing_global, active_clients: self.inner.active_clients, - socket: self.socket, }; let state = OldDeviceState { diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 16559e4e..df711387 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -4,12 +4,15 @@ use crate::{ config::{CompOutputConfig, ScreenFilter}, shell::Shell, state::BackendData, - utils::{env::dev_var, prelude::*}, - wayland::protocols::output_power::OutputPowerState, + utils::{env::dev_var, global::remove_global_with_timer, prelude::*}, + wayland::protocols::{drm::WlDrmState, output_power::OutputPowerState}, }; -use anyhow::{Context, Result}; -use calloop::LoopSignal; +use anyhow::{self, Context, Result}; +use calloop::{ + LoopSignal, + timer::{TimeoutAction, Timer}, +}; use cosmic_comp_config::output::comp::{AdaptiveSync, OutputState}; use indexmap::IndexMap; use render::gles::GbmGlowBackend; @@ -32,11 +35,12 @@ use smithay::{ control::{Device as _, connector::Interface, crtc}, }, input::{self, Libinput}, + wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags, wayland_server::{Client, DisplayHandle}, }, utils::{Clock, DevPath, Monotonic, Size}, wayland::{ - dmabuf::DmabufGlobal, + dmabuf::{DmabufFeedbackBuilder, DmabufGlobal}, drm_syncobj::{DrmSyncobjState, supports_syncobj_eventfd}, relative_pointer::RelativePointerManagerState, }, @@ -48,12 +52,12 @@ use std::{ collections::{HashMap, HashSet}, path::Path, sync::{Arc, RwLock, atomic::AtomicBool}, + time::Duration, }; mod device; mod drm_helpers; pub mod render; -mod socket; mod surface; use device::*; pub(crate) use surface::Surface; @@ -74,6 +78,7 @@ pub struct KmsState { libinput: Libinput, pub syncobj_state: Option, + pub dmabuf_global: Option, } pub struct KmsGuard<'a> { @@ -131,6 +136,7 @@ pub fn init_backend( libinput: libinput_context, syncobj_state: None, + dmabuf_global: None, }); // manually add already present gpus @@ -142,9 +148,10 @@ pub fn init_backend( } } - if let Err(err) = state.backend.kms().select_primary_gpu(dh) { + if let Err(err) = state.select_primary_gpu(dh) { warn!("Failed to determine primary gpu: {}", err); } + state.update_default_feedback(); if let Err(err) = state.refresh_output_config() { info!( @@ -317,11 +324,13 @@ fn init_udev( { let backend = state.backend.kms(); - if matches!(event, UdevEvent::Added { .. } | UdevEvent::Removed { .. }) - && backend.primary_node.read().unwrap().is_none() - && let Err(err) = state.backend.kms().select_primary_gpu(&dh) - { - warn!("Failed to determine a new primary gpu: {}", err); + if matches!(event, UdevEvent::Added { .. } | UdevEvent::Removed { .. }) { + if backend.primary_node.read().unwrap().is_none() + && let Err(err) = state.select_primary_gpu(&dh) + { + warn!("Failed to determine a new primary gpu: {}", err); + } + state.update_default_feedback(); } } @@ -465,50 +474,118 @@ impl State { device.drm.pause(); } } -} -impl KmsState { fn select_primary_gpu(&mut self, dh: &DisplayHandle) -> Result<()> { // We don't have to check the allow/blocklist here, // as any disallowed devices won't be in `self.drm_devices`. - let mut primary_node = self.primary_node.write().unwrap(); + let kms = self.backend.kms(); + let mut primary_node = kms.primary_node.write().unwrap(); let _ = primary_node.take(); // if we error don't leave an old node in place - *primary_node = determine_primary_gpu(&self.drm_devices, self.session.seat())?; + *primary_node = determine_primary_gpu(&kms.drm_devices, kms.session.seat())?; if let Some(node) = *primary_node { info!("Using {} as primary gpu for rendering.", node); - self.software_renderer.take(); - } else if self.software_renderer.is_none() { + kms.software_renderer.take(); + + // setup minimal feedback. We will update it in `update_default_feedback` + let primary_formats = kms + .drm_devices + .values() + .find(|dev| dev.inner.render_node == node) + .unwrap() + .texture_formats + .clone(); + let feedback = DmabufFeedbackBuilder::new(node.dev_id(), primary_formats.clone()) + .build() + .unwrap(); + + if let Some(global) = kms.dmabuf_global.as_ref() { + self.common + .dmabuf_state + .set_default_feedback(global, &feedback); + } else { + let dmabuf_global = self + .common + .dmabuf_state + .create_global_with_default_feedback::(dh, &feedback); + kms.dmabuf_global = Some(dmabuf_global); + }; + + let device_path = node + .dev_path_with_type(NodeType::Render) + .or_else(|| node.dev_path()) + .ok_or(anyhow::anyhow!( + "Could not determine path for gpu node: {}", + node + ))?; + + if let Some(drm) = self.common.wl_drm_state.as_mut() { + drm.update_device(device_path, primary_formats); + } else { + self.common.wl_drm_state = Some(WlDrmState::new::( + dh, + device_path, + primary_formats, + kms.dmabuf_global.as_ref().unwrap(), + )); + } + } else if kms.software_renderer.is_none() { info!("Failed to find a suitable gpu, using software renderingr"); - self.software_renderer = match software_renderer() { + kms.software_renderer = match software_renderer() { Ok(renderer) => Some(renderer), Err(err) => { error!(?err, "Failed to initialize software EGL renderer."); None } }; + + if let Some(drm) = self.common.wl_drm_state.take() { + remove_global_with_timer(dh, &self.common.event_loop_handle, drm.global().clone()); + } + if let Some(global) = kms.dmabuf_global.take() { + self.common + .dmabuf_state + .disable_global::(dh, &global); + let source = Timer::from_duration(Duration::from_secs(5)); + let res = + self.common + .event_loop_handle + .insert_source(source, move |_, _, state| { + state + .common + .dmabuf_state + .destroy_global::(&state.common.display_handle, global); + TimeoutAction::Drop + }); + if let Err(err) = res { + tracing::error!( + "failed to insert timer source to destroy output global: {}", + err + ); + } + } } if !crate::utils::env::bool_var("COSMIC_DISABLE_SYNCOBJ").unwrap_or(false) { if let Some(primary_node) = primary_node .as_ref() .and_then(|node| node.node_with_type(NodeType::Primary).and_then(|x| x.ok())) - && let Some(device) = self.drm_devices.get(&primary_node) + && let Some(device) = kms.drm_devices.get(&primary_node) { let import_device = device.drm.device().device_fd().clone(); if supports_syncobj_eventfd(&import_device) { - if let Some(state) = self.syncobj_state.as_mut() { + if let Some(state) = kms.syncobj_state.as_mut() { state.update_device(import_device); } else { let syncobj_state = DrmSyncobjState::new::(dh, import_device); - self.syncobj_state = Some(syncobj_state); + kms.syncobj_state = Some(syncobj_state); } return Ok(()); } } - if let Some(old_state) = self.syncobj_state.take() { + if let Some(old_state) = kms.syncobj_state.take() { dh.remove_global::(old_state.into_global()); } } @@ -516,6 +593,45 @@ impl KmsState { Ok(()) } + fn update_default_feedback(&mut self) { + let kms = self.backend.kms(); + let primary_node = kms.primary_node.read().unwrap(); + if let Some(primary_node) = *primary_node { + let primary_formats = kms + .drm_devices + .values() + .find(|dev| dev.inner.render_node == primary_node) + .unwrap() + .texture_formats + .clone(); + + let mut feedback = + DmabufFeedbackBuilder::new(primary_node.dev_id(), primary_formats.clone()); + for dev in kms + .drm_devices + .values() + .filter(|dev| dev.inner.render_node != primary_node) + { + feedback = feedback.add_preference_tranche( + dev.inner.render_node.dev_id(), + TrancheFlags::Sampling, + dev.texture_formats.iter().cloned(), + 6..=6, + ); + } + + let default_feedback = feedback.build().unwrap(); + self.common.dmabuf_state.set_default_feedback( + kms.dmabuf_global + .as_ref() + .expect("Primary node but no dmabuf global?"), + &default_feedback, + ); + } + } +} + +impl KmsState { pub fn switch_vt(&mut self, num: i32) -> Result<(), anyhow::Error> { self.session.change_vt(num).map_err(Into::into) } @@ -523,29 +639,25 @@ impl KmsState { pub fn dmabuf_imported( &mut self, client: Option, - global: &DmabufGlobal, + _global: &DmabufGlobal, dmabuf: Dmabuf, ) -> Result { + let device_node = dmabuf + .node() + .unwrap_or_else(|| self.primary_node.read().unwrap().unwrap()); let mut device = self .drm_devices .values_mut() - .find(|device| { - device - .socket - .as_ref() - .map(|s| &s.dmabuf_global == global) - .unwrap_or(false) - }) - .context("Couldn't find gpu for dmabuf global")?; + .find(|dev| dev.inner.render_node == device_node) + .ok_or(anyhow::anyhow!( + "Unable to find device for node: {}", + device_node + ))?; // If device advertised to client doesn't support format/modifier, select // first device that does. This is needed for image-copy from // output/toplevel on a different node. - // - // TODO: After - // https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/268, - // only try the device specified explicitly by the client, if set. - if !device.texture_formats.contains(&dmabuf.format()) { + if dmabuf.node().is_none() && !device.texture_formats.contains(&dmabuf.format()) { device = self .drm_devices .values_mut() diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index eb232377..cbc89381 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -1529,7 +1529,7 @@ fn get_surface_dmabuf_feedback( render_node: DrmNode, target_node: DrmNode, render_formats: FormatSet, - _target_formats: FormatSet, + target_formats: FormatSet, primary_plane_formats: FormatSet, overlay_plane_formats: Option, ) -> SurfaceDmabufFeedback { @@ -1547,60 +1547,40 @@ fn get_surface_dmabuf_feedback( .cloned() .collect::() }); - let builder = DmabufFeedbackBuilder::new(render_node.dev_id(), render_formats); - /* - // Sadly no implementation would pick this up as a preferred render tranche, - // where the combined formats would increase our chances of doing a dmabuf copy. - // .. So we should probably not advertise this on the off-chance it actually triggers bugs. - // + let mut builder = DmabufFeedbackBuilder::new(render_node.dev_id(), render_formats.clone()); - let combined_formats = render_formats.intersection(&target_formats).cloned().collect::(); - if target_node != render_node.dev_id() && !combined_formats.is_empty() { - builder = builder.add_preference_tranche( - render_node.dev_id(), - None, - combined_formats, - ); - }; - - // We also can't advertise scan out tranches for the actual display device, - // as e.g. the nvidia driver might then send us dmabufs, that makes e.g. the iris hangs on import... - if target_node != render_node.dev_id() && !combined_formats.is_empty() { + if target_node != render_node { builder = builder.add_preference_tranche( target_node.dev_id(), - Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout), - combined_formats, + zwp_linux_dmabuf_feedback_v1::TrancheFlags::Sampling, + target_formats, + 6..=6, ); }; - - // So no fun combinations, we gotta wait for dmabuf-v6 - */ - let render_feedback = builder.clone().build().unwrap(); - let primary_scanout_feedback = (target_node == render_node).then(|| { + + let primary_scanout_feedback = builder + .clone() + .add_preference_tranche( + target_node.dev_id(), + zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout, + primary_plane_formats, + 4..=6, + ) + .build() + .unwrap(); + let overlay_scanout_feedback = overlay_plane_formats.map(|formats| { builder - .clone() .add_preference_tranche( - render_node.dev_id(), - Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout), - primary_plane_formats, + target_node.dev_id(), + zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout, + formats, + 4..=6, ) .build() .unwrap() }); - let overlay_scanout_feedback = overlay_plane_formats - .filter(|_| target_node == render_node) - .map(|formats| { - builder - .add_preference_tranche( - render_node.dev_id(), - Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout), - formats, - ) - .build() - .unwrap() - }); SurfaceDmabufFeedback { render_feedback, diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 0ff249a7..24ef64b4 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -6,6 +6,7 @@ use crate::{ shell::{Devices, SeatExt}, state::{BackendData, Common}, utils::prelude::*, + wayland::protocols::drm::WlDrmState, }; use anyhow::{Context, Result, anyhow}; use cosmic_comp_config::output::comp::{OutputConfig, TransformDef}; @@ -273,7 +274,7 @@ fn init_egl_client_side( .create_global_with_default_feedback::(dh, &feedback); let render_node = render_node.unwrap().unwrap(); - let _drm_global_id = state.common.wl_drm_state.create_global::( + state.common.wl_drm_state = Some(WlDrmState::new::( dh, render_node .dev_path_with_type(NodeType::Render) @@ -284,7 +285,7 @@ fn init_egl_client_side( ))?, dmabuf_formats, &dmabuf_global, - ); + )); info!("EGL hardware-acceleration enabled."); } diff --git a/src/backend/x11.rs b/src/backend/x11.rs index bd0e1b34..98b27453 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -6,6 +6,7 @@ use crate::{ shell::{Devices, SeatExt}, state::{BackendData, Common}, utils::prelude::*, + wayland::protocols::drm::WlDrmState, }; use anyhow::{Context, Result, anyhow}; use cosmic_comp_config::output::comp::OutputConfig; @@ -497,7 +498,7 @@ where .common .dmabuf_state .create_global_with_default_feedback::(dh, &default_feedback); - let _drm_global_id = state.common.wl_drm_state.create_global::( + state.common.wl_drm_state = Some(WlDrmState::new::( dh, render_node .dev_path_with_type(NodeType::Render) @@ -508,7 +509,7 @@ where ))?, renderer.dmabuf_formats(), &dmabuf_global, - ); + )); info!("EGL hardware-acceleration enabled."); diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index e146efeb..99bb2f78 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -838,10 +838,7 @@ impl CosmicSurface { self.0 .send_dmabuf_feedback(output, primary_scan_out_output, |_, data| { if is_fullscreen { - feedback - .primary_scanout_feedback - .as_ref() - .unwrap_or(&feedback.render_feedback) + &feedback.primary_scanout_feedback } else if frame_time_filter_fn(data) == Kind::ScanoutCandidate { feedback .overlay_scanout_feedback diff --git a/src/state.rs b/src/state.rs index 24ccd9e1..8cfee1a9 100644 --- a/src/state.rs +++ b/src/state.rs @@ -276,7 +276,7 @@ pub struct Common { pub idle_inhibiting_surfaces: HashSet, pub shm_state: ShmState, pub cursor_shape_manager_state: CursorShapeManagerState, - pub wl_drm_state: WlDrmState>, + pub wl_drm_state: Option>>, pub viewporter_state: ViewporterState, pub kde_decoration_state: KdeDecorationState, pub xdg_decoration_state: XdgDecorationState, @@ -326,7 +326,7 @@ pub enum LockedBackend<'a> { pub struct SurfaceDmabufFeedback { pub render_feedback: DmabufFeedback, pub overlay_scanout_feedback: Option, - pub primary_scanout_feedback: Option, + pub primary_scanout_feedback: DmabufFeedback, } #[derive(Debug)] @@ -671,7 +671,7 @@ impl State { let cursor_shape_manager_state = CursorShapeManagerState::new::(dh); let seat_state = SeatState::::new(); let viewporter_state = ViewporterState::new::(dh); - let wl_drm_state = WlDrmState::>::default(); + let wl_drm_state = None; let kde_decoration_state = KdeDecorationState::new::(dh, Mode::Client); let xdg_decoration_state = XdgDecorationState::new::(dh); let session_lock_manager_state = @@ -1066,10 +1066,7 @@ impl Common { surface, render_element_states, &feedback.render_feedback, - feedback - .primary_scanout_feedback - .as_ref() - .unwrap_or(&feedback.render_feedback), + &feedback.primary_scanout_feedback, ) }, ) diff --git a/src/utils/global.rs b/src/utils/global.rs new file mode 100644 index 00000000..e309f68a --- /dev/null +++ b/src/utils/global.rs @@ -0,0 +1,28 @@ +use std::time::Duration; + +use calloop::{ + LoopHandle, + timer::{TimeoutAction, Timer}, +}; +use smithay::reexports::wayland_server::DisplayHandle; +use wayland_backend::server::GlobalId; + +pub fn remove_global_with_timer( + dh: &DisplayHandle, + event_loop_handle: &LoopHandle, + id: GlobalId, +) { + dh.disable_global::(id.clone()); + let source = Timer::from_duration(Duration::from_secs(5)); + let dh = dh.clone(); + let res = event_loop_handle.insert_source(source, move |_, _, _state| { + dh.remove_global::(id.clone()); + TimeoutAction::Drop + }); + if let Err(err) = res { + tracing::error!( + "failed to insert timer source to destroy output global: {}", + err + ); + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ac1df019..401597cc 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,6 +4,7 @@ pub mod env; mod ids; pub(crate) use self::ids::id_gen; pub mod geometry; +pub mod global; pub mod iced; pub mod prelude; pub mod quirks; diff --git a/src/wayland/handlers/dmabuf.rs b/src/wayland/handlers/dmabuf.rs index 4d9732bb..572fe01b 100644 --- a/src/wayland/handlers/dmabuf.rs +++ b/src/wayland/handlers/dmabuf.rs @@ -40,7 +40,7 @@ impl DmabufHandler for State { fn new_surface_feedback( &mut self, surface: &WlSurface, - global: &DmabufGlobal, + _global: &DmabufGlobal, ) -> Option { let BackendData::Kms(kms) = &self.backend else { return None; @@ -57,18 +57,7 @@ impl DmabufHandler for State { f.ended_at.is_none() && f.surface.has_surface(surface, WindowSurfaceType::all()) }); - let node = kms - .drm_devices - .values() - .find(|device| { - device - .socket - .as_ref() - .map(|s| &s.dmabuf_global == global) - .unwrap_or(false) - })? - .inner - .render_node; + let node = kms.primary_node.read().unwrap().unwrap(); let kms_surface = kms .drm_devices .values() @@ -77,9 +66,7 @@ impl DmabufHandler for State { Some(with_states(surface, |data| { if is_fullscreen { - feedback - .primary_scanout_feedback - .unwrap_or(feedback.render_feedback) + feedback.primary_scanout_feedback } else if frame_time_filter_fn(data) == Kind::ScanoutCandidate { feedback .overlay_scanout_feedback diff --git a/src/wayland/protocols/drm.rs b/src/wayland/protocols/drm.rs index c86e5ff0..d2902c98 100644 --- a/src/wayland/protocols/drm.rs +++ b/src/wayland/protocols/drm.rs @@ -38,7 +38,11 @@ use smithay::{ }; use tracing::trace; -use std::{convert::TryFrom, path::PathBuf, sync::Arc}; +use std::{ + convert::TryFrom, + path::PathBuf, + sync::{Arc, Mutex}, +}; pub enum ImportError { Failed, @@ -52,19 +56,28 @@ pub trait DrmHandler { } } -#[derive(Debug, Default)] -pub struct WlDrmState(std::marker::PhantomData); +#[derive(Debug)] +pub struct WlDrmState { + device: Arc>, + global: GlobalId, + _marker: std::marker::PhantomData, +} + +#[derive(Debug)] +pub struct DrmGlobalDeviceData { + formats: Vec, + path: PathBuf, +} /// Data associated with a drm global. pub struct DrmGlobalData { filter: Box Fn(&'a Client) -> bool + Send + Sync>, - formats: Arc>, - device_path: PathBuf, dmabuf_global: DmabufGlobal, + device: Arc>, } pub struct DrmInstanceData { - formats: Arc>, + device: Arc>, dmabuf_global: DmabufGlobal, } @@ -86,16 +99,24 @@ where data_init: &mut DataInit<'_, D>, ) { let data = DrmInstanceData { - formats: global_data.formats.clone(), + device: global_data.device.clone(), dmabuf_global: global_data.dmabuf_global, }; let drm_instance = data_init.init(resource, data); - drm_instance.device(global_data.device_path.to_string_lossy().into_owned()); + drm_instance.device( + global_data + .device + .lock() + .unwrap() + .path + .to_string_lossy() + .into_owned(), + ); if drm_instance.version() >= 2 { drm_instance.capabilities(wl_drm::Capability::Prime as u32); } - for format in global_data.formats.iter() { + for format in global_data.device.lock().unwrap().formats.iter() { if let Ok(converted) = wl_drm::Format::try_from(*format as u32) { drm_instance.format(converted as u32); } @@ -148,7 +169,7 @@ where } => { let format = match Fourcc::try_from(format) { Ok(format) => { - if !data.formats.contains(&format) { + if !data.device.lock().unwrap().formats.contains(&format) { drm.post_error( wl_drm::Error::InvalidFormat, String::from("Format not advertised by wl_drm"), @@ -220,13 +241,12 @@ where } impl WlDrmState { - pub fn create_global( - &mut self, + pub fn new( display: &DisplayHandle, device_path: PathBuf, formats: FormatSet, dmabuf_global: &DmabufGlobal, - ) -> GlobalId + ) -> Self where D: GlobalDispatch + Dispatch @@ -234,19 +254,16 @@ impl WlDrmState { + DmabufHandler + 'static, { - self.create_global_with_filter::(display, device_path, formats, dmabuf_global, |_| { - true - }) + Self::new_with_filter::(display, device_path, formats, dmabuf_global, |_| true) } - pub fn create_global_with_filter( - &mut self, + pub fn new_with_filter( display: &DisplayHandle, device_path: PathBuf, formats: FormatSet, dmabuf_global: &DmabufGlobal, client_filter: F, - ) -> GlobalId + ) -> Self where D: GlobalDispatch + Dispatch @@ -255,21 +272,45 @@ impl WlDrmState { + 'static, F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, { - let formats = Arc::new( - formats + let device = Arc::new(Mutex::new(DrmGlobalDeviceData { + formats: formats .into_iter() .filter(|f| f.modifier == Modifier::Invalid) .map(|f| f.code) .collect(), - ); + path: device_path, + })); + let data = DrmGlobalData { filter: Box::new(client_filter), - formats, - device_path, + device: device.clone(), dmabuf_global: *dmabuf_global, }; - display.create_global::(2, data) + let global = display.create_global::(2, data); + + WlDrmState { + device, + global, + _marker: Default::default(), + } + } + + pub fn update_device(&mut self, device_path: PathBuf, formats: FormatSet) { + let new_device = DrmGlobalDeviceData { + formats: formats + .into_iter() + .filter(|f| f.modifier == Modifier::Invalid) + .map(|f| f.code) + .collect(), + path: device_path, + }; + + *self.device.lock().unwrap() = new_device; + } + + pub fn global(&self) -> &GlobalId { + &self.global } } diff --git a/src/wayland/protocols/output_configuration/mod.rs b/src/wayland/protocols/output_configuration/mod.rs index 4bd1c0bd..d413e634 100644 --- a/src/wayland/protocols/output_configuration/mod.rs +++ b/src/wayland/protocols/output_configuration/mod.rs @@ -1,9 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only -use calloop::{ - LoopHandle, - timer::{TimeoutAction, Timer}, -}; +use calloop::LoopHandle; use cosmic_comp_config::output::comp::AdaptiveSync; use cosmic_protocols::output_management::v1::server::{ zcosmic_output_configuration_head_v1::ZcosmicOutputConfigurationHeadV1, @@ -30,7 +27,7 @@ use smithay::{ utils::{Logical, Physical, Point, Size, Transform}, wayland::output::WlOutputData, }; -use std::{convert::TryFrom, sync::Mutex, time::Duration}; +use std::{convert::TryFrom, sync::Mutex}; mod handlers; @@ -519,26 +516,6 @@ where } } -fn remove_global_with_timer( - dh: &DisplayHandle, - event_loop_handle: &LoopHandle, - id: GlobalId, -) { - dh.disable_global::(id.clone()); - let source = Timer::from_duration(Duration::from_secs(5)); - let dh = dh.clone(); - let res = event_loop_handle.insert_source(source, move |_, _, _state| { - dh.remove_global::(id.clone()); - TimeoutAction::Drop - }); - if let Err(err) = res { - tracing::error!( - "failed to insert timer source to destroy output global: {}", - err - ); - } -} - macro_rules! delegate_output_configuration { ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ @@ -578,4 +555,4 @@ macro_rules! delegate_output_configuration { } pub(crate) use delegate_output_configuration; -use crate::utils::prelude::OutputExt; +use crate::utils::{global::remove_global_with_timer, prelude::OutputExt};