From 15e73830fa07ee5e737388bc955403c5cc7c6501 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 9 Apr 2025 11:58:15 -0700 Subject: [PATCH] Use gbm device from image copy protocol instead of default node This fixes `BufferConstraints` errors for workspace capture on Nvidia outputs of a Intel+Nvidia system. (That didn't happen on Amd+Nvidia; maybe a linear stride alignment issue?) --- src/backend/wayland/buffer.rs | 23 +++++++++++----- src/backend/wayland/dmabuf.rs | 28 ------------------- src/backend/wayland/gbm_devices.rs | 43 ++++++++++++++++++++++++++++++ src/backend/wayland/mod.rs | 8 +++--- 4 files changed, 64 insertions(+), 38 deletions(-) create mode 100644 src/backend/wayland/gbm_devices.rs diff --git a/src/backend/wayland/buffer.rs b/src/backend/wayland/buffer.rs index 8ee6d5d..d23a7a0 100644 --- a/src/backend/wayland/buffer.rs +++ b/src/backend/wayland/buffer.rs @@ -79,15 +79,18 @@ impl AppData { #[cfg(not(feature = "force-shm-screencopy"))] fn create_gbm_buffer( - &self, + &mut self, format: u32, modifiers: &[u64], (width, height): (u32, u32), needs_linear: bool, + drm_dev: Option, ) -> anyhow::Result> { - let (Some((node, gbm)), Some(feedback)) = - (self.gbm.as_ref(), self.dmabuf_feedback.as_ref()) - else { + let Some(feedback) = self.dmabuf_feedback.as_ref() else { + return Ok(None); + }; + let drm_dev = drm_dev.unwrap_or(feedback.main_device() as u64); + let Some((node, gbm)) = self.gbm_devices.gbm_device(drm_dev)? else { return Ok(None); }; let formats = feedback.format_table(); @@ -171,12 +174,12 @@ impl AppData { .into(), ), buffer, - node: Some(node.clone()), + node: Some(node.to_owned()), size: (width, height), })) } - pub fn create_buffer(&self, formats: &Formats) -> Buffer { + pub fn create_buffer(&mut self, formats: &Formats) -> Buffer { // XXX Handle other formats? let format = wl_shm::Format::Abgr8888; @@ -186,7 +189,13 @@ impl AppData { .iter() .find(|(f, _)| *f == u32::from(format)) { - match self.create_gbm_buffer(u32::from(format), modifiers, formats.buffer_size, false) { + match self.create_gbm_buffer( + u32::from(format), + modifiers, + formats.buffer_size, + false, + formats.dmabuf_device.map(|dev| dev as u64), + ) { Ok(Some(buffer)) => { return buffer; } diff --git a/src/backend/wayland/dmabuf.rs b/src/backend/wayland/dmabuf.rs index 3b4a872..27f265b 100644 --- a/src/backend/wayland/dmabuf.rs +++ b/src/backend/wayland/dmabuf.rs @@ -7,8 +7,6 @@ use cctk::{ }; use cosmic::cctk; -use std::{fs, io, os::unix::fs::MetadataExt, path::PathBuf}; - use wayland_protocols::wp::linux_dmabuf::zv1::client::{ zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, @@ -27,20 +25,6 @@ impl DmabufHandler for AppData { _proxy: &ZwpLinuxDmabufFeedbackV1, feedback: DmabufFeedback, ) { - if self.gbm.is_none() { - #[allow(clippy::unnecessary_cast)] - match find_gbm_device(feedback.main_device() as u64) { - Ok(Some(gbm)) => { - self.gbm = Some(gbm); - } - Ok(None) => { - log::error!("Gbm main device '{}' not found", feedback.main_device()); - } - Err(err) => { - log::error!("Failed to open gbm main device: {}", err); - } - } - } self.dmabuf_feedback = Some(feedback); } fn created( @@ -67,16 +51,4 @@ impl DmabufHandler for AppData { } } -fn find_gbm_device(dev: u64) -> io::Result)>> { - for i in std::fs::read_dir("/dev/dri")? { - let i = i?; - if i.metadata()?.rdev() == dev { - let file = fs::File::options().read(true).write(true).open(i.path())?; - log::info!("Opened gbm main device '{}'", i.path().display()); - return Ok(Some((i.path(), gbm::Device::new(file)?))); - } - } - Ok(None) -} - sctk::delegate_dmabuf!(AppData); diff --git a/src/backend/wayland/gbm_devices.rs b/src/backend/wayland/gbm_devices.rs new file mode 100644 index 0000000..72de291 --- /dev/null +++ b/src/backend/wayland/gbm_devices.rs @@ -0,0 +1,43 @@ +use std::{ + collections::hash_map::{self, HashMap}, + fs, io, + os::unix::fs::MetadataExt, + path::{Path, PathBuf}, +}; + +// TODO Purge gbm devices that are no longer needed/valid? +#[derive(Default)] +pub struct GbmDevices { + devices: HashMap)>, +} + +impl GbmDevices { + pub fn gbm_device(&mut self, dev: u64) -> io::Result)>> { + Ok(match self.devices.entry(dev) { + hash_map::Entry::Occupied(entry) => { + let (path, gbm) = entry.into_mut(); + Some((path, gbm)) + } + hash_map::Entry::Vacant(entry) => { + if let Some(value) = find_gbm_device(dev)? { + let (path, gbm) = entry.insert(value); + Some((path, gbm)) + } else { + None + } + } + }) + } +} + +fn find_gbm_device(dev: u64) -> io::Result)>> { + for i in std::fs::read_dir("/dev/dri")? { + let i = i?; + if i.metadata()?.rdev() == dev { + let file = fs::File::options().read(true).write(true).open(i.path())?; + log::info!("Opened gbm main device '{}'", i.path().display()); + return Ok(Some((i.path(), gbm::Device::new(file)?))); + } + } + Ok(None) +} diff --git a/src/backend/wayland/mod.rs b/src/backend/wayland/mod.rs index 32debec..2594bea 100644 --- a/src/backend/wayland/mod.rs +++ b/src/backend/wayland/mod.rs @@ -27,13 +27,15 @@ use cosmic::{ }, }; use futures_channel::mpsc; -use std::{cell::RefCell, collections::HashMap, fs, path::PathBuf, sync::Arc, thread}; +use std::{cell::RefCell, collections::HashMap, sync::Arc, thread}; mod buffer; use buffer::Buffer; mod capture; use capture::Capture; mod dmabuf; +mod gbm_devices; +use gbm_devices::GbmDevices; mod screencopy; use screencopy::{ScreencopySession, SessionData}; mod toplevel; @@ -59,7 +61,7 @@ pub struct AppData { capture_filter: CaptureFilter, captures: RefCell>>, dmabuf_feedback: Option, - gbm: Option<(PathBuf, gbm::Device)>, + gbm_devices: GbmDevices, thread_pool: futures_executor::ThreadPool, } @@ -310,7 +312,7 @@ fn start(conn: Connection) -> mpsc::Receiver { capture_filter: CaptureFilter::default(), captures: RefCell::new(HashMap::new()), dmabuf_feedback: None, - gbm: None, + gbm_devices: GbmDevices::default(), thread_pool, };