kms: Dmabuf Feedback support

This commit is contained in:
Victoria Brekenfeld 2023-03-09 17:39:47 +01:00
parent 486266f7bb
commit c8bb417d9b
8 changed files with 234 additions and 26 deletions

View file

@ -6,7 +6,7 @@ use crate::{
backend::render::{workspace_elements, CLEAR_COLOR}, backend::render::{workspace_elements, CLEAR_COLOR},
config::OutputConfig, config::OutputConfig,
shell::Shell, shell::Shell,
state::{BackendData, ClientState, Common, Data, Fps}, state::{BackendData, ClientState, Common, Data, Fps, SurfaceDmabufFeedback},
utils::prelude::*, utils::prelude::*,
wayland::{ wayland::{
handlers::screencopy::{render_session, UserdataExt}, handlers::screencopy::{render_session, UserdataExt},
@ -38,7 +38,7 @@ use smithay::{
glow::GlowRenderer, glow::GlowRenderer,
multigpu::{gbm::GbmGlesBackend, Error as MultiError, GpuManager}, multigpu::{gbm::GbmGlesBackend, Error as MultiError, GpuManager},
utils::draw_render_elements, utils::draw_render_elements,
Bind, Blit, Offscreen, Renderer, TextureFilter, Bind, Blit, ImportDma, Offscreen, Renderer, TextureFilter,
}, },
session::{libseat::LibSeatSession, Event as SessionEvent, Session}, session::{libseat::LibSeatSession, Event as SessionEvent, Session},
udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent}, udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent},
@ -58,12 +58,16 @@ use smithay::{
}, },
input::Libinput, input::Libinput,
nix::{fcntl::OFlag, sys::stat::dev_t}, 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}, wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource},
}, },
utils::{DeviceFd, Size, Transform}, utils::{DeviceFd, Size, Transform},
wayland::{ wayland::{
dmabuf::{get_dmabuf, DmabufGlobal}, compositor::with_states,
dmabuf::{get_dmabuf, DmabufFeedbackBuilder, DmabufGlobal, SurfaceDmabufFeedbackState},
relative_pointer::RelativePointerManagerState, relative_pointer::RelativePointerManagerState,
seat::WaylandFocus, seat::WaylandFocus,
}, },
@ -119,6 +123,7 @@ pub struct Surface {
dirty: bool, dirty: bool,
render_timer_token: Option<RegistrationToken>, render_timer_token: Option<RegistrationToken>,
fps: Fps, fps: Fps,
feedback: Option<SurfaceDmabufFeedback>,
} }
pub type GbmDrmCompositor = DrmCompositor< pub type GbmDrmCompositor = DrmCompositor<
@ -889,6 +894,7 @@ impl Device {
dirty: false, dirty: false,
render_timer_token: None, render_timer_token: None,
fps: Fps::new(renderer.as_mut()), fps: Fps::new(renderer.as_mut()),
feedback: None,
}; };
self.surfaces.insert(crtc, data); self.surfaces.insert(crtc, data);
@ -909,10 +915,18 @@ fn render_node_for_output(
.unwrap_or_else(|| workspace.windows().collect::<Vec<_>>()) .unwrap_or_else(|| workspace.windows().collect::<Vec<_>>())
.into_iter() .into_iter()
.flat_map(|w| { .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()?; let client = dh.get_client(w.wl_surface()?.id()).ok()?;
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.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>() { if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() {
return xwayland_client.user_data().get::<DrmNode>().cloned(); return xwayland_client.user_data().get::<DrmNode>().cloned();
} }
@ -936,6 +950,51 @@ fn render_node_for_output(
} }
} }
fn get_surface_dmabuf_feedback(
render_node: DrmNode,
renderer: &mut GlMultiRenderer<'_, '_>,
compositor: &GbmDrmCompositor,
) -> Option<SurfaceDmabufFeedback> {
let render_formats = renderer.dmabuf_formats().copied().collect::<HashSet<_>>();
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::<HashSet<_>>()
.intersection(&render_formats)
.copied()
.collect::<Vec<_>>();
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 { impl Surface {
pub fn render_output( pub fn render_output(
&mut self, &mut self,
@ -1088,7 +1147,7 @@ impl Surface {
} else { } else {
None None
}; };
state.send_frames(&self.output, &frame_result.states); state.send_frames(&self.output, &frame_result.states, self.feedback.as_ref());
compositor compositor
.queue_frame(feedback) .queue_frame(feedback)
.with_context(|| "Failed to submit result for display")? .with_context(|| "Failed to submit result for display")?
@ -1215,6 +1274,16 @@ impl KmsState {
.unwrap_or_else(|_| String::from("Unknown")) .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); surface.surface = Some(target);
true true
}; };
@ -1287,8 +1356,6 @@ impl KmsState {
} }
pub fn dmabuf_imported(&mut self, global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<()> { pub fn dmabuf_imported(&mut self, global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<()> {
use smithay::backend::renderer::ImportDma;
for device in self.devices.values() { for device in self.devices.values() {
if device if device
.socket .socket

View file

@ -10,7 +10,10 @@ use smithay::{
calloop::RegistrationToken, calloop::RegistrationToken,
wayland_server::{backend::GlobalId, Client, DisplayHandle}, wayland_server::{backend::GlobalId, Client, DisplayHandle},
}, },
wayland::{dmabuf::DmabufGlobal, socket::ListeningSocketSource}, wayland::{
dmabuf::{DmabufFeedbackBuilder, DmabufGlobal},
socket::ListeningSocketSource,
},
xwayland::XWaylandClientData, xwayland::XWaylandClientData,
}; };
use std::sync::Arc; use std::sync::Arc;
@ -59,10 +62,14 @@ impl State {
false 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 let dmabuf_global = self
.common .common
.dmabuf_state .dmabuf_state
.create_global_with_filter::<State, _>(dh, formats.clone(), filter); .create_global_with_filter_and_default_feedback::<State, _>(dh, &feedback, filter);
let drm_global_id = self let drm_global_id = self
.common .common

View file

@ -83,7 +83,7 @@ impl WinitState {
self.screencopy.clear(); self.screencopy.clear();
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
self.fps.displayed(); self.fps.displayed();
state.send_frames(&self.output, &states); state.send_frames(&self.output, &states, None);
if damage.is_some() { if damage.is_some() {
let mut output_presentation_feedback = let mut output_presentation_feedback =
state.take_presentation_feedback(&self.output, &states); state.take_presentation_feedback(&self.output, &states);

View file

@ -247,7 +247,7 @@ impl Surface {
.with_context(|| "Failed to submit buffer for display")?; .with_context(|| "Failed to submit buffer for display")?;
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
self.fps.displayed(); self.fps.displayed();
state.send_frames(&self.output, &states); state.send_frames(&self.output, &states, None);
if damage.is_some() { if damage.is_some() {
let mut output_presentation_feedback = let mut output_presentation_feedback =
state.take_presentation_feedback(&self.output, &states); state.take_presentation_feedback(&self.output, &states);

View file

@ -2,13 +2,17 @@ use std::time::Duration;
use smithay::{ use smithay::{
backend::renderer::{ backend::renderer::{
element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, element::{
surface::WaylandSurfaceRenderElement, utils::select_dmabuf_feedback, AsRenderElements,
RenderElementStates,
},
ImportAll, Renderer, ImportAll, Renderer,
}, },
desktop::{ desktop::{
utils::{ utils::{
send_frames_surface_tree, take_presentation_feedback_surface_tree, send_dmabuf_feedback_surface_tree, send_frames_surface_tree,
with_surfaces_surface_tree, OutputPresentationFeedback, take_presentation_feedback_surface_tree, with_surfaces_surface_tree,
OutputPresentationFeedback,
}, },
Window, Window,
}, },
@ -34,6 +38,8 @@ use smithay::{
xwayland::{xwm::X11Relatable, X11Surface}, xwayland::{xwm::X11Relatable, X11Surface},
}; };
use crate::state::SurfaceDmabufFeedback;
space_elements! { space_elements! {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub CosmicSurface; pub CosmicSurface;
@ -369,7 +375,7 @@ impl CosmicSurface {
{ {
match self { match self {
CosmicSurface::Wayland(window) => { 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) => { CosmicSurface::X11(surface) => {
if let Some(wl_surface) = surface.wl_surface() { if let Some(wl_surface) = surface.wl_surface() {
@ -386,6 +392,47 @@ impl CosmicSurface {
} }
} }
pub fn send_dmabuf_feedback<F1>(
&self,
output: &Output,
feedback: &SurfaceDmabufFeedback,
render_element_states: &RenderElementStates,
primary_scan_out_output: F1,
) where
F1: FnMut(&WlSurface, &SurfaceData) -> Option<Output> + 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<F1, F2>( pub fn take_presentation_feedback<F1, F2>(
&self, &self,
output_feedback: &mut OutputPresentationFeedback, output_feedback: &mut OutputPresentationFeedback,

View file

@ -6,12 +6,13 @@ use crate::{
element::{window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement}, element::{window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement},
focus::target::{KeyboardFocusTarget, PointerFocusTarget}, focus::target::{KeyboardFocusTarget, PointerFocusTarget},
}, },
state::SurfaceDmabufFeedback,
utils::prelude::*, utils::prelude::*,
}; };
use smithay::{ use smithay::{
backend::renderer::{ backend::renderer::{
element::{AsRenderElements, RenderElement}, element::{AsRenderElements, RenderElement, RenderElementStates},
ImportAll, ImportMem, Renderer, ImportAll, ImportMem, Renderer,
}, },
desktop::space::SpaceElement, desktop::space::SpaceElement,
@ -91,6 +92,21 @@ impl MoveGrabState {
.active_window() .active_window()
.send_frame(output, time, throttle, primary_scan_out_output) .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<Output> + Copy,
) {
self.window.active_window().send_dmabuf_feedback(
output,
feedback,
render_element_states,
primary_scan_out_output,
)
}
} }
pub struct MoveSurfaceGrab { pub struct MoveSurfaceGrab {

View file

@ -20,15 +20,18 @@ use smithay::{
backend::{ backend::{
drm::DrmNode, drm::DrmNode,
renderer::{ renderer::{
element::{default_primary_scanout_output_compare, RenderElementStates}, element::{
default_primary_scanout_output_compare, utils::select_dmabuf_feedback,
RenderElementStates,
},
glow::GlowRenderer, glow::GlowRenderer,
}, },
}, },
desktop::utils::{ desktop::utils::{
send_frames_surface_tree, surface_presentation_feedback_flags_from_states, send_dmabuf_feedback_surface_tree, send_frames_surface_tree,
surface_primary_scanout_output, take_presentation_feedback_surface_tree, surface_presentation_feedback_flags_from_states, surface_primary_scanout_output,
update_surface_primary_scanout_output, with_surfaces_surface_tree, take_presentation_feedback_surface_tree, update_surface_primary_scanout_output,
OutputPresentationFeedback, with_surfaces_surface_tree, OutputPresentationFeedback,
}, },
input::{pointer::CursorImageStatus, Seat, SeatState}, input::{pointer::CursorImageStatus, Seat, SeatState},
output::{Mode as OutputMode, Output, Scale}, output::{Mode as OutputMode, Output, Scale},
@ -45,7 +48,7 @@ use smithay::{
wayland::{ wayland::{
compositor::CompositorState, compositor::CompositorState,
data_device::DataDeviceState, data_device::DataDeviceState,
dmabuf::DmabufState, dmabuf::{DmabufFeedback, DmabufState},
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitState, keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitState,
output::OutputManagerState, output::OutputManagerState,
presentation::PresentationState, presentation::PresentationState,
@ -130,6 +133,11 @@ pub enum BackendData {
Unset, Unset,
} }
pub struct SurfaceDmabufFeedback {
pub render_feedback: DmabufFeedback,
pub scanout_feedback: DmabufFeedback,
}
impl BackendData { impl BackendData {
pub fn kms(&mut self) -> &mut KmsState { pub fn kms(&mut self) -> &mut KmsState {
match self { match self {
@ -363,7 +371,12 @@ impl Common {
self.last_active_seat.as_ref().expect("No seat?") 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 time = self.clock.now();
let throttle = Some(Duration::from_secs(1)); let throttle = Some(Duration::from_secs(1));
@ -401,6 +414,14 @@ impl Common {
throttle, throttle,
surface_primary_scanout_output, 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); 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, time,
throttle, throttle,
surface_primary_scanout_output, 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); 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,
)
},
);
}
} }
} }

View file

@ -25,7 +25,7 @@ use smithay::{
Bind, Blit, BufferType, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, 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, output::Output,
reexports::wayland_server::{ reexports::wayland_server::{
protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat, wl_surface::WlSurface}, 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}, utils::{IsAlive, Logical, Physical, Rectangle, Scale, Transform},
wayland::{ wayland::{
dmabuf::get_dmabuf, compositor::with_states,
dmabuf::{get_dmabuf, SurfaceDmabufFeedbackState},
seat::WaylandFocus, seat::WaylandFocus,
shm::{with_buffer_contents, with_buffer_contents_mut}, shm::{with_buffer_contents, with_buffer_contents_mut},
}, },
@ -178,9 +179,21 @@ impl ScreencopyHandler for State {
.get_client(surface.id()) .get_client(surface.id())
.ok() .ok()
.and_then(|client| { .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::<ClientState>() { if let Some(normal_client) = client.get_data::<ClientState>() {
return normal_client.drm_node.clone(); 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::<XWaylandClientData>() { if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() {
return xwayland_client.user_data().get::<DrmNode>().cloned(); return xwayland_client.user_data().get::<DrmNode>().cloned();
} }