From ca00df0b374fd69b49eeb584dc255d06c1fe2afa Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 19 Dec 2025 18:49:22 +0100 Subject: [PATCH] kms: Track active clients instead of active buffers --- src/backend/kms/device.rs | 11 ++-- src/backend/kms/mod.rs | 91 +++++++++++++--------------------- src/state.rs | 23 ++++++++- src/wayland/handlers/buffer.rs | 23 ++------- src/wayland/handlers/dmabuf.rs | 31 ++---------- src/wayland/handlers/drm.rs | 24 ++------- 6 files changed, 72 insertions(+), 131 deletions(-) diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index ad07a9c7..0cf9e083 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -38,12 +38,13 @@ use smithay::{ drm::control::{Device as ControlDevice, ModeTypeFlags, connector, crtc}, gbm::BufferObjectFlags as GbmBufferFlags, rustix::fs::OFlags, - wayland_server::{DisplayHandle, Weak, protocol::wl_buffer::WlBuffer}, + wayland_server::DisplayHandle, }, utils::{Clock, DevPath, DeviceFd, Monotonic, Point, Transform}, wayland::drm_lease::{DrmLease, DrmLeaseState}, }; use tracing::{error, info, warn}; +use wayland_backend::server::ClientId; use std::{ borrow::BorrowMut, @@ -117,7 +118,7 @@ pub struct InnerDevice { pub leased_connectors: Vec<(connector::Handle, crtc::Handle)>, pub leasing_global: Option, pub active_leases: Vec, - pub active_buffers: HashSet>, + pub active_clients: HashSet, } impl fmt::Debug for InnerDevice { @@ -133,7 +134,7 @@ impl fmt::Debug for InnerDevice { .field("leased_connectors", &self.leased_connectors) .field("leasing_global", &self.leasing_global) .field("active_leases", &self.active_leases) - .field("active_buffers", &self.active_buffers.len()) + .field("active_clients", &self.active_clients.len()) .finish() } } @@ -338,7 +339,7 @@ impl State { leased_connectors: Vec::new(), leasing_global, active_leases: Vec::new(), - active_buffers: HashSet::new(), + active_clients: HashSet::new(), }, supports_atomic, @@ -738,7 +739,7 @@ impl InnerDevice { pub fn in_use(&self, primary: Option<&DrmNode>) -> bool { Some(&self.render_node) == primary || !self.surfaces.is_empty() - || !self.active_buffers.is_empty() + || !self.active_clients.is_empty() } pub fn connector_added( diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 70b3eabf..3a5933fa 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -14,7 +14,7 @@ use indexmap::IndexMap; use render::gles::GbmGlowBackend; use smithay::{ backend::{ - allocator::{Buffer, dmabuf::Dmabuf, format::FormatSet}, + allocator::{dmabuf::Dmabuf, format::FormatSet}, drm::{DrmDeviceFd, DrmNode, NodeType, VrrSupport, output::DrmOutputRenderElements}, egl::{EGLContext, EGLDevice, EGLDisplay}, input::InputEvent, @@ -41,7 +41,7 @@ use smithay::{ }, }; use surface::GbmDrmOutput; -use tracing::{debug, error, info, trace, warn}; +use tracing::{debug, error, info, warn}; use std::{ collections::{HashMap, HashSet}, @@ -487,78 +487,55 @@ impl KmsState { pub fn dmabuf_imported( &mut self, - _client: Option, + client: Option, global: &DmabufGlobal, dmabuf: Dmabuf, ) -> Result { - let (expected_node, mut other_nodes) = self + let device = self .drm_devices .values_mut() - .partition::, _>(|device| { + .find(|device| { device .socket .as_ref() .map(|s| &s.dmabuf_global == global) .unwrap_or(false) - }); - other_nodes.retain(|device| device.socket.is_some()); + }) + .context("Couldn't find gpu for dmabuf global")?; - let mut last_err = anyhow::anyhow!("Dmabuf cannot be imported on any gpu"); - for device in expected_node.into_iter().chain(other_nodes.into_iter()) { - let mut _egl = None; - let egl_display = if let Some(egl_display) = device - .inner - .egl - .as_ref() - .map(|internals| &internals.display) - { - egl_display - } else { - _egl = - Some(init_egl(&device.inner.gbm).context("Failed to initialize egl context")?); - &_egl.as_ref().unwrap().display - }; + let new_client = if let Some(client) = client { + let new = device.inner.active_clients.insert(client.id()); + device.inner.update_egl( + self.primary_node.read().unwrap().as_ref(), + self.api.as_mut(), + )? && new + } else { + false + }; - if !egl_display - .dmabuf_texture_formats() - .contains(&dmabuf.format()) - { - trace!( - "Skipping import of dmabuf on {:?}: unsupported format", - device.inner.render_node + let egl = device + .inner + .egl + .as_ref() + .context("EGL initialization Error")?; + egl.display + .create_image_from_dmabuf(&dmabuf) + .inspect(|image| unsafe { + smithay::backend::egl::ffi::egl::DestroyImageKHR( + **egl.display.get_display_handle(), + *image, ); - continue; - } + }) + .context("Failed to create EGLImage from dmabuf")?; - let result = egl_display - .create_image_from_dmabuf(&dmabuf) - .map(|image| { - unsafe { - smithay::backend::egl::ffi::egl::DestroyImageKHR( - **egl_display.get_display_handle(), - image, - ); - }; - device.inner.render_node - }) - .map_err(Into::into); + let node = device.inner.render_node; + dmabuf.set_node(node); - match result { - Ok(node) => { - dmabuf.set_node(node); // so the MultiRenderer knows what node to use - return Ok(node); - } - Err(err) => { - trace!( - ?err, - "Failed to import dmabuf on {:?}", device.inner.render_node - ); - last_err = err; - } - } + if new_client { + self.refresh_used_devices()?; } - Err(last_err) + Ok(node) } pub fn schedule_render(&mut self, output: &Output) { diff --git a/src/state.rs b/src/state.rs index 0ab568f0..7da07209 100644 --- a/src/state.rs +++ b/src/state.rs @@ -114,6 +114,7 @@ use smithay::{ xwayland::XWaylandClientData, }; use time::UtcOffset; +use tracing::warn; #[cfg(feature = "systemd")] use std::os::fd::OwnedFd; @@ -149,9 +150,12 @@ macro_rules! fl { pub struct ClientState { pub compositor_client_state: CompositorClientState, pub advertised_drm_node: Option, + pub evlh: LoopHandle<'static, State>, pub evls: LoopSignal, pub security_context: Option, } +unsafe impl Send for ClientState {} +unsafe impl Sync for ClientState {} impl ClientState { /// We treat a client as "sandboxed" if it has a security context for any sandbox engine @@ -167,7 +171,23 @@ impl ClientState { impl ClientData for ClientState { fn initialized(&self, _client_id: ClientId) {} - fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) { + fn disconnected(&self, client_id: ClientId, _reason: DisconnectReason) { + self.evlh.insert_idle(move |state| { + if let BackendData::Kms(kms_state) = &mut state.backend { + 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()) + { + if let Err(err) = kms_state.refresh_used_devices() { + warn!(?err, "Failed to init devices."); + }; + break; + } + } + } + }); self.evls.wakeup(); } } @@ -783,6 +803,7 @@ impl State { BackendData::Kms(kms_state) => *kms_state.primary_node.read().unwrap(), _ => None, }, + evlh: self.common.event_loop_handle.clone(), evls: self.common.event_loop_signal.clone(), security_context: None, } diff --git a/src/wayland/handlers/buffer.rs b/src/wayland/handlers/buffer.rs index 56e7af46..184eee4d 100644 --- a/src/wayland/handlers/buffer.rs +++ b/src/wayland/handlers/buffer.rs @@ -1,27 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::state::{BackendData, State}; +use crate::state::State; use smithay::{ - reexports::wayland_server::{Resource, protocol::wl_buffer::WlBuffer}, - wayland::buffer::BufferHandler, + reexports::wayland_server::protocol::wl_buffer::WlBuffer, wayland::buffer::BufferHandler, }; -use tracing::warn; impl BufferHandler for State { - fn buffer_destroyed(&mut self, buffer: &WlBuffer) { - if let BackendData::Kms(kms_state) = &mut self.backend { - for device in kms_state.drm_devices.values_mut() { - if device.inner.active_buffers.remove(&buffer.downgrade()) - && !device - .inner - .in_use(kms_state.primary_node.read().unwrap().as_ref()) - { - if let Err(err) = kms_state.refresh_used_devices() { - warn!(?err, "Failed to init devices."); - }; - break; - } - } - } - } + fn buffer_destroyed(&mut self, _buffer: &WlBuffer) {} } diff --git a/src/wayland/handlers/dmabuf.rs b/src/wayland/handlers/dmabuf.rs index a1195d06..baae1df4 100644 --- a/src/wayland/handlers/dmabuf.rs +++ b/src/wayland/handlers/dmabuf.rs @@ -1,13 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::state::{BackendData, State}; +use crate::state::State; use smithay::{ backend::allocator::dmabuf::Dmabuf, delegate_dmabuf, - reexports::wayland_server::Resource, wayland::dmabuf::{DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier}, }; -use tracing::warn; impl DmabufHandler for State { fn dmabuf_state(&mut self) -> &mut DmabufState { @@ -20,34 +18,13 @@ impl DmabufHandler for State { dmabuf: Dmabuf, import_notifier: ImportNotifier, ) { - match self - .backend - .dmabuf_imported(import_notifier.client(), global, dmabuf) - { + let client = import_notifier.client(); + match self.backend.dmabuf_imported(client.clone(), global, dmabuf) { Err(err) => { tracing::debug!(?err, "dmabuf import failed"); import_notifier.failed() } - Ok(Some(node)) => { - // kms backend - let Ok(buffer) = import_notifier.successful::() else { - return; - }; - - if let BackendData::Kms(kms_state) = &mut self.backend { - if let Some(device) = kms_state - .drm_devices - .values_mut() - .find(|dev| dev.inner.render_node == node) - { - device.inner.active_buffers.insert(buffer.downgrade()); - } - if let Err(err) = kms_state.refresh_used_devices() { - warn!(?err, "Failed to init devices."); - }; - } - } - Ok(None) => { + Ok(_) => { let _ = import_notifier.successful::(); } } diff --git a/src/wayland/handlers/drm.rs b/src/wayland/handlers/drm.rs index b3419085..e035bbbd 100644 --- a/src/wayland/handlers/drm.rs +++ b/src/wayland/handlers/drm.rs @@ -1,15 +1,14 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - state::{BackendData, State}, + state::State, wayland::protocols::drm::{DrmHandler, ImportError, delegate_wl_drm}, }; use smithay::{ backend::{allocator::dmabuf::Dmabuf, drm::DrmNode}, - reexports::wayland_server::{Resource, protocol::wl_buffer::WlBuffer}, + reexports::wayland_server::protocol::wl_buffer::WlBuffer, wayland::dmabuf::DmabufGlobal, }; -use tracing::warn; impl DrmHandler> for State { fn dmabuf_imported( @@ -22,24 +21,7 @@ impl DrmHandler> for State { .map_err(|_| ImportError::Failed) } - fn buffer_created(&mut self, buffer: WlBuffer, result: Option) { - if let Some(node) = result { - // kms backend - if let BackendData::Kms(kms_state) = &mut self.backend { - if let Some(device) = kms_state - .drm_devices - .values_mut() - .find(|device| device.inner.render_node == node) - { - device.inner.active_buffers.insert(buffer.downgrade()); - } - - if let Err(err) = kms_state.refresh_used_devices() { - warn!(?err, "Failed to init devices."); - }; - } - } - } + fn buffer_created(&mut self, _buffer: WlBuffer, _result: Option) {} } delegate_wl_drm!(State; Option);