Skip to content

Commit

Permalink
kms: Dmabuf feedback support
Browse files Browse the repository at this point in the history
  • Loading branch information
Drakulix committed Apr 5, 2023
1 parent 636d389 commit bc49507
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 36 deletions.
126 changes: 107 additions & 19 deletions src/backend/kms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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},
Expand All @@ -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},
Expand All @@ -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,
},
Expand Down Expand Up @@ -119,6 +122,7 @@ pub struct Surface {
dirty: bool,
render_timer_token: Option<RegistrationToken>,
fps: Fps,
feedback: HashMap<DrmNode, SurfaceDmabufFeedback>,
}

pub type GbmDrmCompositor = DrmCompositor<
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -889,13 +893,27 @@ impl Device {
dirty: false,
render_timer_token: None,
fps: Fps::new(renderer.as_mut()),
feedback: HashMap::new(),
};
self.surfaces.insert(crtc, data);

Ok(output)
}
}

pub fn source_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.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
}

fn render_node_for_output(
dh: &DisplayHandle,
output: &Output,
Expand All @@ -908,16 +926,7 @@ fn render_node_for_output(
.map(|w| vec![w.clone()])
.unwrap_or_else(|| workspace.windows().collect::<Vec<_>>())
.into_iter()
.flat_map(|w| {
let client = dh.get_client(w.wl_surface()?.id()).ok()?;
if let Some(normal_client) = client.get_data::<ClientState>() {
return normal_client.drm_node.clone();
}
if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() {
return xwayland_client.user_data().get::<DrmNode>().cloned();
}
None
})
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s, dh)))
.collect::<Vec<_>>();
if nodes.contains(&target_node) || nodes.is_empty() {
target_node
Expand All @@ -936,6 +945,59 @@ fn render_node_for_output(
}
}

fn get_surface_dmabuf_feedback(
render_node: DrmNode,
render_formats: HashSet<Format>,
target_formats: HashSet<Format>,
compositor: &GbmDrmCompositor,
) -> SurfaceDmabufFeedback {
let combined_formats = render_formats
.intersection(&target_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(&combined_formats)
.copied()
.collect::<Vec<_>>();

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,
Expand Down Expand Up @@ -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::<HashSet<_>>();
let target_formats = api
.single_renderer(target_node)
.unwrap()
.dmabuf_formats()
.copied()
.collect::<HashSet<_>>();
get_surface_dmabuf_feedback(
source_node,
render_formats,
target_formats,
compositor,
)
})
.clone(),
)
});
compositor
.queue_frame(feedback)
.with_context(|| "Failed to submit result for display")?
Expand Down Expand Up @@ -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()),
Expand Down Expand Up @@ -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
Expand Down
11 changes: 9 additions & 2 deletions src/backend/kms/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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::<State, _>(dh, formats.clone(), filter);
.create_global_with_filter_and_default_feedback::<State, _>(dh, &feedback, filter);

let drm_global_id = self
.common
Expand Down
2 changes: 1 addition & 1 deletion src/backend/winit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/backend/x11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
55 changes: 51 additions & 4 deletions src/shell/element/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand All @@ -34,6 +38,8 @@ use smithay::{
xwayland::{xwm::X11Relatable, X11Surface},
};

use crate::state::SurfaceDmabufFeedback;

space_elements! {
#[derive(Debug, Clone, PartialEq)]
pub CosmicSurface;
Expand Down Expand Up @@ -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() {
Expand All @@ -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>(
&self,
output_feedback: &mut OutputPresentationFeedback,
Expand Down
5 changes: 5 additions & 0 deletions src/shell/layout/floating/grabs/moving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
shell::{
element::{window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement},
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
CosmicSurface,
},
utils::prelude::*,
};
Expand Down Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit bc49507

Please sign in to comment.