kms: Use new DrmOutput api

This commit is contained in:
Victoria Brekenfeld 2025-01-02 17:38:59 +01:00 committed by Victoria Brekenfeld
parent e356e3c589
commit 8b87d6524e
10 changed files with 347 additions and 217 deletions

View file

@ -1,28 +1,40 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::{ use crate::{
backend::render::{output_elements, CursorMode, GlMultiRenderer, CLEAR_COLOR},
config::{AdaptiveSync, OutputConfig, OutputState}, config::{AdaptiveSync, OutputConfig, OutputState},
shell::Shell, shell::Shell,
utils::prelude::*, utils::prelude::*,
wayland::protocols::screencopy::Frame as ScreencopyFrame,
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use libc::dev_t; use libc::dev_t;
use smithay::{ use smithay::{
backend::{ backend::{
allocator::gbm::GbmDevice, allocator::{
drm::{DrmDevice, DrmDeviceFd, DrmEvent, DrmNode}, gbm::{GbmAllocator, GbmDevice},
Fourcc,
},
drm::{
compositor::FrameFlags, output::DrmOutputManager, DrmDevice, DrmDeviceFd, DrmEvent,
DrmNode,
},
egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay}, egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay},
session::Session, session::Session,
}, },
desktop::utils::OutputPresentationFeedback,
output::{Mode as OutputMode, Output, PhysicalProperties, Scale, Subpixel}, output::{Mode as OutputMode, Output, PhysicalProperties, Scale, Subpixel},
reexports::{ reexports::{
calloop::{LoopHandle, RegistrationToken}, calloop::{LoopHandle, RegistrationToken},
drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags}, drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags},
gbm::BufferObjectFlags as GbmBufferFlags,
rustix::fs::OFlags, rustix::fs::OFlags,
wayland_server::{protocol::wl_buffer::WlBuffer, DisplayHandle, Weak}, wayland_server::{protocol::wl_buffer::WlBuffer, DisplayHandle, Weak},
}, },
utils::{DevPath, DeviceFd, Point, Transform}, utils::{
Buffer as BufferCoords, Clock, DevPath, DeviceFd, Monotonic, Point, Rectangle, Transform,
},
wayland::drm_lease::{DrmLease, DrmLeaseState}, wayland::drm_lease::{DrmLease, DrmLeaseState},
}; };
use tracing::{error, info, warn}; use tracing::{error, info, warn};
@ -32,7 +44,7 @@ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
fmt, fmt,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{atomic::AtomicBool, Arc, RwLock}, sync::{atomic::AtomicBool, mpsc::Receiver, Arc, RwLock},
}; };
use super::{drm_helpers, socket::Socket, surface::Surface}; use super::{drm_helpers, socket::Socket, surface::Surface};
@ -44,6 +56,16 @@ pub struct EGLInternals {
pub context: EGLContext, pub context: EGLContext,
} }
pub type GbmDrmOutputManager = DrmOutputManager<
GbmAllocator<DrmDeviceFd>,
GbmDevice<DrmDeviceFd>,
Option<(
OutputPresentationFeedback,
Receiver<(ScreencopyFrame, Vec<Rectangle<i32, BufferCoords>>)>,
)>,
DrmDeviceFd,
>;
pub struct Device { pub struct Device {
pub dev_node: DrmNode, pub dev_node: DrmNode,
pub render_node: DrmNode, pub render_node: DrmNode,
@ -51,8 +73,8 @@ pub struct Device {
pub outputs: HashMap<connector::Handle, Output>, pub outputs: HashMap<connector::Handle, Output>,
pub surfaces: HashMap<crtc::Handle, Surface>, pub surfaces: HashMap<crtc::Handle, Surface>,
pub drm: GbmDrmOutputManager,
pub gbm: GbmDevice<DrmDeviceFd>, pub gbm: GbmDevice<DrmDeviceFd>,
pub drm: DrmDevice,
supports_atomic: bool, supports_atomic: bool,
@ -72,8 +94,7 @@ impl fmt::Debug for Device {
.field("render_node", &self.render_node) .field("render_node", &self.render_node)
.field("outputs", &self.outputs) .field("outputs", &self.outputs)
.field("surfaces", &self.surfaces) .field("surfaces", &self.surfaces)
.field("drm", &self.drm) .field("drm", &"..")
.field("gbm", &self.gbm)
.field("egl", &self.egl) .field("egl", &self.egl)
.field("supports_atomic", &self.supports_atomic) .field("supports_atomic", &self.supports_atomic)
.field("leased_connectors", &self.leased_connectors) .field("leased_connectors", &self.leased_connectors)
@ -190,6 +211,23 @@ impl State {
} }
}; };
let drm = GbmDrmOutputManager::new(
drm,
GbmAllocator::new(
gbm.clone(),
GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT,
),
gbm.clone(),
Some(gbm.clone()),
[
Fourcc::Abgr2101010,
Fourcc::Argb2101010,
Fourcc::Abgr8888,
Fourcc::Argb8888,
],
render_formats,
);
let mut device = Device { let mut device = Device {
dev_node: drm_node, dev_node: drm_node,
render_node, render_node,
@ -197,8 +235,8 @@ impl State {
outputs: HashMap::new(), outputs: HashMap::new(),
surfaces: HashMap::new(), surfaces: HashMap::new(),
gbm: gbm.clone(),
drm, drm,
gbm,
supports_atomic, supports_atomic,
@ -264,6 +302,7 @@ impl State {
&mut self.common.workspace_state.update(), &mut self.common.workspace_state.update(),
&self.common.xdg_activation_state, &self.common.xdg_activation_state,
self.common.startup_done.clone(), self.common.startup_done.clone(),
&self.common.clock,
); );
self.backend.kms().refresh_used_devices()?; self.backend.kms().refresh_used_devices()?;
@ -364,6 +403,7 @@ impl State {
&mut self.common.workspace_state.update(), &mut self.common.workspace_state.update(),
&self.common.xdg_activation_state, &self.common.xdg_activation_state,
self.common.startup_done.clone(), self.common.startup_done.clone(),
&self.common.clock,
); );
// Don't remove the outputs, before potentially new ones have been initialized. // Don't remove the outputs, before potentially new ones have been initialized.
// reading a new config means outputs might become enabled, that were previously disabled. // reading a new config means outputs might become enabled, that were previously disabled.
@ -417,6 +457,7 @@ impl State {
&mut self.common.workspace_state.update(), &mut self.common.workspace_state.update(),
&self.common.xdg_activation_state, &self.common.xdg_activation_state,
self.common.startup_done.clone(), self.common.startup_done.clone(),
&self.common.clock,
); );
self.common.refresh(); self.common.refresh();
} else { } else {
@ -435,7 +476,8 @@ pub struct OutputChanges {
impl Device { impl Device {
pub fn enumerate_surfaces(&mut self) -> Result<OutputChanges> { pub fn enumerate_surfaces(&mut self) -> Result<OutputChanges> {
// enumerate our outputs // enumerate our outputs
let config = drm_helpers::display_configuration(&mut self.drm, self.supports_atomic)?; let config =
drm_helpers::display_configuration(self.drm.device_mut(), self.supports_atomic)?;
let surfaces = self let surfaces = self
.surfaces .surfaces
@ -486,19 +528,20 @@ impl Device {
.get(&conn) .get(&conn)
.cloned() .cloned()
.map(|output| Ok(output)) .map(|output| Ok(output))
.unwrap_or_else(|| create_output_for_conn(&mut self.drm, conn)) .unwrap_or_else(|| create_output_for_conn(self.drm.device_mut(), conn))
.context("Failed to create `Output`")?; .context("Failed to create `Output`")?;
let non_desktop = match drm_helpers::get_property_val(&self.drm, conn, "non-desktop") { let non_desktop =
Ok((val_type, value)) => val_type.convert_value(value).as_boolean().unwrap(), match drm_helpers::get_property_val(self.drm.device_mut(), conn, "non-desktop") {
Err(err) => { Ok((val_type, value)) => val_type.convert_value(value).as_boolean().unwrap(),
warn!( Err(err) => {
?err, warn!(
"Failed to determine if connector is meant desktop usage, assuming so." ?err,
); "Failed to determine if connector is meant desktop usage, assuming so."
false );
} false
}; }
};
if non_desktop { if non_desktop {
if let Some(crtc) = maybe_crtc { if let Some(crtc) = maybe_crtc {
@ -528,7 +571,7 @@ impl Device {
.user_data() .user_data()
.insert_if_missing(|| RefCell::new(OutputConfig::default())); .insert_if_missing(|| RefCell::new(OutputConfig::default()));
populate_modes(&mut self.drm, &output, conn, position) populate_modes(self.drm.device_mut(), &output, conn, position)
.with_context(|| "Failed to enumerate connector modes")?; .with_context(|| "Failed to enumerate connector modes")?;
let has_surface = if let Some(crtc) = maybe_crtc { let has_surface = if let Some(crtc) = maybe_crtc {

View file

@ -16,7 +16,7 @@ use smithay::{
dmabuf::Dmabuf, dmabuf::Dmabuf,
gbm::{GbmAllocator, GbmBufferFlags}, gbm::{GbmAllocator, GbmBufferFlags},
}, },
drm::{DrmDeviceFd, DrmNode, NodeType}, drm::{output::DrmOutputRenderElements, DrmDeviceFd, DrmNode, NodeType},
egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay}, egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay},
input::InputEvent, input::InputEvent,
libinput::{LibinputInputBackend, LibinputSessionInterface}, libinput::{LibinputInputBackend, LibinputSessionInterface},
@ -27,13 +27,17 @@ use smithay::{
output::Output, output::Output,
reexports::{ reexports::{
calloop::{Dispatcher, EventLoop, LoopHandle}, calloop::{Dispatcher, EventLoop, LoopHandle},
drm::control::{crtc, Device as _}, drm::{
control::{crtc, Device as _},
Device as _,
},
input::{self, Libinput}, input::{self, Libinput},
wayland_server::{Client, DisplayHandle}, wayland_server::{Client, DisplayHandle},
}, },
utils::{DevPath, Size}, utils::{Clock, DevPath, Monotonic, Size},
wayland::{dmabuf::DmabufGlobal, relative_pointer::RelativePointerManagerState}, wayland::{dmabuf::DmabufGlobal, relative_pointer::RelativePointerManagerState},
}; };
use surface::GbmDrmOutput;
use tracing::{error, info, trace, warn}; use tracing::{error, info, trace, warn};
use std::{ use std::{
@ -53,7 +57,7 @@ pub(crate) use surface::Surface;
use device::*; use device::*;
pub use surface::Timings; pub use surface::Timings;
use super::render::init_shaders; use super::render::{init_shaders, output_elements, CursorMode, CLEAR_COLOR};
#[derive(Debug)] #[derive(Debug)]
pub struct KmsState { pub struct KmsState {
@ -321,6 +325,7 @@ impl State {
&mut state.common.workspace_state.update(), &mut state.common.workspace_state.update(),
&state.common.xdg_activation_state, &state.common.xdg_activation_state,
state.common.startup_done.clone(), state.common.startup_done.clone(),
&state.common.clock,
); );
state.common.refresh(); state.common.refresh();
}); });
@ -528,6 +533,7 @@ impl KmsState {
loop_handle: &LoopHandle<'static, State>, loop_handle: &LoopHandle<'static, State>,
shell: Arc<RwLock<Shell>>, shell: Arc<RwLock<Shell>>,
startup_done: Arc<AtomicBool>, startup_done: Arc<AtomicBool>,
clock: &Clock<Monotonic>,
) -> Result<Vec<Output>, anyhow::Error> { ) -> Result<Vec<Output>, anyhow::Error> {
if !self.session.is_active() { if !self.session.is_active() {
return Ok(Vec::new()); return Ok(Vec::new());
@ -554,7 +560,7 @@ impl KmsState {
// TODO: Right now we always keep crtcs of already enabled outputs, // TODO: Right now we always keep crtcs of already enabled outputs,
// even if another configuration could potentially enable more outputs // even if another configuration could potentially enable more outputs
let res_handles = device.drm.resource_handles()?; let res_handles = device.drm.device().resource_handles()?;
let free_crtcs = res_handles let free_crtcs = res_handles
.crtcs() .crtcs()
.iter() .iter()
@ -579,16 +585,20 @@ impl KmsState {
}); });
for conn in open_conns { for conn in open_conns {
let conn_info = device.drm.get_connector(conn, false).with_context(|| { let conn_info = device
format!( .drm
"Failed to query drm device: {:?}", .device()
device.drm.dev_path().as_deref().map(Path::display), .get_connector(conn, false)
) .with_context(|| {
})?; format!(
"Failed to query drm device: {:?}",
device.drm.device().dev_path().as_deref().map(Path::display),
)
})?;
'outer: for encoder_info in conn_info 'outer: for encoder_info in conn_info
.encoders() .encoders()
.iter() .iter()
.flat_map(|encoder_handle| device.drm.get_encoder(*encoder_handle)) .flat_map(|encoder_handle| device.drm.device().get_encoder(*encoder_handle))
{ {
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) { for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
if free_crtcs.contains(&crtc) { if free_crtcs.contains(&crtc) {
@ -638,13 +648,20 @@ impl KmsState {
let mut all_outputs = Vec::new(); let mut all_outputs = Vec::new();
for device in self.drm_devices.values_mut() { for device in self.drm_devices.values_mut() {
// reconfigure // reconfigure existing
let now = clock.now();
let output_map = device
.surfaces
.iter()
.map(|(crtc, surface)| (*crtc, surface.output.clone()))
.collect::<HashMap<_, _>>();
for (crtc, surface) in device.surfaces.iter_mut() { for (crtc, surface) in device.surfaces.iter_mut() {
let output_config = surface.output.config(); let output_config = surface.output.config();
let drm = &mut device.drm; let drm = &mut device.drm;
let conn = surface.connector; let conn = surface.connector;
let conn_info = drm.get_connector(conn, false)?; let conn_info = drm.device().get_connector(conn, false)?;
let mode = conn_info let mode = conn_info
.modes() .modes()
.iter() .iter()
@ -662,14 +679,64 @@ impl KmsState {
if !test_only { if !test_only {
if !surface.is_active() { if !surface.is_active() {
let drm_surface = drm let compositor: GbmDrmOutput = {
.create_surface(*crtc, *mode, &[conn]) let mut planes = drm
.with_context(|| "Failed to create drm surface")?; .device()
let gbm = device.gbm.clone(); .planes(crtc)
let cursor_size = drm.cursor_size(); .with_context(|| "Failed to enumerate planes")?;
let driver = drm.device().get_driver().ok();
// QUIRK: Using an overlay plane on a nvidia card breaks the display controller (wtf...)
if driver.is_some_and(|driver| {
driver
.name()
.to_string_lossy()
.to_lowercase()
.contains("nvidia")
}) {
planes.overlay = vec![];
}
let mut renderer = self
.api
.single_renderer(&device.render_node)
.with_context(|| "Failed to create renderer")?;
let mut elements = DrmOutputRenderElements::default();
for (crtc, output) in output_map.iter() {
let output_elements = output_elements(
Some(&device.render_node),
&mut renderer,
&shell,
now,
&output,
CursorMode::All,
None,
)
.with_context(|| "Failed to render outputs")?;
elements.add_output(crtc, CLEAR_COLOR, output_elements);
}
let compositor = drm
.initialize_output(
*crtc,
*mode,
&[conn],
&surface.output,
Some(planes),
&mut renderer,
&elements,
)
.with_context(|| "Failed to create drm surface")?;
let _ = renderer;
compositor
};
if let Some(bpc) = output_config.max_bpc { if let Some(bpc) = output_config.max_bpc {
if let Err(err) = drm_helpers::set_max_bpc(drm, conn, bpc) { if let Err(err) = drm_helpers::set_max_bpc(drm.device(), conn, bpc) {
warn!( warn!(
?bpc, ?bpc,
?err, ?err,
@ -682,7 +749,7 @@ impl KmsState {
let vrr = output_config.vrr; let vrr = output_config.vrr;
std::mem::drop(output_config); std::mem::drop(output_config);
match surface.resume(drm_surface, gbm, cursor_size) { match surface.resume(compositor) {
Ok(_) => { Ok(_) => {
surface.output.set_adaptive_sync_support( surface.output.set_adaptive_sync_support(
surface.adaptive_sync_support().ok(), surface.adaptive_sync_support().ok(),
@ -711,8 +778,29 @@ impl KmsState {
surface.output.set_adaptive_sync(AdaptiveSync::Disabled); surface.output.set_adaptive_sync(AdaptiveSync::Disabled);
} }
} }
surface
.set_mode(*mode) let mut renderer = self
.api
.single_renderer(&device.render_node)
.with_context(|| "Failed to create renderer")?;
let mut elements = DrmOutputRenderElements::default();
for (crtc, output) in output_map.iter() {
let output_elements = output_elements(
Some(&device.render_node),
&mut renderer,
&shell,
now,
&output,
CursorMode::All,
None,
)
.with_context(|| "Failed to render outputs")?;
elements.add_output(crtc, CLEAR_COLOR, output_elements);
}
drm.use_mode(&surface.crtc, *mode, &mut renderer, &elements)
.context("Failed to apply new mode")?; .context("Failed to apply new mode")?;
} }
} }
@ -731,6 +819,36 @@ impl KmsState {
.map(|(_, output)| output.clone()) .map(|(_, output)| output.clone())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
{
let mut renderer = self
.api
.single_renderer(&device.render_node)
.with_context(|| "Failed to create renderer")?;
let mut elements = DrmOutputRenderElements::default();
for (crtc, output) in output_map.iter() {
let output_elements = output_elements(
Some(&device.render_node),
&mut renderer,
&shell,
now,
&output,
CursorMode::All,
None,
)
.with_context(|| "Failed to render outputs")?;
elements.add_output(crtc, CLEAR_COLOR, output_elements);
}
if let Err(err) = device
.drm
.try_to_restore_modifiers(&mut renderer, &elements)
{
warn!(?err, "Failed to restore modifiers");
}
}
} }
// we need to handle mirroring, after all outputs have been enabled // we need to handle mirroring, after all outputs have been enabled

View file

@ -3,12 +3,12 @@
use crate::{ use crate::{
backend::render::{ backend::render::{
element::{CosmicElement, DamageElement}, element::{CosmicElement, DamageElement},
init_shaders, workspace_elements, CursorMode, ElementFilter, GlMultiRenderer, CLEAR_COLOR, init_shaders, output_elements, CursorMode, GlMultiRenderer, CLEAR_COLOR,
}, },
config::AdaptiveSync, config::AdaptiveSync,
shell::Shell, shell::Shell,
state::SurfaceDmabufFeedback, state::SurfaceDmabufFeedback,
utils::{prelude::*, quirks::workspace_overview_is_open}, utils::prelude::*,
wayland::{ wayland::{
handlers::screencopy::{submit_buffer, FrameHolder, SessionData}, handlers::screencopy::{submit_buffer, FrameHolder, SessionData},
protocols::screencopy::{ protocols::screencopy::{
@ -23,14 +23,13 @@ use smithay::{
backend::{ backend::{
allocator::{ allocator::{
format::FormatSet, format::FormatSet,
gbm::{GbmAllocator, GbmBufferFlags, GbmDevice}, gbm::{GbmAllocator, GbmDevice},
Fourcc, Fourcc,
}, },
drm::{ drm::{
compositor::{ compositor::{BlitFrameResultError, FrameError, FrameFlags, PrimaryPlaneElement},
BlitFrameResultError, DrmCompositor, FrameError, FrameFlags, PrimaryPlaneElement, output::DrmOutput,
}, DrmDeviceFd, DrmEventMetadata, DrmEventTime, DrmNode, VrrSupport,
DrmDeviceFd, DrmEventMetadata, DrmEventTime, DrmNode, DrmSurface, VrrSupport,
}, },
egl::EGLContext, egl::EGLContext,
renderer::{ renderer::{
@ -57,17 +56,14 @@ use smithay::{
timer::{TimeoutAction, Timer}, timer::{TimeoutAction, Timer},
EventLoop, LoopHandle, RegistrationToken, EventLoop, LoopHandle, RegistrationToken,
}, },
drm::{ drm::control::{connector, crtc},
control::{connector, crtc, Mode},
Device as _,
},
wayland_protocols::wp::{ wayland_protocols::wp::{
linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1, linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1,
presentation_time::server::wp_presentation_feedback, presentation_time::server::wp_presentation_feedback,
}, },
wayland_server::protocol::wl_surface::WlSurface, wayland_server::protocol::wl_surface::WlSurface,
}, },
utils::{Buffer as BufferCoords, Clock, Monotonic, Physical, Rectangle, Size, Transform}, utils::{Buffer as BufferCoords, Clock, Monotonic, Physical, Rectangle, Transform},
wayland::{ wayland::{
dmabuf::{get_dmabuf, DmabufFeedbackBuilder}, dmabuf::{get_dmabuf, DmabufFeedbackBuilder},
presentation::Refresh, presentation::Refresh,
@ -107,7 +103,7 @@ static NVIDIA_LOGO: &'static [u8] = include_bytes!("../../../../resources/icons/
#[derive(Debug)] #[derive(Debug)]
pub struct Surface { pub struct Surface {
pub(crate) connector: connector::Handle, pub(crate) connector: connector::Handle,
pub(super) _crtc: crtc::Handle, pub(super) crtc: crtc::Handle,
pub(crate) output: Output, pub(crate) output: Output,
known_nodes: HashSet<DrmNode>, known_nodes: HashSet<DrmNode>,
@ -130,7 +126,7 @@ pub struct SurfaceThreadState {
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
vrr_mode: AdaptiveSync, vrr_mode: AdaptiveSync,
frame_flags: FrameFlags, frame_flags: FrameFlags,
compositor: Option<GbmDrmCompositor>, compositor: Option<GbmDrmOutput>,
state: QueueState, state: QueueState,
timings: Timings, timings: Timings,
@ -189,7 +185,7 @@ impl MirroringState {
} }
} }
pub type GbmDrmCompositor = DrmCompositor< pub type GbmDrmOutput = DrmOutput<
GbmAllocator<DrmDeviceFd>, GbmAllocator<DrmDeviceFd>,
GbmDevice<DrmDeviceFd>, GbmDevice<DrmDeviceFd>,
Option<( Option<(
@ -227,9 +223,7 @@ impl Default for QueueState {
pub enum ThreadCommand { pub enum ThreadCommand {
Suspend(SyncSender<()>), Suspend(SyncSender<()>),
Resume { Resume {
surface: DrmSurface, compositor: GbmDrmOutput,
gbm: GbmDevice<DrmDeviceFd>,
cursor_size: Size<u32, BufferCoords>,
result: SyncSender<Result<()>>, result: SyncSender<Result<()>>,
}, },
NodeAdded { NodeAdded {
@ -243,9 +237,9 @@ pub enum ThreadCommand {
UpdateMirroring(Option<Output>), UpdateMirroring(Option<Output>),
VBlank(Option<DrmEventMetadata>), VBlank(Option<DrmEventMetadata>),
ScheduleRender, ScheduleRender,
SetMode(Mode, SyncSender<Result<()>>),
AdaptiveSyncAvailable(SyncSender<Result<VrrSupport>>), AdaptiveSyncAvailable(SyncSender<Result<VrrSupport>>),
UseAdaptiveSync(AdaptiveSync), UseAdaptiveSync(AdaptiveSync),
AllowOverlayScanout(bool, SyncSender<()>),
End, End,
DpmsOff, DpmsOff,
} }
@ -351,7 +345,7 @@ impl Surface {
Ok(Surface { Ok(Surface {
connector, connector,
_crtc: crtc, crtc,
output: output.clone(), output: output.clone(),
known_nodes: HashSet::new(), known_nodes: HashSet::new(),
active, active,
@ -402,12 +396,6 @@ impl Surface {
.send(ThreadCommand::UpdateMirroring(output)); .send(ThreadCommand::UpdateMirroring(output));
} }
pub fn set_mode(&mut self, mode: Mode) -> Result<()> {
let (tx, rx) = std::sync::mpsc::sync_channel(1);
let _ = self.thread_command.send(ThreadCommand::SetMode(mode, tx));
rx.recv().context("Surface thread died")?
}
pub fn adaptive_sync_support(&self) -> Result<VrrSupport> { pub fn adaptive_sync_support(&self) -> Result<VrrSupport> {
let (tx, rx) = std::sync::mpsc::sync_channel(1); let (tx, rx) = std::sync::mpsc::sync_channel(1);
let _ = self let _ = self
@ -441,31 +429,26 @@ impl Surface {
let _ = rx.recv(); let _ = rx.recv();
} }
pub fn resume( pub fn resume(&mut self, compositor: GbmDrmOutput) -> Result<()> {
&mut self,
surface: DrmSurface,
gbm: GbmDevice<DrmDeviceFd>,
cursor_size: Size<u32, BufferCoords>,
) -> Result<()> {
let (tx, rx) = std::sync::mpsc::sync_channel(1); let (tx, rx) = std::sync::mpsc::sync_channel(1);
self.plane_formats = surface self.plane_formats = compositor.with_compositor(|c| {
.plane_info() c.surface()
.formats .plane_info()
.iter() .formats
.copied() .iter()
.chain( .copied()
surface .chain(
.planes() c.surface()
.overlay .planes()
.iter() .overlay
.flat_map(|p| p.formats.iter().cloned()), .iter()
) .flat_map(|p| p.formats.iter().cloned()),
.collect::<FormatSet>(); )
.collect::<FormatSet>()
});
let _ = self.thread_command.send(ThreadCommand::Resume { let _ = self.thread_command.send(ThreadCommand::Resume {
surface, compositor,
gbm,
cursor_size,
result: tx, result: tx,
}); });
@ -560,13 +543,8 @@ fn surface_thread(
.handle() .handle()
.insert_source(thread_receiver, move |command, _, state| match command { .insert_source(thread_receiver, move |command, _, state| match command {
Event::Msg(ThreadCommand::Suspend(tx)) => state.suspend(tx), Event::Msg(ThreadCommand::Suspend(tx)) => state.suspend(tx),
Event::Msg(ThreadCommand::Resume { Event::Msg(ThreadCommand::Resume { compositor, result }) => {
surface, let _ = result.send(state.resume(compositor));
gbm,
cursor_size,
result,
}) => {
let _ = result.send(state.resume(surface, gbm, cursor_size));
} }
Event::Msg(ThreadCommand::NodeAdded { node, gbm, egl }) => { Event::Msg(ThreadCommand::NodeAdded { node, gbm, egl }) => {
if let Err(err) = state.node_added(node, gbm, egl) { if let Err(err) = state.node_added(node, gbm, egl) {
@ -589,20 +567,13 @@ fn surface_thread(
Event::Msg(ThreadCommand::UpdateMirroring(mirroring_output)) => { Event::Msg(ThreadCommand::UpdateMirroring(mirroring_output)) => {
state.update_mirroring(mirroring_output); state.update_mirroring(mirroring_output);
} }
Event::Msg(ThreadCommand::SetMode(mode, result)) => {
if let Some(compositor) = state.compositor.as_mut() {
let _ = result.send(compositor.use_mode(mode).map_err(Into::into));
} else {
let _ = result.send(Err(anyhow::anyhow!("Set mode with inactive surface")));
}
}
Event::Msg(ThreadCommand::AdaptiveSyncAvailable(result)) => { Event::Msg(ThreadCommand::AdaptiveSyncAvailable(result)) => {
if let Some(compositor) = state.compositor.as_mut() { if let Some(compositor) = state.compositor.as_mut() {
let _ = result.send( let _ = result.send(
compositor compositor
.vrr_supported( .with_compositor(|c| {
compositor.pending_connectors().into_iter().next().unwrap(), c.vrr_supported(c.pending_connectors().into_iter().next().unwrap())
) })
.map_err(Into::into), .map_err(Into::into),
); );
} else { } else {
@ -614,7 +585,7 @@ fn surface_thread(
} }
Event::Msg(ThreadCommand::DpmsOff) => { Event::Msg(ThreadCommand::DpmsOff) => {
if let Some(compositor) = state.compositor.as_mut() { if let Some(compositor) = state.compositor.as_mut() {
if let Err(err) = compositor.clear() { if let Err(err) = compositor.with_compositor(|c| c.clear()) {
error!("Failed to set DPMS off: {:?}", err); error!("Failed to set DPMS off: {:?}", err);
} }
match std::mem::replace(&mut state.state, QueueState::Idle) { match std::mem::replace(&mut state.state, QueueState::Idle) {
@ -670,69 +641,22 @@ impl SurfaceThreadState {
let _ = tx.send(()); let _ = tx.send(());
} }
fn resume( fn resume(&mut self, compositor: GbmDrmOutput) -> Result<()> {
&mut self, let mode = compositor.with_compositor(|c| c.surface().pending_mode());
surface: DrmSurface,
gbm: GbmDevice<DrmDeviceFd>,
cursor_size: Size<u32, BufferCoords>,
) -> Result<()> {
let driver = surface.get_driver().ok();
let mut planes = surface.planes().clone();
// QUIRK: Using an overlay plane on a nvidia card breaks the display controller (wtf...)
if driver.is_some_and(|driver| {
driver
.name()
.to_string_lossy()
.to_lowercase()
.contains("nvidia")
}) {
planes.overlay = vec![];
}
let render_formats = self
.api
.single_renderer(&self.target_node)
.unwrap()
.dmabuf_formats();
self.timings self.timings
.set_refresh_interval(Some(Duration::from_secs_f64( .set_refresh_interval(Some(Duration::from_secs_f64(
1_000.0 / drm_helpers::calculate_refresh_rate(surface.pending_mode()) as f64, 1_000.0 / drm_helpers::calculate_refresh_rate(mode) as f64,
))); )));
match DrmCompositor::new( if crate::utils::env::bool_var("COSMIC_DISABLE_DIRECT_SCANOUT").unwrap_or(false) {
&self.output, self.frame_flags.remove(FrameFlags::ALLOW_SCANOUT);
surface, } else if crate::utils::env::bool_var("COSMIC_DISABLE_OVERLAY_SCANOUT").unwrap_or(false) {
Some(planes), self.frame_flags
GbmAllocator::new( .remove(FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT);
gbm.clone(),
GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT,
),
gbm.clone(),
[
Fourcc::Abgr2101010,
Fourcc::Argb2101010,
Fourcc::Abgr8888,
Fourcc::Argb8888,
],
render_formats,
cursor_size,
Some(gbm),
) {
Ok(compositor) => {
if crate::utils::env::bool_var("COSMIC_DISABLE_DIRECT_SCANOUT").unwrap_or(false) {
self.frame_flags.remove(FrameFlags::ALLOW_SCANOUT);
}
self.active.store(true, Ordering::SeqCst);
self.compositor = Some(compositor);
Ok(())
}
Err(err) => {
self.active.store(false, Ordering::SeqCst);
Err(err.into())
}
} }
self.active.store(true, Ordering::SeqCst);
self.compositor = Some(compositor);
Ok(())
} }
fn node_added( fn node_added(
@ -817,7 +741,7 @@ impl SurfaceThreadState {
if self if self
.compositor .compositor
.as_ref() .as_ref()
.is_some_and(|comp| comp.vrr_enabled()) => .is_some_and(|comp| comp.with_compositor(|c| c.vrr_enabled())) =>
{ {
Refresh::Variable(rate) Refresh::Variable(rate)
} }
@ -975,47 +899,27 @@ impl SurfaceThreadState {
_ => false, _ => false,
}; };
let mut elements = { if self.vrr_mode == AdaptiveSync::Enabled {
let shell = self.shell.read().unwrap(); let shell = self.shell.read().unwrap();
let output = self.mirroring.as_ref().unwrap_or(&self.output); let output = self.mirroring.as_ref().unwrap_or(&self.output);
vrr = shell.workspaces.active(output).1.get_fullscreen().is_some();
}
let (previous_workspace, workspace) = shell.workspaces.active(output); let mut elements = output_elements(
let (previous_idx, idx) = shell.workspaces.active_num(&output); Some(&render_node),
let previous_workspace = previous_workspace &mut renderer,
.zip(previous_idx) &self.shell,
.map(|((w, start), idx)| (w.handle, idx, start)); self.clock.now(),
if self.vrr_mode == AdaptiveSync::Enabled { self.mirroring.as_ref().unwrap_or(&self.output),
vrr = workspace.get_fullscreen().is_some(); CursorMode::All,
} #[cfg(not(feature = "debug"))]
let workspace = (workspace.handle, idx); None,
#[cfg(feature = "debug")]
std::mem::drop(shell); Some((&self.egui, &self.timings)),
)
let element_filter = if workspace_overview_is_open(output) { .map_err(|err| {
ElementFilter::LayerShellOnly anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err)
} else { })?;
ElementFilter::All
};
workspace_elements(
Some(&render_node),
&mut renderer,
&self.shell,
self.clock.now(),
output,
previous_workspace,
workspace,
CursorMode::All,
element_filter,
#[cfg(not(feature = "debug"))]
None,
#[cfg(feature = "debug")]
Some((&self.egui, &self.timings)),
)
.map_err(|err| {
anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err)
})?
};
self.timings.set_vrr(vrr); self.timings.set_vrr(vrr);
self.timings.elements_done(&self.clock); self.timings.elements_done(&self.clock);
@ -1172,24 +1076,24 @@ impl SurfaceThreadState {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
renderer = self.api.single_renderer(&self.target_node).unwrap(); renderer = self.api.single_renderer(&self.target_node).unwrap();
if let Err(err) = compositor.use_vrr(false) { if let Err(err) = compositor.with_compositor(|c| c.use_vrr(vrr)) {
warn!("Unable to set adaptive VRR state: {}", err); warn!("Unable to set adaptive VRR state: {}", err);
} }
compositor.render_frame( compositor.render_frame(
&mut renderer, &mut renderer,
&elements, &elements,
[0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 1.0],
FrameFlags::DEFAULT, self.frame_flags,
) )
} else { } else {
if let Err(err) = compositor.use_vrr(vrr) { if let Err(err) = compositor.with_compositor(|c| c.use_vrr(vrr)) {
warn!("Unable to set adaptive VRR state: {}", err); warn!("Unable to set adaptive VRR state: {}", err);
} }
compositor.render_frame( compositor.render_frame(
&mut renderer, &mut renderer,
&elements, &elements,
CLEAR_COLOR, // TODO use a theme neutral color CLEAR_COLOR, // TODO use a theme neutral color
FrameFlags::DEFAULT, self.frame_flags,
) )
}; };
self.timings.draw_done(&self.clock); self.timings.draw_done(&self.clock);

View file

@ -499,6 +499,53 @@ pub enum ElementFilter {
LayerShellOnly, LayerShellOnly,
} }
pub fn output_elements<R>(
_gpu: Option<&DrmNode>,
renderer: &mut R,
shell: &Arc<RwLock<Shell>>,
now: Time<Monotonic>,
output: &Output,
cursor_mode: CursorMode,
_fps: Option<(&EguiState, &Timings)>,
) -> Result<Vec<CosmicElement<R>>, RenderError<R::Error>>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: Send + Clone + 'static,
<R as Renderer>::Error: FromGlesError,
CosmicMappedRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
{
let shell_guard = shell.read().unwrap();
let (previous_workspace, workspace) = shell_guard.workspaces.active(output);
let (previous_idx, idx) = shell_guard.workspaces.active_num(&output);
let previous_workspace = previous_workspace
.zip(previous_idx)
.map(|((w, start), idx)| (w.handle, idx, start));
let workspace = (workspace.handle, idx);
std::mem::drop(shell_guard);
let element_filter = if workspace_overview_is_open(output) {
ElementFilter::LayerShellOnly
} else {
ElementFilter::All
};
workspace_elements(
_gpu,
renderer,
shell,
now,
output,
previous_workspace,
workspace,
cursor_mode,
element_filter,
_fps,
)
}
#[profiling::function] #[profiling::function]
pub fn workspace_elements<R>( pub fn workspace_elements<R>(
_gpu: Option<&DrmNode>, _gpu: Option<&DrmNode>,

View file

@ -228,6 +228,7 @@ pub fn init_backend(
&mut state.common.workspace_state.update(), &mut state.common.workspace_state.update(),
&state.common.xdg_activation_state, &state.common.xdg_activation_state,
state.common.startup_done.clone(), state.common.startup_done.clone(),
&state.common.clock,
); );
state.common.refresh(); state.common.refresh();
} }

View file

@ -363,6 +363,7 @@ pub fn init_backend(
&mut state.common.workspace_state.update(), &mut state.common.workspace_state.update(),
&state.common.xdg_activation_state, &state.common.xdg_activation_state,
state.common.startup_done.clone(), state.common.startup_done.clone(),
&state.common.clock,
); );
state.common.refresh(); state.common.refresh();
} }

View file

@ -11,7 +11,6 @@ use cosmic_config::{ConfigGet, CosmicConfigEntry};
use cosmic_settings_config::window_rules::ApplicationException; use cosmic_settings_config::window_rules::ApplicationException;
use cosmic_settings_config::{shortcuts, window_rules, Shortcuts}; use cosmic_settings_config::{shortcuts, window_rules, Shortcuts};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smithay::wayland::xdg_activation::XdgActivationState;
pub use smithay::{ pub use smithay::{
backend::input::KeyState, backend::input::KeyState,
input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState}, input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState},
@ -25,6 +24,10 @@ pub use smithay::{
}, },
utils::{Logical, Physical, Point, Size, Transform}, utils::{Logical, Physical, Point, Size, Transform},
}; };
use smithay::{
utils::{Clock, Monotonic},
wayland::xdg_activation::XdgActivationState,
};
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::{BTreeMap, HashMap}, collections::{BTreeMap, HashMap},
@ -381,6 +384,7 @@ impl Config {
workspace_state: &mut WorkspaceUpdateGuard<'_, State>, workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
xdg_activation_state: &XdgActivationState, xdg_activation_state: &XdgActivationState,
startup_done: Arc<AtomicBool>, startup_done: Arc<AtomicBool>,
clock: &Clock<Monotonic>,
) { ) {
let outputs = output_state.outputs().collect::<Vec<_>>(); let outputs = output_state.outputs().collect::<Vec<_>>();
let mut infos = outputs let mut infos = outputs
@ -422,6 +426,7 @@ impl Config {
workspace_state, workspace_state,
xdg_activation_state, xdg_activation_state,
startup_done.clone(), startup_done.clone(),
clock,
) { ) {
warn!(?err, "Failed to set new config."); warn!(?err, "Failed to set new config.");
found_outputs.clear(); found_outputs.clear();
@ -446,6 +451,7 @@ impl Config {
workspace_state, workspace_state,
xdg_activation_state, xdg_activation_state,
startup_done, startup_done,
clock,
) { ) {
error!(?err, "Failed to reset config."); error!(?err, "Failed to reset config.");
} else { } else {
@ -477,6 +483,7 @@ impl Config {
workspace_state, workspace_state,
xdg_activation_state, xdg_activation_state,
startup_done, startup_done,
clock,
) { ) {
warn!(?err, "Failed to set new config.",); warn!(?err, "Failed to set new config.",);
} else { } else {

View file

@ -297,11 +297,16 @@ impl BackendData {
workspace_state: &mut WorkspaceUpdateGuard<'_, State>, workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
xdg_activation_state: &XdgActivationState, xdg_activation_state: &XdgActivationState,
startup_done: Arc<AtomicBool>, startup_done: Arc<AtomicBool>,
clock: &Clock<Monotonic>,
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
let result = match self { let result = match self {
BackendData::Kms(ref mut state) => { BackendData::Kms(ref mut state) => state.apply_config_for_outputs(
state.apply_config_for_outputs(test_only, loop_handle, shell.clone(), startup_done) test_only,
} loop_handle,
shell.clone(),
startup_done,
clock,
),
BackendData::Winit(ref mut state) => state.apply_config_for_outputs(test_only), BackendData::Winit(ref mut state) => state.apply_config_for_outputs(test_only),
BackendData::X11(ref mut state) => state.apply_config_for_outputs(test_only), BackendData::X11(ref mut state) => state.apply_config_for_outputs(test_only),
_ => unreachable!("No backend set when applying output config"), _ => unreachable!("No backend set when applying output config"),

View file

@ -32,8 +32,7 @@ impl DrmLeaseHandler for State {
.drm_devices .drm_devices
.get_mut(&node) .get_mut(&node)
.ok_or(LeaseRejected::default())?; .ok_or(LeaseRejected::default())?;
let mut builder = DrmLeaseBuilder::new(backend.drm.device());
let mut builder = DrmLeaseBuilder::new(&backend.drm);
for conn in request.connectors { for conn in request.connectors {
if let Some((_, crtc)) = backend if let Some((_, crtc)) = backend
.leased_connectors .leased_connectors
@ -44,6 +43,7 @@ impl DrmLeaseHandler for State {
builder.add_crtc(*crtc); builder.add_crtc(*crtc);
let planes = backend let planes = backend
.drm .drm
.device()
.planes(crtc) .planes(crtc)
.map_err(LeaseRejected::with_cause)?; .map_err(LeaseRejected::with_cause)?;
let (primary_plane, primary_plane_claim) = planes let (primary_plane, primary_plane_claim) = planes
@ -52,6 +52,7 @@ impl DrmLeaseHandler for State {
.find_map(|plane| { .find_map(|plane| {
backend backend
.drm .drm
.device_mut()
.claim_plane(plane.handle, *crtc) .claim_plane(plane.handle, *crtc)
.map(|claim| (plane, claim)) .map(|claim| (plane, claim))
}) })
@ -60,6 +61,7 @@ impl DrmLeaseHandler for State {
if let Some((cursor, claim)) = planes.cursor.into_iter().find_map(|plane| { if let Some((cursor, claim)) = planes.cursor.into_iter().find_map(|plane| {
backend backend
.drm .drm
.device_mut()
.claim_plane(plane.handle, *crtc) .claim_plane(plane.handle, *crtc)
.map(|claim| (plane, claim)) .map(|claim| (plane, claim))
}) { }) {

View file

@ -140,6 +140,7 @@ impl State {
&mut self.common.workspace_state.update(), &mut self.common.workspace_state.update(),
&self.common.xdg_activation_state, &self.common.xdg_activation_state,
self.common.startup_done.clone(), self.common.startup_done.clone(),
&self.common.clock,
); );
if let Err(err) = res { if let Err(err) = res {
warn!(?err, "Failed to apply config. Resetting"); warn!(?err, "Failed to apply config. Resetting");
@ -161,6 +162,7 @@ impl State {
&mut self.common.workspace_state.update(), &mut self.common.workspace_state.update(),
&self.common.xdg_activation_state, &self.common.xdg_activation_state,
self.common.startup_done.clone(), self.common.startup_done.clone(),
&self.common.clock,
) { ) {
error!(?err, "Failed to reset output config."); error!(?err, "Failed to reset output config.");
} }