From 78b8dc6fce308a6835002c0045091e9f8d266bf8 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 5 Dec 2024 14:12:34 +0100 Subject: [PATCH] kms: Disable direct-scanout when lease is active --- src/backend/kms/device.rs | 63 ++++++++++++++++++++++++++++++- src/backend/kms/surface/mod.rs | 30 ++++++++++++++- src/wayland/handlers/drm_lease.rs | 45 ++++++++++++++++++++-- 3 files changed, 130 insertions(+), 8 deletions(-) diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index 936bb960..e9204b0d 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ + backend::render::{output_elements, CursorMode, GlMultiRenderer, CLEAR_COLOR}, config::{AdaptiveSync, OutputConfig, OutputState}, shell::Shell, utils::prelude::*, @@ -15,7 +16,10 @@ use smithay::{ gbm::{GbmAllocator, GbmDevice}, Fourcc, }, - drm::{output::DrmOutputManager, DrmDevice, DrmDeviceFd, DrmEvent, DrmNode}, + drm::{ + compositor::FrameMode, output::DrmOutputManager, DrmDevice, DrmDeviceFd, DrmEvent, + DrmNode, + }, egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay}, session::Session, }, @@ -28,7 +32,9 @@ use smithay::{ rustix::fs::OFlags, wayland_server::{protocol::wl_buffer::WlBuffer, DisplayHandle, Weak}, }, - utils::{Buffer as BufferCoords, DevPath, DeviceFd, Point, Rectangle, Transform}, + utils::{ + Buffer as BufferCoords, Clock, DevPath, DeviceFd, Monotonic, Point, Rectangle, Transform, + }, wayland::drm_lease::{DrmLease, DrmLeaseState}, }; use tracing::{error, info, warn}; @@ -606,6 +612,59 @@ impl Device { } } + pub fn allow_direct_scanout( + &mut self, + flag: bool, + renderer: &mut GlMultiRenderer, + clock: &Clock, + shell: &Arc>, + ) -> Result<()> { + for surface in self.surfaces.values_mut() { + surface.allow_direct_scanout(flag); + } + + if !flag { + let now = clock.now(); + + let output_map = self + .surfaces + .iter() + .map(|(crtc, surface)| (*crtc, surface.output.clone())) + .collect::>(); + + self.drm.with_compositors::>(|map| { + for (crtc, compositor) in map.iter() { + let elements = match output_map.get(crtc) { + Some(output) => output_elements( + Some(&self.render_node), + renderer, + shell, + now, + &output, + CursorMode::All, + None, + ) + .with_context(|| "Failed to render outputs")?, + None => Vec::new(), + }; + + let mut compositor = compositor.lock().unwrap(); + compositor.render_frame( + renderer, + &elements, + CLEAR_COLOR, + FrameMode::COMPOSITE, + )?; + compositor.commit_frame()?; + } + + Ok(()) + })?; + } + + Ok(()) + } + pub fn in_use(&self, primary: Option<&DrmNode>) -> bool { Some(&self.render_node) == primary || !self.surfaces.is_empty() diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index 840d279b..af48ee37 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -125,6 +125,7 @@ pub struct SurfaceThreadState { target_node: DrmNode, active: Arc, vrr_mode: AdaptiveSync, + direct_scanout_allowed: bool, compositor: Option, state: QueueState, @@ -238,6 +239,7 @@ pub enum ThreadCommand { ScheduleRender, AdaptiveSyncAvailable(SyncSender>), UseAdaptiveSync(AdaptiveSync), + AllowDirectScanout(bool, SyncSender<()>), End, DpmsOff, } @@ -421,6 +423,14 @@ impl Surface { Ok(true) } + pub fn allow_direct_scanout(&mut self, flag: bool) { + let (tx, rx) = std::sync::mpsc::sync_channel(1); + let _ = self + .thread_command + .send(ThreadCommand::AllowDirectScanout(flag, tx)); + let _ = rx.recv(); + } + pub fn suspend(&mut self) { let (tx, rx) = std::sync::mpsc::sync_channel(1); let _ = self.thread_command.send(ThreadCommand::Suspend(tx)); @@ -517,6 +527,8 @@ fn surface_thread( target_node, active, compositor: None, + direct_scanout_allowed: !crate::utils::env::bool_var("COSMIC_DISABLE_DIRECT_SCANOUT") + .unwrap_or(false), vrr_mode: AdaptiveSync::Disabled, state: QueueState::Idle, @@ -604,6 +616,12 @@ fn surface_thread( }; } } + Event::Msg(ThreadCommand::AllowDirectScanout(flag, tx)) => { + state.direct_scanout_allowed = flag + && !crate::utils::env::bool_var("COSMIC_DISABLE_DIRECT_SCANOUT") + .unwrap_or(false); + let _ = tx.send(()); + } Event::Closed | Event::Msg(ThreadCommand::End) => { signal.stop(); signal.wakeup(); @@ -1074,7 +1092,11 @@ impl SurfaceThreadState { &mut renderer, &elements, [0.0, 0.0, 0.0, 1.0], - FrameMode::ALL, + if self.direct_scanout_allowed { + FrameMode::ALL + } else { + FrameMode::COMPOSITE + }, ) } else { if let Err(err) = compositor.with_compositor(|c| c.use_vrr(vrr)) { @@ -1084,7 +1106,11 @@ impl SurfaceThreadState { &mut renderer, &elements, CLEAR_COLOR, // TODO use a theme neutral color - FrameMode::ALL, + if self.direct_scanout_allowed { + FrameMode::ALL + } else { + FrameMode::COMPOSITE + }, ) }; self.timings.draw_done(&self.clock); diff --git a/src/wayland/handlers/drm_lease.rs b/src/wayland/handlers/drm_lease.rs index 55528cce..02bd1a14 100644 --- a/src/wayland/handlers/drm_lease.rs +++ b/src/wayland/handlers/drm_lease.rs @@ -26,12 +26,30 @@ impl DrmLeaseHandler for State { node: DrmNode, request: DrmLeaseRequest, ) -> Result { - let backend = self - .backend - .kms() + let kms = self.backend.kms(); + let backend = kms .drm_devices .get_mut(&node) .ok_or(LeaseRejected::default())?; + let mut renderer = match kms.api.single_renderer(&backend.render_node) { + Ok(renderer) => renderer, + Err(err) => { + tracing::warn!( + ?err, + "Failed to create renderer to disable direct scanout, denying lease" + ); + return Err(LeaseRejected::default()); + } + }; + if let Err(err) = backend.allow_direct_scanout( + false, + &mut renderer, + &self.common.clock, + &self.common.shell, + ) { + tracing::warn!(?err, "Failed to disable direct scanout"); + return Err(LeaseRejected::default()); + } let mut builder = DrmLeaseBuilder::new(backend.drm.device()); for conn in request.connectors { @@ -88,8 +106,27 @@ impl DrmLeaseHandler for State { } fn lease_destroyed(&mut self, node: DrmNode, lease: u32) { - if let Some(backend) = self.backend.kms().drm_devices.get_mut(&node) { + let kms = self.backend.kms(); + if let Some(backend) = kms.drm_devices.get_mut(&node) { backend.active_leases.retain(|l| l.id() != lease); + + if backend.active_leases.is_empty() { + let mut renderer = match kms.api.single_renderer(&backend.render_node) { + Ok(renderer) => renderer, + Err(err) => { + tracing::warn!(?err, "Failed to create renderer to enable direct scanout."); + return; + } + }; + if let Err(err) = backend.allow_direct_scanout( + true, + &mut renderer, + &self.common.clock, + &self.common.shell, + ) { + tracing::warn!(?err, "Failed to enable direct scanout"); + } + } } } }