From bc49507353f6980496b94ffd25b119505afd38d3 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 31 Mar 2023 13:57:37 +0200 Subject: [PATCH] kms: Dmabuf feedback support --- src/backend/kms/mod.rs | 126 ++++++++++++++++++---- src/backend/kms/socket.rs | 11 +- src/backend/winit.rs | 2 +- src/backend/x11.rs | 2 +- src/shell/element/surface.rs | 55 +++++++++- src/shell/layout/floating/grabs/moving.rs | 5 + src/state.rs | 100 +++++++++++++++-- src/wayland/handlers/screencopy.rs | 2 + 8 files changed, 267 insertions(+), 36 deletions(-) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 63fb6472..30665f99 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -6,7 +6,7 @@ use crate::{ backend::render::{workspace_elements, CLEAR_COLOR}, config::OutputConfig, shell::Shell, - state::{BackendData, ClientState, Common, Data, Fps}, + state::{BackendData, ClientState, Common, Data, Fps, SurfaceDmabufFeedback}, utils::prelude::*, wayland::{ handlers::screencopy::{render_session, UserdataExt}, @@ -22,7 +22,7 @@ use smithay::{ dmabuf::{AnyError, AsDmabuf, Dmabuf, DmabufAllocator}, gbm::{GbmAllocator, GbmBufferFlags, GbmDevice}, vulkan::{ImageUsageFlags, VulkanAllocator}, - Allocator, Format, + Allocator, Format, Fourcc, }, drm::{ compositor::{DrmCompositor, PrimaryPlaneElement}, @@ -38,7 +38,7 @@ use smithay::{ glow::GlowRenderer, multigpu::{gbm::GbmGlesBackend, Error as MultiError, GpuManager}, utils::draw_render_elements, - Bind, Blit, Offscreen, Renderer, TextureFilter, + Bind, Blit, ImportDma, Offscreen, Renderer, TextureFilter, }, session::{libseat::LibSeatSession, Event as SessionEvent, Session}, udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent}, @@ -58,12 +58,15 @@ use smithay::{ }, input::Libinput, nix::{fcntl::OFlag, sys::stat::dev_t}, - wayland_protocols::wp::presentation_time::server::wp_presentation_feedback, + wayland_protocols::wp::{ + linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1, + presentation_time::server::wp_presentation_feedback, + }, wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource}, }, utils::{DeviceFd, Size, Transform}, wayland::{ - dmabuf::{get_dmabuf, DmabufGlobal}, + dmabuf::{get_dmabuf, DmabufFeedbackBuilder, DmabufGlobal}, relative_pointer::RelativePointerManagerState, seat::WaylandFocus, }, @@ -119,6 +122,7 @@ pub struct Surface { dirty: bool, render_timer_token: Option, fps: Fps, + feedback: HashMap, } pub type GbmDrmCompositor = DrmCompositor< @@ -420,7 +424,7 @@ impl State { path.display() ) })?; - let formats = egl_context.dmabuf_render_formats().clone(); + let formats = egl_context.dmabuf_texture_formats().clone(); (render_node, formats) // NOTE: We need the to drop the EGL types here again, // otherwise the EGLDisplay created below might share the same GBM context @@ -889,6 +893,7 @@ impl Device { dirty: false, render_timer_token: None, fps: Fps::new(renderer.as_mut()), + feedback: HashMap::new(), }; self.surfaces.insert(crtc, data); @@ -896,6 +901,19 @@ impl Device { } } +pub fn source_node_for_surface(w: &WlSurface, dh: &DisplayHandle) -> Option { + // 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::() { + return normal_client.drm_node.clone(); + } + // last but not least all xwayland-surfaces should also share a single node + if let Some(xwayland_client) = client.get_data::() { + return xwayland_client.user_data().get::().cloned(); + } + None +} + fn render_node_for_output( dh: &DisplayHandle, output: &Output, @@ -908,16 +926,7 @@ fn render_node_for_output( .map(|w| vec![w.clone()]) .unwrap_or_else(|| workspace.windows().collect::>()) .into_iter() - .flat_map(|w| { - let client = dh.get_client(w.wl_surface()?.id()).ok()?; - if let Some(normal_client) = client.get_data::() { - return normal_client.drm_node.clone(); - } - if let Some(xwayland_client) = client.get_data::() { - return xwayland_client.user_data().get::().cloned(); - } - None - }) + .flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s, dh))) .collect::>(); if nodes.contains(&target_node) || nodes.is_empty() { target_node @@ -936,6 +945,59 @@ fn render_node_for_output( } } +fn get_surface_dmabuf_feedback( + render_node: DrmNode, + render_formats: HashSet, + target_formats: HashSet, + compositor: &GbmDrmCompositor, +) -> SurfaceDmabufFeedback { + let combined_formats = render_formats + .intersection(&target_formats) + .copied() + .collect::>(); + + let surface = compositor.surface(); + let planes = surface.planes().unwrap(); + // We limit the scan-out trache to formats we can also render from + // so that there is always a fallback render path available in case + // the supplied buffer can not be scanned out directly + let planes_formats = surface + .supported_formats(planes.primary.handle) + .unwrap() + .into_iter() + .chain( + planes + .overlay + .iter() + .flat_map(|p| surface.supported_formats(p.handle).unwrap()), + ) + .collect::>() + .intersection(&combined_formats) + .copied() + .collect::>(); + + let target_node = surface.device_fd().dev_id().unwrap(); + let mut builder = DmabufFeedbackBuilder::new(render_node.dev_id(), render_formats); + if target_node != render_node.dev_id() && !combined_formats.is_empty() { + builder = builder.add_preference_tranche(target_node, None, combined_formats); + }; + let render_feedback = builder.clone().build().unwrap(); + + let scanout_feedback = builder + .add_preference_tranche( + target_node, + Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout), + planes_formats, + ) + .build() + .unwrap(); + + SurfaceDmabufFeedback { + render_feedback, + scanout_feedback, + } +} + impl Surface { pub fn render_output( &mut self, @@ -1088,7 +1150,34 @@ impl Surface { } else { None }; - state.send_frames(&self.output, &frame_result.states); + + state.send_frames(&self.output, &frame_result.states, |source_node| { + Some( + self.feedback + .entry(source_node) + .or_insert_with(|| { + let render_formats = api + .single_renderer(&source_node) + .unwrap() + .dmabuf_formats() + .copied() + .collect::>(); + let target_formats = api + .single_renderer(target_node) + .unwrap() + .dmabuf_formats() + .copied() + .collect::>(); + get_surface_dmabuf_feedback( + source_node, + render_formats, + target_formats, + compositor, + ) + }) + .clone(), + ) + }); compositor .queue_frame(feedback) .with_context(|| "Failed to submit result for display")? @@ -1204,6 +1293,7 @@ impl KmsState { GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT, ), device.gbm.clone(), + &[Fourcc::Abgr8888, Fourcc::Argb8888], device.formats.clone(), drm.cursor_size(), Some(device.gbm.clone()), @@ -1287,8 +1377,6 @@ impl KmsState { } pub fn dmabuf_imported(&mut self, global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<()> { - use smithay::backend::renderer::ImportDma; - for device in self.devices.values() { if device .socket diff --git a/src/backend/kms/socket.rs b/src/backend/kms/socket.rs index 0a3822cd..b0f1c897 100644 --- a/src/backend/kms/socket.rs +++ b/src/backend/kms/socket.rs @@ -10,7 +10,10 @@ use smithay::{ calloop::RegistrationToken, wayland_server::{backend::GlobalId, Client, DisplayHandle}, }, - wayland::{dmabuf::DmabufGlobal, socket::ListeningSocketSource}, + wayland::{ + dmabuf::{DmabufFeedbackBuilder, DmabufGlobal}, + socket::ListeningSocketSource, + }, xwayland::XWaylandClientData, }; use std::sync::Arc; @@ -59,10 +62,14 @@ impl State { false }; + let feedback = DmabufFeedbackBuilder::new(render_node.dev_id(), formats.clone()) + .build() + .with_context(|| "Failed to create drm format shared memory table")?; + let dmabuf_global = self .common .dmabuf_state - .create_global_with_filter::(dh, formats.clone(), filter); + .create_global_with_filter_and_default_feedback::(dh, &feedback, filter); let drm_global_id = self .common diff --git a/src/backend/winit.rs b/src/backend/winit.rs index fde048b6..0fb26f6b 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -83,7 +83,7 @@ impl WinitState { self.screencopy.clear(); #[cfg(feature = "debug")] self.fps.displayed(); - state.send_frames(&self.output, &states); + state.send_frames(&self.output, &states, |_| None); if damage.is_some() { let mut output_presentation_feedback = state.take_presentation_feedback(&self.output, &states); diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 1f7f0a26..60595d76 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -247,7 +247,7 @@ impl Surface { .with_context(|| "Failed to submit buffer for display")?; #[cfg(feature = "debug")] self.fps.displayed(); - state.send_frames(&self.output, &states); + state.send_frames(&self.output, &states, |_| None); if damage.is_some() { let mut output_presentation_feedback = state.take_presentation_feedback(&self.output, &states); diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 9ea91eaa..c312c5f7 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -2,13 +2,17 @@ use std::time::Duration; use smithay::{ backend::renderer::{ - element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, + element::{ + surface::WaylandSurfaceRenderElement, utils::select_dmabuf_feedback, AsRenderElements, + RenderElementStates, + }, ImportAll, Renderer, }, desktop::{ utils::{ - send_frames_surface_tree, take_presentation_feedback_surface_tree, - with_surfaces_surface_tree, OutputPresentationFeedback, + send_dmabuf_feedback_surface_tree, send_frames_surface_tree, + take_presentation_feedback_surface_tree, with_surfaces_surface_tree, + OutputPresentationFeedback, }, Window, }, @@ -34,6 +38,8 @@ use smithay::{ xwayland::{xwm::X11Relatable, X11Surface}, }; +use crate::state::SurfaceDmabufFeedback; + space_elements! { #[derive(Debug, Clone, PartialEq)] pub CosmicSurface; @@ -369,7 +375,7 @@ impl CosmicSurface { { match self { CosmicSurface::Wayland(window) => { - window.send_frame(output, time, throttle, primary_scan_out_output) + window.send_frame(output, time, throttle, primary_scan_out_output); } CosmicSurface::X11(surface) => { if let Some(wl_surface) = surface.wl_surface() { @@ -386,6 +392,47 @@ impl CosmicSurface { } } + pub fn send_dmabuf_feedback( + &self, + output: &Output, + feedback: &SurfaceDmabufFeedback, + render_element_states: &RenderElementStates, + primary_scan_out_output: F1, + ) where + F1: FnMut(&WlSurface, &SurfaceData) -> Option + Copy, + { + match self { + CosmicSurface::Wayland(window) => { + window.send_dmabuf_feedback(output, primary_scan_out_output, |surface, _| { + select_dmabuf_feedback( + surface, + render_element_states, + &feedback.render_feedback, + &feedback.scanout_feedback, + ) + }) + } + CosmicSurface::X11(surface) => { + if let Some(wl_surface) = surface.wl_surface() { + send_dmabuf_feedback_surface_tree( + &wl_surface, + output, + primary_scan_out_output, + |surface, _| { + select_dmabuf_feedback( + surface, + render_element_states, + &feedback.render_feedback, + &feedback.scanout_feedback, + ) + }, + ) + } + } + _ => unreachable!(), + } + } + pub fn take_presentation_feedback( &self, output_feedback: &mut OutputPresentationFeedback, diff --git a/src/shell/layout/floating/grabs/moving.rs b/src/shell/layout/floating/grabs/moving.rs index 047150bb..7c1d3839 100644 --- a/src/shell/layout/floating/grabs/moving.rs +++ b/src/shell/layout/floating/grabs/moving.rs @@ -5,6 +5,7 @@ use crate::{ shell::{ element::{window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement}, focus::target::{KeyboardFocusTarget, PointerFocusTarget}, + CosmicSurface, }, utils::prelude::*, }; @@ -91,6 +92,10 @@ impl MoveGrabState { .active_window() .send_frame(output, time, throttle, primary_scan_out_output) } + + pub fn window(&self) -> CosmicSurface { + self.window.active_window() + } } pub struct MoveSurfaceGrab { diff --git a/src/state.rs b/src/state.rs index b93c7afd..3597e341 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - backend::{kms::KmsState, winit::WinitState, x11::X11State}, + backend::{ + kms::{source_node_for_surface, KmsState}, + winit::WinitState, + x11::X11State, + }, config::{Config, OutputConfig}, shell::{layout::floating::SeatMoveGrabState, Shell}, utils::prelude::*, @@ -20,15 +24,18 @@ use smithay::{ backend::{ drm::DrmNode, renderer::{ - element::{default_primary_scanout_output_compare, RenderElementStates}, + element::{ + default_primary_scanout_output_compare, utils::select_dmabuf_feedback, + RenderElementStates, + }, glow::GlowRenderer, }, }, desktop::utils::{ - send_frames_surface_tree, surface_presentation_feedback_flags_from_states, - surface_primary_scanout_output, take_presentation_feedback_surface_tree, - update_surface_primary_scanout_output, with_surfaces_surface_tree, - OutputPresentationFeedback, + send_dmabuf_feedback_surface_tree, send_frames_surface_tree, + surface_presentation_feedback_flags_from_states, surface_primary_scanout_output, + take_presentation_feedback_surface_tree, update_surface_primary_scanout_output, + with_surfaces_surface_tree, OutputPresentationFeedback, }, input::{pointer::CursorImageStatus, Seat, SeatState}, output::{Mode as OutputMode, Output, Scale}, @@ -45,11 +52,12 @@ use smithay::{ wayland::{ compositor::CompositorState, data_device::DataDeviceState, - dmabuf::DmabufState, + dmabuf::{DmabufFeedback, DmabufState}, keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitState, output::OutputManagerState, presentation::PresentationState, primary_selection::PrimarySelectionState, + seat::WaylandFocus, shell::{kde::decoration::KdeDecorationState, xdg::decoration::XdgDecorationState}, shm::ShmState, viewporter::ViewporterState, @@ -130,6 +138,12 @@ pub enum BackendData { Unset, } +#[derive(Debug, Clone)] +pub struct SurfaceDmabufFeedback { + pub render_feedback: DmabufFeedback, + pub scanout_feedback: DmabufFeedback, +} + impl BackendData { pub fn kms(&mut self) -> &mut KmsState { match self { @@ -363,7 +377,12 @@ impl Common { self.last_active_seat.as_ref().expect("No seat?") } - pub fn send_frames(&self, output: &Output, render_element_states: &RenderElementStates) { + pub fn send_frames( + &self, + output: &Output, + render_element_states: &RenderElementStates, + mut dmabuf_feedback: impl FnMut(DrmNode) -> Option, + ) { let time = self.clock.now(); let throttle = Some(Duration::from_secs(1)); @@ -401,6 +420,21 @@ impl Common { throttle, surface_primary_scanout_output, ); + let window = grab_state.window(); + if let Some(feedback) = window + .wl_surface() + .and_then(|wl_surface| { + source_node_for_surface(&wl_surface, &self.display_handle) + }) + .and_then(|source| dmabuf_feedback(source)) + { + window.send_dmabuf_feedback( + output, + &feedback, + render_element_states, + surface_primary_scanout_output, + ); + } } } } @@ -432,6 +466,20 @@ impl Common { ); }); window.send_frame(output, time, throttle, surface_primary_scanout_output); + if let Some(feedback) = window + .wl_surface() + .and_then(|wl_surface| { + source_node_for_surface(&wl_surface, &self.display_handle) + }) + .and_then(|source| dmabuf_feedback(source)) + { + window.send_dmabuf_feedback( + output, + &feedback, + render_element_states, + surface_primary_scanout_output, + ); + } } }); @@ -466,7 +514,24 @@ impl Common { time, throttle, surface_primary_scanout_output, - ) + ); + if let Some(feedback) = source_node_for_surface(&wl_surface, &self.display_handle) + .and_then(|source| dmabuf_feedback(source)) + { + send_dmabuf_feedback_surface_tree( + &wl_surface, + output, + surface_primary_scanout_output, + |surface, _| { + select_dmabuf_feedback( + surface, + render_element_states, + &feedback.render_feedback, + &feedback.scanout_feedback, + ) + }, + ) + } } }); @@ -482,6 +547,23 @@ impl Common { ); }); layer_surface.send_frame(output, time, throttle, surface_primary_scanout_output); + if let Some(feedback) = + source_node_for_surface(layer_surface.wl_surface(), &self.display_handle) + .and_then(|source| dmabuf_feedback(source)) + { + layer_surface.send_dmabuf_feedback( + output, + surface_primary_scanout_output, + |surface, _| { + select_dmabuf_feedback( + surface, + render_element_states, + &feedback.render_feedback, + &feedback.scanout_feedback, + ) + }, + ); + } } } diff --git a/src/wayland/handlers/screencopy.rs b/src/wayland/handlers/screencopy.rs index b0042911..cbb7638d 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -178,9 +178,11 @@ impl ScreencopyHandler for State { .get_client(surface.id()) .ok() .and_then(|client| { + // Lets check the global drm-node the client got either through default-feedback or wl_drm if let Some(normal_client) = client.get_data::() { return normal_client.drm_node.clone(); } + // last but not least all xwayland-surfaces should also share a single node if let Some(xwayland_client) = client.get_data::() { return xwayland_client.user_data().get::().cloned(); }