kms: Track used devices and free resources

This commit is contained in:
Victoria Brekenfeld 2024-01-17 11:34:19 +00:00 committed by Victoria Brekenfeld
parent e74b0dfaaa
commit ef3486f7e0
14 changed files with 303 additions and 130 deletions

View file

@ -39,6 +39,7 @@ use smithay::{
glow::GlowRenderer, glow::GlowRenderer,
multigpu::{gbm::GbmGlesBackend, Error as MultiError, GpuManager}, multigpu::{gbm::GbmGlesBackend, Error as MultiError, GpuManager},
sync::SyncPoint, sync::SyncPoint,
utils::with_renderer_surface_state,
Bind, ImportDma, Offscreen, Bind, ImportDma, Offscreen,
}, },
session::{libseat::LibSeatSession, Event as SessionEvent, Session}, session::{libseat::LibSeatSession, Event as SessionEvent, Session},
@ -62,7 +63,10 @@ use smithay::{
linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1, linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1,
presentation_time::server::wp_presentation_feedback, presentation_time::server::wp_presentation_feedback,
}, },
wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource}, wayland_server::{
protocol::{wl_buffer::WlBuffer, wl_surface::WlSurface},
Client, DisplayHandle, Resource, Weak,
},
}, },
utils::{DeviceFd, Size, Transform}, utils::{DeviceFd, Size, Transform},
wayland::{ wayland::{
@ -97,13 +101,14 @@ pub struct KmsState {
pub devices: HashMap<DrmNode, Device>, pub devices: HashMap<DrmNode, Device>,
pub input_devices: HashMap<String, input::Device>, pub input_devices: HashMap<String, input::Device>,
pub api: GpuManager<GbmGlesBackend<GlowRenderer>>, pub api: GpuManager<GbmGlesBackend<GlowRenderer>>,
pub primary: DrmNode, pub primary_node: DrmNode,
session: LibSeatSession, session: LibSeatSession,
pub auto_assign: bool,
_tokens: Vec<RegistrationToken>, _tokens: Vec<RegistrationToken>,
} }
pub struct Device { pub struct Device {
render_node: DrmNode, pub render_node: DrmNode,
surfaces: HashMap<crtc::Handle, Surface>, surfaces: HashMap<crtc::Handle, Surface>,
pub drm: DrmDevice, pub drm: DrmDevice,
gbm: GbmDevice<DrmDeviceFd>, gbm: GbmDevice<DrmDeviceFd>,
@ -113,6 +118,7 @@ pub struct Device {
pub non_desktop_connectors: Vec<(connector::Handle, crtc::Handle)>, pub non_desktop_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>>,
event_token: Option<RegistrationToken>, event_token: Option<RegistrationToken>,
socket: Option<Socket>, socket: Option<Socket>,
} }
@ -367,6 +373,11 @@ pub fn init_backend(
.map_err(|err| err.error) .map_err(|err| err.error)
.context("Failed to initialize session event source")?; .context("Failed to initialize session event source")?;
let auto_assign = matches!(
std::env::var("COSMIC_RENDER_AUTO_ASSIGN").map(|val| val.to_lowercase()),
Ok(val) if val == "y" || val == "yes" || val == "true"
);
state.backend = BackendData::Kms(KmsState { state.backend = BackendData::Kms(KmsState {
api, api,
_tokens: vec![ _tokens: vec![
@ -374,8 +385,9 @@ pub fn init_backend(
session_event_source, session_event_source,
udev_event_source, udev_event_source,
], ],
primary, primary_node: primary,
session, session,
auto_assign,
devices: HashMap::new(), devices: HashMap::new(),
input_devices: HashMap::new(), input_devices: HashMap::new(),
}); });
@ -598,6 +610,7 @@ impl State {
}) })
.ok(), .ok(),
active_leases: Vec::new(), active_leases: Vec::new(),
active_buffers: HashSet::new(),
event_token: Some(token), event_token: Some(token),
socket, socket,
}; };
@ -621,9 +634,13 @@ impl State {
let mut renderer = match backend.api.single_renderer(&render_node) { let mut renderer = match backend.api.single_renderer(&render_node) {
Ok(renderer) => renderer, Ok(renderer) => renderer,
Err(err) => { Err(err) => {
warn!(?err, "Failed to initialize output.");
backend.api.as_mut().remove_node(&render_node); backend.api.as_mut().remove_node(&render_node);
return Ok(()); return Err(err).with_context(|| {
format!(
"Failed to initialize renderer for device: {}, skipping",
render_node
)
});
} }
}; };
init_shaders(&mut renderer).expect("Failed to initialize renderer"); init_shaders(&mut renderer).expect("Failed to initialize renderer");
@ -686,6 +703,11 @@ impl State {
}; };
} }
} }
if !device.in_use(&backend.primary_node) {
backend.api.as_mut().remove_node(&render_node);
}
backend.devices.insert(drm_node, device); backend.devices.insert(drm_node, device);
} }
@ -735,6 +757,31 @@ impl State {
outputs_removed.push(surface.output.clone()); outputs_removed.push(surface.output.clone());
} }
} }
backend
.api
.as_mut()
.add_node(device.render_node, device.gbm.clone())
.with_context(|| {
format!(
"Failed to initialize renderer for device: {}, skipping",
device.render_node
)
})?;
let mut renderer = match backend.api.single_renderer(&device.render_node) {
Ok(renderer) => renderer,
Err(err) => {
backend.api.as_mut().remove_node(&device.render_node);
return Err(err).with_context(|| {
format!(
"Failed to initialize renderer for device: {}, skipping",
device.render_node
)
});
}
};
init_shaders(&mut renderer).expect("Failed to initialize renderer");
for (crtc, conn) in changes.added { for (crtc, conn) in changes.added {
let non_desktop = let non_desktop =
match drm_helpers::get_property_val(&device.drm, conn, "non-desktop") { match drm_helpers::get_property_val(&device.drm, conn, "non-desktop") {
@ -778,13 +825,6 @@ impl State {
); );
} }
} else { } else {
let mut renderer = match backend.api.single_renderer(&device.render_node) {
Ok(renderer) => renderer,
Err(err) => {
warn!(?err, "Failed to initialize output.");
continue;
}
};
match device.setup_surface(crtc, conn, (w, 0), &mut renderer) { match device.setup_surface(crtc, conn, (w, 0), &mut renderer) {
Ok(output) => { Ok(output) => {
w += output w += output
@ -827,6 +867,15 @@ impl State {
.remove_output(&output, seats.iter().cloned()); .remove_output(&output, seats.iter().cloned());
} }
{
let backend = self.backend.kms();
if let Some(device) = backend.devices.get_mut(&drm_node) {
if !device.in_use(&backend.primary_node) {
backend.api.as_mut().remove_node(&device.render_node);
}
}
}
Ok(()) Ok(())
} }
@ -1012,49 +1061,58 @@ impl Device {
Ok(output) Ok(output)
} }
pub fn in_use(&self, primary: &DrmNode) -> bool {
&self.render_node == primary || !self.surfaces.is_empty() || !self.active_buffers.is_empty()
}
} }
pub fn source_node_for_surface(w: &WlSurface, dh: &DisplayHandle) -> Option<DrmNode> { fn source_node_for_surface<'a>(
// Lets check the global drm-node the client got either through default-feedback or wl_drm w: &WlSurface,
let client = dh.get_client(w.id()).ok()?; mut devices: impl Iterator<Item = &'a Device>,
if let Some(normal_client) = client.get_data::<ClientState>() { ) -> Option<DrmNode> {
return normal_client.drm_node.clone(); with_renderer_surface_state(w, |state| {
} state.buffer().and_then(|buffer| {
// last but not least all xwayland-surfaces should also share a single node devices.find_map(|dev| {
if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() { dev.active_buffers
return xwayland_client.user_data().get::<DrmNode>().cloned(); .contains(&buffer.downgrade())
} .then_some(dev.render_node)
None })
})
})
} }
fn render_node_for_output( fn render_node_for_output(
dh: &DisplayHandle,
output: &Output, output: &Output,
primary_node: DrmNode,
target_node: DrmNode, target_node: DrmNode,
shell: &Shell, shell: &Shell,
non_target_devices: &Vec<(&DrmNode, &mut Device)>,
) -> DrmNode { ) -> DrmNode {
if target_node == primary_node {
return target_node;
}
let workspace = shell.active_space(output); let workspace = shell.active_space(output);
let nodes = workspace let nodes = workspace
.get_fullscreen() .get_fullscreen()
.map(|w| vec![w.clone()]) .map(|w| vec![w.clone()])
.unwrap_or_else(|| workspace.windows().collect::<Vec<_>>()) .unwrap_or_else(|| workspace.windows().collect::<Vec<_>>())
.into_iter() .into_iter()
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s, dh))) .flat_map(|w| {
w.wl_surface().and_then(|s| {
source_node_for_surface(&s, non_target_devices.iter().map(|(_, dev)| &**dev))
})
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if nodes.contains(&target_node) || nodes.is_empty() {
if nodes.is_empty() {
// we don't want to force needlessly expensive imports,
// so we only choose the target node if it is save and use the main_device otherwise.
// (possibly fixable, once we have kernel API to test for migrations or dmabuf-v5)
target_node target_node
} else { } else {
nodes primary_node
.iter()
.fold(HashMap::new(), |mut count_map, node| {
let count = count_map.entry(node).or_insert(0);
*count += 1;
count_map
})
.into_iter()
.reduce(|a, b| if a.1 > b.1 { a } else { b })
.map(|(node, _)| *node)
.unwrap_or(target_node)
} }
} }
@ -1494,11 +1552,17 @@ impl KmsState {
target: DrmNode, target: DrmNode,
shell: &Shell, shell: &Shell,
) { ) {
let render = render_node_for_output(dh, &output, target, &shell); let render = render_node_for_output(
&output,
self.primary_node,
target,
&shell,
&self.devices.iter_mut().collect(),
);
if let Err(err) = self.api.early_import( if let Err(err) = self.api.early_import(
if let Some(client) = dh.get_client(surface.id()).ok() { if let Some(client) = dh.get_client(surface.id()).ok() {
if let Some(normal_client) = client.get_data::<ClientState>() { if let Some(normal_client) = client.get_data::<ClientState>() {
normal_client.drm_node.clone() normal_client.advertised_drm_node.clone()
} else if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() { } else if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() {
xwayland_client.user_data().get::<DrmNode>().cloned() xwayland_client.user_data().get::<DrmNode>().cloned()
} else { } else {
@ -1514,19 +1578,42 @@ impl KmsState {
} }
} }
pub fn dmabuf_imported(&mut self, global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<()> { pub fn dmabuf_imported(
for device in self.devices.values() { &mut self,
_client: Option<Client>,
global: &DmabufGlobal,
dmabuf: Dmabuf,
) -> Result<DrmNode> {
for device in self.devices.values_mut() {
if device if 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)
{ {
if device.render_node != self.primary_node {
if !device.in_use(&self.primary_node) {
self.api
.as_mut()
.add_node(device.render_node, device.gbm.clone())
.context("Failed to initialize device")?;
let mut renderer = match self.api.single_renderer(&device.render_node) {
Ok(renderer) => renderer,
Err(err) => {
self.api.as_mut().remove_node(&device.render_node);
return Err(err).context("Failed to initialize renderer");
}
};
init_shaders(&mut renderer).context("Failed to initialize shaders")?;
}
}
return self return self
.api .api
.single_renderer(&device.render_node)? .single_renderer(&device.render_node)?
.import_dmabuf(&dmabuf, None) .import_dmabuf(&dmabuf, None)
.map(|_| ()) .map(|_| device.render_node)
.map_err(Into::into); .map_err(Into::into);
} }
} }
@ -1582,13 +1669,14 @@ impl KmsState {
if let Some(surface) = target_device.surfaces.get_mut(&crtc) { if let Some(surface) = target_device.surfaces.get_mut(&crtc) {
let target_node = target_device.render_node; let target_node = target_device.render_node;
let render_node = render_node_for_output( let render_node = render_node_for_output(
&state.common.display_handle,
&surface.output, &surface.output,
backend.primary_node,
target_node, target_node,
&state.common.shell, &state.common.shell,
&other,
); );
let common = &mut state.common;
let common = &mut state.common;
let result = if render_node != target_node { let result = if render_node != target_node {
let render_device = &mut other let render_device = &mut other
.iter_mut() .iter_mut()

View file

@ -50,7 +50,7 @@ impl State {
// initialize globals // initialize globals
let filter = move |client: &Client| { let filter = move |client: &Client| {
if let Some(normal_client) = client.get_data::<ClientState>() { if let Some(normal_client) = client.get_data::<ClientState>() {
let dev_id = normal_client.drm_node.unwrap(); let dev_id = normal_client.advertised_drm_node.unwrap();
return dev_id == render_node; return dev_id == render_node;
} }
if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() { if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() {

View file

@ -331,6 +331,15 @@ pub fn init_shaders<R: AsGlowRenderer>(renderer: &mut R) -> Result<(), GlesError
let glow_renderer = renderer.glow_renderer_mut(); let glow_renderer = renderer.glow_renderer_mut();
let gles_renderer: &mut GlesRenderer = glow_renderer.borrow_mut(); let gles_renderer: &mut GlesRenderer = glow_renderer.borrow_mut();
{
let egl_context = gles_renderer.egl_context();
if egl_context.user_data().get::<IndicatorShader>().is_some()
&& egl_context.user_data().get::<BackdropShader>().is_some()
{
return Ok(());
}
}
let outline_shader = gles_renderer.compile_custom_pixel_shader( let outline_shader = gles_renderer.compile_custom_pixel_shader(
OUTLINE_SHADER, OUTLINE_SHADER,
&[ &[

View file

@ -12,7 +12,10 @@ use anyhow::{Context, Result};
use std::{env, ffi::OsString, process, sync::Arc}; use std::{env, ffi::OsString, process, sync::Arc};
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use crate::wayland::handlers::compositor::client_compositor_state; use crate::{
state::BackendData, utils::prelude::SeatExt,
wayland::handlers::compositor::client_compositor_state,
};
pub mod backend; pub mod backend;
pub mod config; pub mod config;
@ -145,15 +148,23 @@ fn init_wayland_display(
event_loop event_loop
.handle() .handle()
.insert_source(source, |client_stream, _, state| { .insert_source(source, |client_stream, _, state| {
let node = match &state.backend {
BackendData::Kms(kms_state) if kms_state.auto_assign => kms_state
.target_node_for_output(&state.common.last_active_seat().active_output()),
_ => None,
};
if let Err(err) = state.common.display_handle.insert_client( if let Err(err) = state.common.display_handle.insert_client(
client_stream, client_stream,
Arc::new(if cfg!(debug_assertions) { Arc::new(if cfg!(debug_assertions) {
state.new_privileged_client_state() state.new_privileged_client_state()
} else if let Some(node) = node {
state.new_client_state_with_node(node)
} else { } else {
state.new_client_state() state.new_client_state()
}), }),
) { ) {
warn!(?err, "Error adding wayland client"); warn!(?err, "Error adding wayland client")
}; };
}) })
.with_context(|| "Failed to init the wayland socket source.")?; .with_context(|| "Failed to init the wayland socket source.")?;

View file

@ -1,11 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::{ use crate::{
backend::{ backend::{kms::KmsState, winit::WinitState, x11::X11State},
kms::{source_node_for_surface, KmsState},
winit::WinitState,
x11::X11State,
},
config::{Config, OutputConfig}, config::{Config, OutputConfig},
input::Devices, input::Devices,
shell::{grabs::SeatMoveGrabState, Shell}, shell::{grabs::SeatMoveGrabState, Shell},
@ -54,8 +50,8 @@ use smithay::{
wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode, wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode,
wayland_server::{ wayland_server::{
backend::{ClientData, ClientId, DisconnectReason}, backend::{ClientData, ClientId, DisconnectReason},
protocol::wl_shm, protocol::{wl_shm, wl_surface::WlSurface},
Client, DisplayHandle, Client, DisplayHandle, Resource,
}, },
}, },
utils::{Clock, IsAlive, Monotonic}, utils::{Clock, IsAlive, Monotonic},
@ -84,6 +80,7 @@ use smithay::{
virtual_keyboard::VirtualKeyboardManagerState, virtual_keyboard::VirtualKeyboardManagerState,
xwayland_keyboard_grab::XWaylandKeyboardGrabState, xwayland_keyboard_grab::XWaylandKeyboardGrabState,
}, },
xwayland::XWaylandClientData,
}; };
use time::UtcOffset; use time::UtcOffset;
use tracing::error; use tracing::error;
@ -111,7 +108,7 @@ macro_rules! fl {
pub struct ClientState { pub struct ClientState {
pub compositor_client_state: CompositorClientState, pub compositor_client_state: CompositorClientState,
pub workspace_client_state: WorkspaceClientState, pub workspace_client_state: WorkspaceClientState,
pub drm_node: Option<DrmNode>, pub advertised_drm_node: Option<DrmNode>,
pub privileged: bool, pub privileged: bool,
pub evls: LoopSignal, pub evls: LoopSignal,
pub security_context: Option<SecurityContext>, pub security_context: Option<SecurityContext>,
@ -123,6 +120,19 @@ impl ClientData for ClientState {
} }
} }
pub fn advertised_node_for_surface(w: &WlSurface, dh: &DisplayHandle) -> Option<DrmNode> {
// Lets check the global drm-node the client got either through default-feedback or wl_drm
let client = dh.get_client(w.id()).ok()?;
if let Some(normal_client) = client.get_data::<ClientState>() {
return normal_client.advertised_drm_node.clone();
}
// last but not least all xwayland-surfaces should also share a single node
if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() {
return xwayland_client.user_data().get::<DrmNode>().cloned();
}
None
}
#[derive(Debug)] #[derive(Debug)]
pub struct State { pub struct State {
pub backend: BackendData, pub backend: BackendData,
@ -168,7 +178,7 @@ pub struct Common {
pub seat_state: SeatState<State>, pub seat_state: SeatState<State>,
pub session_lock_manager_state: SessionLockManagerState, pub session_lock_manager_state: SessionLockManagerState,
pub shm_state: ShmState, pub shm_state: ShmState,
pub wl_drm_state: WlDrmState, pub wl_drm_state: WlDrmState<Option<DrmNode>>,
pub viewporter_state: ViewporterState, pub viewporter_state: ViewporterState,
pub kde_decoration_state: KdeDecorationState, pub kde_decoration_state: KdeDecorationState,
pub xdg_decoration_state: XdgDecorationState, pub xdg_decoration_state: XdgDecorationState,
@ -278,11 +288,16 @@ impl BackendData {
pub fn dmabuf_imported( pub fn dmabuf_imported(
&mut self, &mut self,
client: Option<Client>,
global: &DmabufGlobal, global: &DmabufGlobal,
dmabuf: Dmabuf, dmabuf: Dmabuf,
) -> Result<(), anyhow::Error> { ) -> Result<Option<DrmNode>, anyhow::Error> {
match self { match self {
BackendData::Kms(ref mut state) => state.dmabuf_imported(global, dmabuf)?, BackendData::Kms(ref mut state) => {
return state
.dmabuf_imported(client, global, dmabuf)
.map(|node| Some(node))
}
BackendData::Winit(ref mut state) => { BackendData::Winit(ref mut state) => {
state.backend.renderer().import_dmabuf(&dmabuf, None)?; state.backend.renderer().import_dmabuf(&dmabuf, None)?;
} }
@ -290,8 +305,8 @@ impl BackendData {
state.renderer.import_dmabuf(&dmabuf, None)?; state.renderer.import_dmabuf(&dmabuf, None)?;
} }
_ => unreachable!("No backend set when importing dmabuf"), _ => unreachable!("No backend set when importing dmabuf"),
} };
Ok(()) Ok(None)
} }
} }
@ -357,7 +372,7 @@ impl State {
ShmState::new::<Self>(dh, vec![wl_shm::Format::Xbgr8888, wl_shm::Format::Abgr8888]); ShmState::new::<Self>(dh, vec![wl_shm::Format::Xbgr8888, wl_shm::Format::Abgr8888]);
let seat_state = SeatState::<Self>::new(); let seat_state = SeatState::<Self>::new();
let viewporter_state = ViewporterState::new::<Self>(dh); let viewporter_state = ViewporterState::new::<Self>(dh);
let wl_drm_state = WlDrmState; let wl_drm_state = WlDrmState::<Option<DrmNode>>::default();
let kde_decoration_state = KdeDecorationState::new::<Self>(&dh, Mode::Client); let kde_decoration_state = KdeDecorationState::new::<Self>(&dh, Mode::Client);
let xdg_decoration_state = XdgDecorationState::new::<Self>(&dh); let xdg_decoration_state = XdgDecorationState::new::<Self>(&dh);
let session_lock_manager_state = let session_lock_manager_state =
@ -432,19 +447,8 @@ impl State {
ClientState { ClientState {
compositor_client_state: CompositorClientState::default(), compositor_client_state: CompositorClientState::default(),
workspace_client_state: WorkspaceClientState::default(), workspace_client_state: WorkspaceClientState::default(),
drm_node: match &self.backend { advertised_drm_node: match &self.backend {
BackendData::Kms(kms_state) => { BackendData::Kms(kms_state) => Some(kms_state.primary_node),
match std::env::var("COSMIC_RENDER_AUTO_ASSIGN").map(|val| val.to_lowercase()) {
Ok(val) if val == "y" || val == "yes" || val == "true" => Some(
kms_state
.target_node_for_output(
&self.common.last_active_seat().active_output(),
)
.unwrap_or(kms_state.primary),
),
_ => Some(kms_state.primary),
}
}
_ => None, _ => None,
}, },
privileged: false, privileged: false,
@ -457,7 +461,7 @@ impl State {
ClientState { ClientState {
compositor_client_state: CompositorClientState::default(), compositor_client_state: CompositorClientState::default(),
workspace_client_state: WorkspaceClientState::default(), workspace_client_state: WorkspaceClientState::default(),
drm_node: Some(drm_node), advertised_drm_node: Some(drm_node),
privileged: false, privileged: false,
evls: self.common.event_loop_signal.clone(), evls: self.common.event_loop_signal.clone(),
security_context: None, security_context: None,
@ -468,8 +472,8 @@ impl State {
ClientState { ClientState {
compositor_client_state: CompositorClientState::default(), compositor_client_state: CompositorClientState::default(),
workspace_client_state: WorkspaceClientState::default(), workspace_client_state: WorkspaceClientState::default(),
drm_node: match &self.backend { advertised_drm_node: match &self.backend {
BackendData::Kms(kms_state) => Some(kms_state.primary), BackendData::Kms(kms_state) => Some(kms_state.primary_node),
_ => None, _ => None,
}, },
privileged: true, privileged: true,
@ -537,7 +541,7 @@ impl Common {
surface_primary_scanout_output, surface_primary_scanout_output,
); );
if let Some(feedback) = if let Some(feedback) =
source_node_for_surface(lock_surface.wl_surface(), &self.display_handle) advertised_node_for_surface(lock_surface.wl_surface(), &self.display_handle)
.and_then(|source| dmabuf_feedback(source)) .and_then(|source| dmabuf_feedback(source))
{ {
send_dmabuf_feedback_surface_tree( send_dmabuf_feedback_surface_tree(
@ -599,7 +603,7 @@ impl Common {
if let Some(feedback) = window if let Some(feedback) = window
.wl_surface() .wl_surface()
.and_then(|wl_surface| { .and_then(|wl_surface| {
source_node_for_surface(&wl_surface, &self.display_handle) advertised_node_for_surface(&wl_surface, &self.display_handle)
}) })
.and_then(|source| dmabuf_feedback(source)) .and_then(|source| dmabuf_feedback(source))
{ {
@ -644,7 +648,7 @@ impl Common {
if let Some(feedback) = window if let Some(feedback) = window
.wl_surface() .wl_surface()
.and_then(|wl_surface| { .and_then(|wl_surface| {
source_node_for_surface(&wl_surface, &self.display_handle) advertised_node_for_surface(&wl_surface, &self.display_handle)
}) })
.and_then(|source| dmabuf_feedback(source)) .and_then(|source| dmabuf_feedback(source))
{ {
@ -693,7 +697,9 @@ impl Common {
window.send_frame(output, time, throttle, surface_primary_scanout_output); window.send_frame(output, time, throttle, surface_primary_scanout_output);
if let Some(feedback) = window if let Some(feedback) = window
.wl_surface() .wl_surface()
.and_then(|wl_surface| source_node_for_surface(&wl_surface, &self.display_handle)) .and_then(|wl_surface| {
advertised_node_for_surface(&wl_surface, &self.display_handle)
})
.and_then(|source| dmabuf_feedback(source)) .and_then(|source| dmabuf_feedback(source))
{ {
window.send_dmabuf_feedback( window.send_dmabuf_feedback(
@ -741,8 +747,9 @@ impl Common {
throttle, throttle,
surface_primary_scanout_output, surface_primary_scanout_output,
); );
if let Some(feedback) = source_node_for_surface(&wl_surface, &self.display_handle) if let Some(feedback) =
.and_then(|source| dmabuf_feedback(source)) advertised_node_for_surface(&wl_surface, &self.display_handle)
.and_then(|source| dmabuf_feedback(source))
{ {
send_dmabuf_feedback_surface_tree( send_dmabuf_feedback_surface_tree(
&wl_surface, &wl_surface,
@ -780,7 +787,7 @@ impl Common {
}); });
layer_surface.send_frame(output, time, throttle, surface_primary_scanout_output); layer_surface.send_frame(output, time, throttle, surface_primary_scanout_output);
if let Some(feedback) = if let Some(feedback) =
source_node_for_surface(layer_surface.wl_surface(), &self.display_handle) advertised_node_for_surface(layer_surface.wl_surface(), &self.display_handle)
.and_then(|source| dmabuf_feedback(source)) .and_then(|source| dmabuf_feedback(source))
{ {
layer_surface.send_dmabuf_feedback( layer_surface.send_dmabuf_feedback(

View file

@ -16,9 +16,8 @@ use smithay::{
use tracing::warn; use tracing::warn;
use crate::{ use crate::{
backend::kms::source_node_for_surface,
shell::element::CosmicSurface, shell::element::CosmicSurface,
state::{BackendData, State}, state::{advertised_node_for_surface, BackendData, State},
}; };
pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) {
@ -99,8 +98,8 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) {
if let Some(wl_surface) = surface.wl_surface() { if let Some(wl_surface) = surface.wl_surface() {
let res = match &mut state.backend { let res = match &mut state.backend {
BackendData::Kms(kms) => { BackendData::Kms(kms) => {
let node = source_node_for_surface(&wl_surface, &state.common.display_handle) let node = advertised_node_for_surface(&wl_surface, &state.common.display_handle)
.unwrap_or(kms.primary); .unwrap_or(kms.primary_node);
kms.api kms.api
.single_renderer(&node) .single_renderer(&node)
.with_context(|| "Failed to get renderer for screenshot") .with_context(|| "Failed to get renderer for screenshot")

View file

@ -1,10 +1,22 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::utils::prelude::*; use crate::{state::BackendData, utils::prelude::*};
use smithay::{ use smithay::{
reexports::wayland_server::protocol::wl_buffer::WlBuffer, wayland::buffer::BufferHandler, reexports::wayland_server::{protocol::wl_buffer::WlBuffer, Resource},
wayland::buffer::BufferHandler,
}; };
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.devices.values_mut() {
if device.active_buffers.remove(&buffer.downgrade()) {
if !device.in_use(&kms_state.primary_node) {
kms_state.api.as_mut().remove_node(&device.render_node);
}
break;
}
}
}
}
} }

View file

@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::state::State; use crate::state::{BackendData, 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},
}; };
@ -18,10 +19,33 @@ impl DmabufHandler for State {
dmabuf: Dmabuf, dmabuf: Dmabuf,
import_notifier: ImportNotifier, import_notifier: ImportNotifier,
) { ) {
if self.backend.dmabuf_imported(global, dmabuf).is_err() { match self
import_notifier.failed(); .backend
} else { .dmabuf_imported(import_notifier.client(), global, dmabuf)
let _ = import_notifier.successful::<State>(); {
Err(err) => {
tracing::debug!(?err, "dmabuf import failed");
import_notifier.failed()
}
Ok(Some(node)) => {
// 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
.devices
.values_mut()
.find(|dev| dev.render_node == node)
{
device.active_buffers.insert(buffer.downgrade());
}
}
}
Ok(None) => {
let _ = import_notifier.successful::<State>();
}
} }
} }
} }

View file

@ -1,19 +1,40 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::{ use crate::{
state::State, state::{BackendData, State},
wayland::protocols::drm::{DrmHandler, ImportError}, wayland::protocols::drm::{delegate_wl_drm, DrmHandler, ImportError},
};
use smithay::{
backend::{allocator::dmabuf::Dmabuf, drm::DrmNode},
reexports::wayland_server::{protocol::wl_buffer::WlBuffer, Resource},
wayland::dmabuf::DmabufGlobal,
}; };
use smithay::{backend::allocator::dmabuf::Dmabuf, wayland::dmabuf::DmabufGlobal};
impl DrmHandler for State { impl DrmHandler<Option<DrmNode>> for State {
fn dmabuf_imported( fn dmabuf_imported(
&mut self, &mut self,
global: &DmabufGlobal, global: &DmabufGlobal,
dmabuf: Dmabuf, dmabuf: Dmabuf,
) -> Result<(), ImportError> { ) -> Result<Option<DrmNode>, ImportError> {
self.backend self.backend
.dmabuf_imported(global, dmabuf) .dmabuf_imported(None, global, dmabuf)
.map_err(|_| ImportError::Failed) .map_err(|_| ImportError::Failed)
} }
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
.devices
.values_mut()
.find(|device| device.render_node == node)
{
device.active_buffers.insert(buffer.downgrade());
}
}
}
}
} }
delegate_wl_drm!(State; Option<DrmNode>);

View file

@ -31,7 +31,6 @@ pub mod toplevel_info;
pub mod toplevel_management; pub mod toplevel_management;
pub mod viewporter; pub mod viewporter;
pub mod virtual_keyboard; pub mod virtual_keyboard;
pub mod wl_drm;
pub mod workspace; pub mod workspace;
pub mod xdg_activation; pub mod xdg_activation;
pub mod xdg_shell; pub mod xdg_shell;

View file

@ -180,7 +180,7 @@ impl ScreencopyHandler for State {
.and_then(|client| { .and_then(|client| {
// Lets check the global drm-node the client got either through default-feedback or wl_drm // Lets check the global drm-node the client got either through default-feedback or wl_drm
if let Some(normal_client) = client.get_data::<ClientState>() { if let Some(normal_client) = client.get_data::<ClientState>() {
return normal_client.drm_node.clone(); return normal_client.advertised_drm_node.clone();
} }
// last but not least all xwayland-surfaces should also share a single node // last but not least all xwayland-surfaces should also share a single node
if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() { if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() {
@ -188,7 +188,7 @@ impl ScreencopyHandler for State {
} }
None None
}) })
.unwrap_or(kms.primary.clone()); .unwrap_or(kms.primary_node.clone());
_kms_renderer = Some(kms.api.single_renderer(&node).unwrap()); _kms_renderer = Some(kms.api.single_renderer(&node).unwrap());
_kms_renderer.as_mut().unwrap().as_mut() _kms_renderer.as_mut().unwrap().as_mut()
} }
@ -456,7 +456,9 @@ fn formats_for_output(
let mut _kms_renderer = None; let mut _kms_renderer = None;
let renderer = match backend { let renderer = match backend {
BackendData::Kms(ref mut kms) => { BackendData::Kms(ref mut kms) => {
let node = kms.target_node_for_output(&output).unwrap_or(kms.primary); let node = kms
.target_node_for_output(&output)
.unwrap_or(kms.primary_node);
_kms_renderer = Some(kms.api.single_renderer(&node).unwrap()); _kms_renderer = Some(kms.api.single_renderer(&node).unwrap());
_kms_renderer.as_mut().unwrap().as_mut() _kms_renderer.as_mut().unwrap().as_mut()
} }
@ -530,7 +532,7 @@ fn node_from_params(
BackendData::Kms(kms) => Some( BackendData::Kms(kms) => Some(
output output
.and_then(|output| kms.target_node_for_output(output)) .and_then(|output| kms.target_node_for_output(output))
.unwrap_or(kms.primary), .unwrap_or(kms.primary_node),
), ),
_ => None, _ => None,
}, },

View file

@ -37,14 +37,14 @@ impl SecurityContextHandler for State {
let drm_node = client_data let drm_node = client_data
.as_ref() .as_ref()
.and_then(|data| data.downcast_ref::<ClientState>()) .and_then(|data| data.downcast_ref::<ClientState>())
.and_then(|data| data.drm_node.clone()) .and_then(|data| data.advertised_drm_node.clone())
.or_else(|| { .or_else(|| {
client_data client_data
.as_ref() .as_ref()
.and_then(|data| data.downcast_ref::<XWaylandClientData>()) .and_then(|data| data.downcast_ref::<XWaylandClientData>())
.and_then(|data| data.user_data().get::<DrmNode>().cloned()) .and_then(|data| data.user_data().get::<DrmNode>().cloned())
}) })
.or_else(|| new_state.drm_node.clone()); .or_else(|| new_state.advertised_drm_node.clone());
if let Err(err) = state.common.display_handle.insert_client( if let Err(err) = state.common.display_handle.insert_client(
client_stream, client_stream,
@ -53,7 +53,7 @@ impl SecurityContextHandler for State {
privileged: privileged privileged: privileged
&& security_context.sandbox_engine.as_deref() && security_context.sandbox_engine.as_deref()
== Some("com.system76.CosmicPanel"), == Some("com.system76.CosmicPanel"),
drm_node, advertised_drm_node: drm_node,
..new_state ..new_state
}), }),
) { ) {

View file

@ -1,5 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{state::State, wayland::protocols::drm::delegate_wl_drm};
delegate_wl_drm!(State);

View file

@ -7,6 +7,7 @@
// You can use all the types from my_protocol as if they went from `wayland_client::protocol`. // You can use all the types from my_protocol as if they went from `wayland_client::protocol`.
pub use generated::wl_drm; pub use generated::wl_drm;
#[allow(non_snake_case, non_upper_case_globals, non_camel_case_types)]
mod generated { mod generated {
use smithay::reexports::wayland_server::{self, protocol::*}; use smithay::reexports::wayland_server::{self, protocol::*};
@ -43,13 +44,15 @@ pub enum ImportError {
InvalidFormat, InvalidFormat,
} }
pub trait DrmHandler { pub trait DrmHandler<R: 'static> {
fn dmabuf_imported(&mut self, global: &DmabufGlobal, dmabuf: Dmabuf) fn dmabuf_imported(&mut self, global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<R, ImportError>;
-> Result<(), ImportError>; fn buffer_created(&mut self, buffer: WlBuffer, result: R) {
let _ = (buffer, result);
}
} }
#[derive(Debug)] #[derive(Debug, Default)]
pub struct WlDrmState; pub struct WlDrmState<R>(std::marker::PhantomData<R>);
/// Data associated with a drm global. /// Data associated with a drm global.
pub struct DrmGlobalData { pub struct DrmGlobalData {
@ -64,13 +67,14 @@ pub struct DrmInstanceData {
dmabuf_global: DmabufGlobal, dmabuf_global: DmabufGlobal,
} }
impl<D> GlobalDispatch<wl_drm::WlDrm, DrmGlobalData, D> for WlDrmState impl<D, R> GlobalDispatch<wl_drm::WlDrm, DrmGlobalData, D> for WlDrmState<R>
where where
D: GlobalDispatch<wl_drm::WlDrm, DrmGlobalData> D: GlobalDispatch<wl_drm::WlDrm, DrmGlobalData>
+ Dispatch<wl_drm::WlDrm, DrmInstanceData> + Dispatch<wl_drm::WlDrm, DrmInstanceData>
+ BufferHandler + BufferHandler
+ DmabufHandler + DmabufHandler
+ 'static, + 'static,
R: 'static,
{ {
fn bind( fn bind(
_state: &mut D, _state: &mut D,
@ -102,14 +106,15 @@ where
} }
} }
impl<D> Dispatch<wl_drm::WlDrm, DrmInstanceData, D> for WlDrmState impl<D, R> Dispatch<wl_drm::WlDrm, DrmInstanceData, D> for WlDrmState<R>
where where
D: GlobalDispatch<wl_drm::WlDrm, DrmGlobalData> D: GlobalDispatch<wl_drm::WlDrm, DrmGlobalData>
+ Dispatch<wl_drm::WlDrm, DrmInstanceData> + Dispatch<wl_drm::WlDrm, DrmInstanceData>
+ Dispatch<WlBuffer, Dmabuf> + Dispatch<WlBuffer, Dmabuf>
+ BufferHandler + BufferHandler
+ DrmHandler + DrmHandler<R>
+ 'static, + 'static,
R: 'static,
{ {
fn request( fn request(
state: &mut D, state: &mut D,
@ -178,10 +183,11 @@ where
match dma.build() { match dma.build() {
Some(dmabuf) => { Some(dmabuf) => {
match state.dmabuf_imported(&data.dmabuf_global, dmabuf.clone()) { match state.dmabuf_imported(&data.dmabuf_global, dmabuf.clone()) {
Ok(()) => { Ok(result) => {
// import was successful // import was successful
data_init.init(id, dmabuf); let buffer = data_init.init(id, dmabuf);
trace!("Created a new validated dma wl_buffer via wl_drm."); trace!("Created a new validated dma wl_buffer via wl_drm.");
state.buffer_created(buffer, result);
} }
Err(ImportError::InvalidFormat) => { Err(ImportError::InvalidFormat) => {
@ -212,7 +218,7 @@ where
} }
} }
impl WlDrmState { impl<R: 'static> WlDrmState<R> {
pub fn create_global<D>( pub fn create_global<D>(
&mut self, &mut self,
display: &DisplayHandle, display: &DisplayHandle,
@ -267,13 +273,13 @@ impl WlDrmState {
} }
macro_rules! delegate_wl_drm { macro_rules! delegate_wl_drm {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty; $r: ty) => {
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::wayland::protocols::drm::wl_drm::WlDrm: $crate::wayland::protocols::drm::DrmGlobalData $crate::wayland::protocols::drm::wl_drm::WlDrm: $crate::wayland::protocols::drm::DrmGlobalData
] => $crate::wayland::protocols::drm::WlDrmState); ] => $crate::wayland::protocols::drm::WlDrmState<$r>);
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::wayland::protocols::drm::wl_drm::WlDrm: $crate::wayland::protocols::drm::DrmInstanceData $crate::wayland::protocols::drm::wl_drm::WlDrm: $crate::wayland::protocols::drm::DrmInstanceData
] => $crate::wayland::protocols::drm::WlDrmState); ] => $crate::wayland::protocols::drm::WlDrmState<$r>);
}; };
} }
pub(crate) use delegate_wl_drm; pub(crate) use delegate_wl_drm;