diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 3a478f86..ddc75fb0 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -33,7 +33,7 @@ use smithay::{ drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags}, input::Libinput, nix::{fcntl::OFlag, sys::stat::dev_t}, - wayland_server::protocol::wl_output, + wayland_server::protocol::{wl_output, wl_surface::WlSurface}, }, utils::signaling::{Linkable, SignalToken, Signaler}, wayland::output::{Mode as OutputMode, Output, PhysicalProperties}, @@ -658,6 +658,40 @@ impl Device { const MAX_CPU_COPIES: usize = 3; +fn render_node_for_output(output: &Output, target_node: DrmNode, shell: &Shell) -> DrmNode { + let workspace = shell.active_space(output); + let nodes = workspace + .get_fullscreen(output) + .map(|w| vec![w]) + .unwrap_or_else(|| workspace.space.windows().collect::>()) + .into_iter() + .flat_map(|w| { + w.toplevel() + .get_surface()? + .as_ref() + .client()? + .data_map() + .get::() + .cloned() + }) + .collect::>(); + if nodes.contains(&target_node) || nodes.len() < MAX_CPU_COPIES { + target_node + } else { + nodes + .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) + } +} + impl Surface { pub fn render_output( &mut self, @@ -673,39 +707,8 @@ impl Surface { self.surface.as_mut().unwrap().reset_buffers(); } - let workspace = state.shell.active_space(&self.output); - let nodes = workspace - .get_fullscreen(&self.output) - .map(|w| vec![w]) - .unwrap_or_else(|| workspace.space.windows().collect::>()) - .into_iter() - .flat_map(|w| { - w.toplevel() - .get_surface()? - .as_ref() - .client()? - .data_map() - .get::() - .cloned() - }) - .collect::>(); - let render_node = if nodes.contains(&target_node) || nodes.len() < MAX_CPU_COPIES { - &target_node - } else { - nodes - .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) - }; - - let mut renderer = api.renderer(render_node, &target_node).unwrap(); + let render_node = render_node_for_output(&self.output, *target_node, &state.shell); + let mut renderer = api.renderer(&render_node, &target_node).unwrap(); let surface = self.surface.as_mut().unwrap(); let (buffer, age) = surface @@ -841,6 +844,20 @@ impl KmsState { } Ok(()) } + pub fn target_node_for_output(&self, output: &Output) -> Option { + self.devices.iter().find(|(_, dev)| dev.surfaces.values().any(|s| s.output == *output)).map(|(target, _)| target).copied() + } + + pub fn try_early_import(&mut self, surface: &WlSurface, output: &Output, target: DrmNode, shell: &Shell) { + let render = render_node_for_output(&output, target, &shell); + if let Err(err) = self.api.early_import( + surface.as_ref().client().and_then(|c| c.data_map().get::().cloned()), + render, + surface, + ) { + slog_scope::debug!("Early import failed: {}", err); + } + } pub fn schedule_render( &mut self, diff --git a/src/shell/handler.rs b/src/shell/handler.rs index 6501a5ea..9931e696 100644 --- a/src/shell/handler.rs +++ b/src/shell/handler.rs @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{config::Config, input::active_output, state::State, utils::SurfaceDropNotifier}; +use crate::{ + config::Config, + input::active_output, + state::{BackendData, State}, + utils::SurfaceDropNotifier, +}; use smithay::{ backend::renderer::utils::on_commit_buffer_handler, desktop::{ @@ -309,14 +314,18 @@ fn check_grab_preconditions( } fn commit(surface: &WlSurface, state: &mut State) { - // TODO figure out which output the surface is on. - for output in state.common.shell.outputs() { - //.cloned().collect::>().into_iter() { + let mut import_nodes = std::collections::HashSet::new(); + for output in state.common.shell.outputs_for_surface(&surface) { + if let BackendData::Kms(ref mut kms_state) = &mut state.backend { + if let Some(target) = kms_state.target_node_for_output(&output) { + if import_nodes.insert(target) { + kms_state.try_early_import(surface, &output, target, &state.common.shell); + } + } + } state .backend - .schedule_render(&state.common.event_loop_handle, output); - // let space = state.common.spaces.active_space(output); - // get output for surface + .schedule_render(&state.common.event_loop_handle, &output); } let state = &mut state.common; diff --git a/src/shell/mod.rs b/src/shell/mod.rs index bc931c92..8217ee60 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -598,6 +598,15 @@ impl Shell { } } + pub fn outputs_for_surface(&self, surface: &WlSurface) -> impl Iterator { + self.space_for_surface(surface) + .and_then(|w| if let Some(window) = w.space.window_for_surface(surface, WindowSurfaceType::ALL) { + Some(w.space.outputs_for_window(&window).into_iter()) + } else { None }) + .into_iter() + .flatten() + } + pub fn space_for_surface(&self, surface: &WlSurface) -> Option<&Workspace> { self.spaces.iter().find(|workspace| { workspace