diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index fc456383..cb8490b0 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}, @@ -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,16 @@ 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}, + compositor::with_states, + dmabuf::{get_dmabuf, DmabufFeedbackBuilder, DmabufGlobal, SurfaceDmabufFeedbackState}, relative_pointer::RelativePointerManagerState, seat::WaylandFocus, }, @@ -119,6 +123,7 @@ pub struct Surface { dirty: bool, render_timer_token: Option, fps: Fps, + feedback: Option, } pub type GbmDrmCompositor = DrmCompositor< @@ -889,6 +894,7 @@ impl Device { dirty: false, render_timer_token: None, fps: Fps::new(renderer.as_mut()), + feedback: None, }; self.surfaces.insert(crtc, data); @@ -909,10 +915,18 @@ fn render_node_for_output( .unwrap_or_else(|| workspace.windows().collect::>()) .into_iter() .flat_map(|w| { + // has the client requested surface-feedback? If yes it is properly rendering on our target_node + if with_states(&w.wl_surface()?, |data| { + SurfaceDmabufFeedbackState::from_states(data).is_some() + }) { + return Some(target_node.clone()); + } + // else lets check the global drm-node the client got either through default-feedback or wl_drm let client = dh.get_client(w.wl_surface()?.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(); } @@ -936,6 +950,51 @@ fn render_node_for_output( } } +fn get_surface_dmabuf_feedback( + render_node: DrmNode, + renderer: &mut GlMultiRenderer<'_, '_>, + compositor: &GbmDrmCompositor, +) -> Option { + let render_formats = renderer.dmabuf_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(&render_formats) + .copied() + .collect::>(); + + let builder = DmabufFeedbackBuilder::new(render_node.dev_id(), render_formats); + let render_feedback = builder.clone().build().unwrap(); + + let scanout_feedback = builder + .add_preference_tranche( + surface.device_fd().dev_id().unwrap(), + Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout), + planes_formats, + ) + .build() + .unwrap(); + + Some(SurfaceDmabufFeedback { + render_feedback, + scanout_feedback, + }) +} + impl Surface { pub fn render_output( &mut self, @@ -1088,7 +1147,7 @@ impl Surface { } else { None }; - state.send_frames(&self.output, &frame_result.states); + state.send_frames(&self.output, &frame_result.states, self.feedback.as_ref()); compositor .queue_frame(feedback) .with_context(|| "Failed to submit result for display")? @@ -1215,6 +1274,16 @@ impl KmsState { .unwrap_or_else(|_| String::from("Unknown")) ) })?; + surface.feedback = + self.api.single_renderer(&device.render_node).ok().and_then( + |mut renderer| { + get_surface_dmabuf_feedback( + device.render_node, + &mut renderer, + &target, + ) + }, + ); surface.surface = Some(target); true }; @@ -1287,8 +1356,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 49244ac2..ecd3787c 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 73b3c72e..a78d1c43 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..23e92375 100644 --- a/src/shell/layout/floating/grabs/moving.rs +++ b/src/shell/layout/floating/grabs/moving.rs @@ -6,12 +6,13 @@ use crate::{ element::{window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement}, focus::target::{KeyboardFocusTarget, PointerFocusTarget}, }, + state::SurfaceDmabufFeedback, utils::prelude::*, }; use smithay::{ backend::renderer::{ - element::{AsRenderElements, RenderElement}, + element::{AsRenderElements, RenderElement, RenderElementStates}, ImportAll, ImportMem, Renderer, }, desktop::space::SpaceElement, @@ -91,6 +92,21 @@ impl MoveGrabState { .active_window() .send_frame(output, time, throttle, primary_scan_out_output) } + + pub fn send_dmabuf_feedback( + &self, + output: &Output, + feedback: &SurfaceDmabufFeedback, + render_element_states: &RenderElementStates, + primary_scan_out_output: impl FnMut(&WlSurface, &SurfaceData) -> Option + Copy, + ) { + self.window.active_window().send_dmabuf_feedback( + output, + feedback, + render_element_states, + primary_scan_out_output, + ) + } } pub struct MoveSurfaceGrab { diff --git a/src/state.rs b/src/state.rs index b93c7afd..24184b35 100644 --- a/src/state.rs +++ b/src/state.rs @@ -20,15 +20,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,7 +48,7 @@ use smithay::{ wayland::{ compositor::CompositorState, data_device::DataDeviceState, - dmabuf::DmabufState, + dmabuf::{DmabufFeedback, DmabufState}, keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitState, output::OutputManagerState, presentation::PresentationState, @@ -130,6 +133,11 @@ pub enum BackendData { Unset, } +pub struct SurfaceDmabufFeedback { + pub render_feedback: DmabufFeedback, + pub scanout_feedback: DmabufFeedback, +} + impl BackendData { pub fn kms(&mut self) -> &mut KmsState { match self { @@ -363,7 +371,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, + dmabuf_feedback: Option<&SurfaceDmabufFeedback>, + ) { let time = self.clock.now(); let throttle = Some(Duration::from_secs(1)); @@ -401,6 +414,14 @@ impl Common { throttle, surface_primary_scanout_output, ); + if let Some(feedback) = dmabuf_feedback { + grab_state.send_dmabuf_feedback( + output, + feedback, + render_element_states, + surface_primary_scanout_output, + ); + } } } } @@ -432,6 +453,14 @@ impl Common { ); }); window.send_frame(output, time, throttle, surface_primary_scanout_output); + if let Some(feedback) = dmabuf_feedback { + window.send_dmabuf_feedback( + output, + feedback, + render_element_states, + surface_primary_scanout_output, + ); + } } }); @@ -466,7 +495,22 @@ impl Common { time, throttle, surface_primary_scanout_output, - ) + ); + if let Some(feedback) = dmabuf_feedback { + 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 +526,20 @@ impl Common { ); }); layer_surface.send_frame(output, time, throttle, surface_primary_scanout_output); + if let Some(feedback) = dmabuf_feedback { + 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 033b18b8..100540c2 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -25,7 +25,7 @@ use smithay::{ Bind, Blit, BufferType, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, }, }, - desktop::{layer_map_for_output, space::SpaceElement}, + desktop::{layer_map_for_output, space::SpaceElement, utils::surface_primary_scanout_output}, output::Output, reexports::wayland_server::{ protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat, wl_surface::WlSurface}, @@ -33,7 +33,8 @@ use smithay::{ }, utils::{IsAlive, Logical, Physical, Rectangle, Scale, Transform}, wayland::{ - dmabuf::get_dmabuf, + compositor::with_states, + dmabuf::{get_dmabuf, SurfaceDmabufFeedbackState}, seat::WaylandFocus, shm::{with_buffer_contents, with_buffer_contents_mut}, }, @@ -178,9 +179,21 @@ impl ScreencopyHandler for State { .get_client(surface.id()) .ok() .and_then(|client| { + // has the client requested surface-feedback? If yes it is properly rendering on the node of the display + if with_states(&surface, |data| { + SurfaceDmabufFeedbackState::from_states(data).is_some() + }) { + if let Some(output) = with_states(&surface, |data| { + surface_primary_scanout_output(&surface, data) + }) { + return kms.target_node_for_output(&output); + } + } + // else 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(); }