Skip to content

Commit

Permalink
kms: Disable direct-scanout when lease is active
Browse files Browse the repository at this point in the history
  • Loading branch information
Drakulix committed Dec 13, 2024
1 parent 193d1da commit 78b8dc6
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 8 deletions.
63 changes: 61 additions & 2 deletions src/backend/kms/device.rs
Original file line number Diff line number Diff line change
@@ -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::*,
Expand All @@ -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,
},
Expand All @@ -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};
Expand Down Expand Up @@ -606,6 +612,59 @@ impl Device {
}
}

pub fn allow_direct_scanout(
&mut self,
flag: bool,
renderer: &mut GlMultiRenderer,
clock: &Clock<Monotonic>,
shell: &Arc<RwLock<Shell>>,
) -> 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::<HashMap<_, _>>();

self.drm.with_compositors::<Result<()>>(|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()
Expand Down
30 changes: 28 additions & 2 deletions src/backend/kms/surface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ pub struct SurfaceThreadState {
target_node: DrmNode,
active: Arc<AtomicBool>,
vrr_mode: AdaptiveSync,
direct_scanout_allowed: bool,
compositor: Option<GbmDrmOutput>,

state: QueueState,
Expand Down Expand Up @@ -238,6 +239,7 @@ pub enum ThreadCommand {
ScheduleRender,
AdaptiveSyncAvailable(SyncSender<Result<VrrSupport>>),
UseAdaptiveSync(AdaptiveSync),
AllowDirectScanout(bool, SyncSender<()>),
End,
DpmsOff,
}
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
Expand Down
45 changes: 41 additions & 4 deletions src/wayland/handlers/drm_lease.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,30 @@ impl DrmLeaseHandler for State {
node: DrmNode,
request: DrmLeaseRequest,
) -> Result<DrmLeaseBuilder, LeaseRejected> {
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 {
Expand Down Expand Up @@ -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");
}
}
}
}
}
Expand Down

0 comments on commit 78b8dc6

Please sign in to comment.