kms: Track active clients instead of active buffers

This commit is contained in:
Victoria Brekenfeld 2025-12-19 18:49:22 +01:00 committed by Victoria Brekenfeld
parent a15e378f1e
commit ca00df0b37
6 changed files with 72 additions and 131 deletions

View file

@ -38,12 +38,13 @@ use smithay::{
drm::control::{Device as ControlDevice, ModeTypeFlags, connector, crtc}, drm::control::{Device as ControlDevice, ModeTypeFlags, connector, crtc},
gbm::BufferObjectFlags as GbmBufferFlags, gbm::BufferObjectFlags as GbmBufferFlags,
rustix::fs::OFlags, rustix::fs::OFlags,
wayland_server::{DisplayHandle, Weak, protocol::wl_buffer::WlBuffer}, wayland_server::DisplayHandle,
}, },
utils::{Clock, DevPath, DeviceFd, Monotonic, Point, Transform}, utils::{Clock, DevPath, DeviceFd, Monotonic, Point, Transform},
wayland::drm_lease::{DrmLease, DrmLeaseState}, wayland::drm_lease::{DrmLease, DrmLeaseState},
}; };
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use wayland_backend::server::ClientId;
use std::{ use std::{
borrow::BorrowMut, borrow::BorrowMut,
@ -117,7 +118,7 @@ pub struct InnerDevice {
pub leased_connectors: Vec<(connector::Handle, crtc::Handle)>, pub leased_connectors: Vec<(connector::Handle, crtc::Handle)>,
pub leasing_global: Option<DrmLeaseState>, pub leasing_global: Option<DrmLeaseState>,
pub active_leases: Vec<DrmLease>, pub active_leases: Vec<DrmLease>,
pub active_buffers: HashSet<Weak<WlBuffer>>, pub active_clients: HashSet<ClientId>,
} }
impl fmt::Debug for InnerDevice { impl fmt::Debug for InnerDevice {
@ -133,7 +134,7 @@ impl fmt::Debug for InnerDevice {
.field("leased_connectors", &self.leased_connectors) .field("leased_connectors", &self.leased_connectors)
.field("leasing_global", &self.leasing_global) .field("leasing_global", &self.leasing_global)
.field("active_leases", &self.active_leases) .field("active_leases", &self.active_leases)
.field("active_buffers", &self.active_buffers.len()) .field("active_clients", &self.active_clients.len())
.finish() .finish()
} }
} }
@ -338,7 +339,7 @@ impl State {
leased_connectors: Vec::new(), leased_connectors: Vec::new(),
leasing_global, leasing_global,
active_leases: Vec::new(), active_leases: Vec::new(),
active_buffers: HashSet::new(), active_clients: HashSet::new(),
}, },
supports_atomic, supports_atomic,
@ -738,7 +739,7 @@ impl InnerDevice {
pub fn in_use(&self, primary: Option<&DrmNode>) -> bool { pub fn in_use(&self, primary: Option<&DrmNode>) -> bool {
Some(&self.render_node) == primary Some(&self.render_node) == primary
|| !self.surfaces.is_empty() || !self.surfaces.is_empty()
|| !self.active_buffers.is_empty() || !self.active_clients.is_empty()
} }
pub fn connector_added( pub fn connector_added(

View file

@ -14,7 +14,7 @@ use indexmap::IndexMap;
use render::gles::GbmGlowBackend; use render::gles::GbmGlowBackend;
use smithay::{ use smithay::{
backend::{ backend::{
allocator::{Buffer, dmabuf::Dmabuf, format::FormatSet}, allocator::{dmabuf::Dmabuf, format::FormatSet},
drm::{DrmDeviceFd, DrmNode, NodeType, VrrSupport, output::DrmOutputRenderElements}, drm::{DrmDeviceFd, DrmNode, NodeType, VrrSupport, output::DrmOutputRenderElements},
egl::{EGLContext, EGLDevice, EGLDisplay}, egl::{EGLContext, EGLDevice, EGLDisplay},
input::InputEvent, input::InputEvent,
@ -41,7 +41,7 @@ use smithay::{
}, },
}; };
use surface::GbmDrmOutput; use surface::GbmDrmOutput;
use tracing::{debug, error, info, trace, warn}; use tracing::{debug, error, info, warn};
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@ -487,78 +487,55 @@ impl KmsState {
pub fn dmabuf_imported( pub fn dmabuf_imported(
&mut self, &mut self,
_client: Option<Client>, client: Option<Client>,
global: &DmabufGlobal, global: &DmabufGlobal,
dmabuf: Dmabuf, dmabuf: Dmabuf,
) -> Result<DrmNode> { ) -> Result<DrmNode> {
let (expected_node, mut other_nodes) = self let device = self
.drm_devices .drm_devices
.values_mut() .values_mut()
.partition::<Vec<_>, _>(|device| { .find(|device| {
device device
.socket .socket
.as_ref() .as_ref()
.map(|s| &s.dmabuf_global == global) .map(|s| &s.dmabuf_global == global)
.unwrap_or(false) .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"); let new_client = if let Some(client) = client {
for device in expected_node.into_iter().chain(other_nodes.into_iter()) { let new = device.inner.active_clients.insert(client.id());
let mut _egl = None; device.inner.update_egl(
let egl_display = if let Some(egl_display) = device self.primary_node.read().unwrap().as_ref(),
.inner self.api.as_mut(),
.egl )? && new
.as_ref() } else {
.map(|internals| &internals.display) false
{ };
egl_display
} else {
_egl =
Some(init_egl(&device.inner.gbm).context("Failed to initialize egl context")?);
&_egl.as_ref().unwrap().display
};
if !egl_display let egl = device
.dmabuf_texture_formats() .inner
.contains(&dmabuf.format()) .egl
{ .as_ref()
trace!( .context("EGL initialization Error")?;
"Skipping import of dmabuf on {:?}: unsupported format", egl.display
device.inner.render_node .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 let node = device.inner.render_node;
.create_image_from_dmabuf(&dmabuf) dmabuf.set_node(node);
.map(|image| {
unsafe {
smithay::backend::egl::ffi::egl::DestroyImageKHR(
**egl_display.get_display_handle(),
image,
);
};
device.inner.render_node
})
.map_err(Into::into);
match result { if new_client {
Ok(node) => { self.refresh_used_devices()?;
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;
}
}
} }
Err(last_err) Ok(node)
} }
pub fn schedule_render(&mut self, output: &Output) { pub fn schedule_render(&mut self, output: &Output) {

View file

@ -114,6 +114,7 @@ use smithay::{
xwayland::XWaylandClientData, xwayland::XWaylandClientData,
}; };
use time::UtcOffset; use time::UtcOffset;
use tracing::warn;
#[cfg(feature = "systemd")] #[cfg(feature = "systemd")]
use std::os::fd::OwnedFd; use std::os::fd::OwnedFd;
@ -149,9 +150,12 @@ macro_rules! fl {
pub struct ClientState { pub struct ClientState {
pub compositor_client_state: CompositorClientState, pub compositor_client_state: CompositorClientState,
pub advertised_drm_node: Option<DrmNode>, pub advertised_drm_node: Option<DrmNode>,
pub evlh: LoopHandle<'static, State>,
pub evls: LoopSignal, pub evls: LoopSignal,
pub security_context: Option<SecurityContext>, pub security_context: Option<SecurityContext>,
} }
unsafe impl Send for ClientState {}
unsafe impl Sync for ClientState {}
impl ClientState { impl ClientState {
/// We treat a client as "sandboxed" if it has a security context for any sandbox engine /// 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 { impl ClientData for ClientState {
fn initialized(&self, _client_id: ClientId) {} 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(); self.evls.wakeup();
} }
} }
@ -783,6 +803,7 @@ impl State {
BackendData::Kms(kms_state) => *kms_state.primary_node.read().unwrap(), BackendData::Kms(kms_state) => *kms_state.primary_node.read().unwrap(),
_ => None, _ => None,
}, },
evlh: self.common.event_loop_handle.clone(),
evls: self.common.event_loop_signal.clone(), evls: self.common.event_loop_signal.clone(),
security_context: None, security_context: None,
} }

View file

@ -1,27 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::state::{BackendData, State}; use crate::state::State;
use smithay::{ use smithay::{
reexports::wayland_server::{Resource, protocol::wl_buffer::WlBuffer}, reexports::wayland_server::protocol::wl_buffer::WlBuffer, wayland::buffer::BufferHandler,
wayland::buffer::BufferHandler,
}; };
use tracing::warn;
impl BufferHandler for State { impl BufferHandler for State {
fn buffer_destroyed(&mut self, buffer: &WlBuffer) { 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;
}
}
}
}
} }

View file

@ -1,13 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::state::{BackendData, State}; use crate::state::State;
use smithay::{ use smithay::{
backend::allocator::dmabuf::Dmabuf, backend::allocator::dmabuf::Dmabuf,
delegate_dmabuf, delegate_dmabuf,
reexports::wayland_server::Resource,
wayland::dmabuf::{DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier}, wayland::dmabuf::{DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier},
}; };
use tracing::warn;
impl DmabufHandler for State { impl DmabufHandler for State {
fn dmabuf_state(&mut self) -> &mut DmabufState { fn dmabuf_state(&mut self) -> &mut DmabufState {
@ -20,34 +18,13 @@ impl DmabufHandler for State {
dmabuf: Dmabuf, dmabuf: Dmabuf,
import_notifier: ImportNotifier, import_notifier: ImportNotifier,
) { ) {
match self let client = import_notifier.client();
.backend match self.backend.dmabuf_imported(client.clone(), global, dmabuf) {
.dmabuf_imported(import_notifier.client(), global, dmabuf)
{
Err(err) => { Err(err) => {
tracing::debug!(?err, "dmabuf import failed"); tracing::debug!(?err, "dmabuf import failed");
import_notifier.failed() import_notifier.failed()
} }
Ok(Some(node)) => { Ok(_) => {
// kms backend
let Ok(buffer) = import_notifier.successful::<State>() 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) => {
let _ = import_notifier.successful::<State>(); let _ = import_notifier.successful::<State>();
} }
} }

View file

@ -1,15 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::{ use crate::{
state::{BackendData, State}, state::State,
wayland::protocols::drm::{DrmHandler, ImportError, delegate_wl_drm}, wayland::protocols::drm::{DrmHandler, ImportError, delegate_wl_drm},
}; };
use smithay::{ use smithay::{
backend::{allocator::dmabuf::Dmabuf, drm::DrmNode}, backend::{allocator::dmabuf::Dmabuf, drm::DrmNode},
reexports::wayland_server::{Resource, protocol::wl_buffer::WlBuffer}, reexports::wayland_server::protocol::wl_buffer::WlBuffer,
wayland::dmabuf::DmabufGlobal, wayland::dmabuf::DmabufGlobal,
}; };
use tracing::warn;
impl DrmHandler<Option<DrmNode>> for State { impl DrmHandler<Option<DrmNode>> for State {
fn dmabuf_imported( fn dmabuf_imported(
@ -22,24 +21,7 @@ impl DrmHandler<Option<DrmNode>> for State {
.map_err(|_| ImportError::Failed) .map_err(|_| ImportError::Failed)
} }
fn buffer_created(&mut self, buffer: WlBuffer, result: Option<DrmNode>) { fn buffer_created(&mut self, _buffer: WlBuffer, _result: Option<DrmNode>) {}
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.");
};
}
}
}
} }
delegate_wl_drm!(State; Option<DrmNode>); delegate_wl_drm!(State; Option<DrmNode>);