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

@ -3,12 +3,12 @@
use crate::{
backend::render::{
element::{CosmicElement, DamageElement},
init_shaders, workspace_elements, CursorMode, ElementFilter, GlMultiRenderer, CLEAR_COLOR,
init_shaders, output_elements, CursorMode, GlMultiRenderer, CLEAR_COLOR,
},
config::AdaptiveSync,
shell::Shell,
state::SurfaceDmabufFeedback,
utils::{prelude::*, quirks::workspace_overview_is_open},
utils::prelude::*,
wayland::{
handlers::screencopy::{submit_buffer, FrameHolder, SessionData},
protocols::screencopy::{
@ -23,14 +23,13 @@ use smithay::{
backend::{
allocator::{
format::FormatSet,
gbm::{GbmAllocator, GbmBufferFlags, GbmDevice},
gbm::{GbmAllocator, GbmDevice},
Fourcc,
},
drm::{
compositor::{
BlitFrameResultError, DrmCompositor, FrameError, FrameFlags, PrimaryPlaneElement,
},
DrmDeviceFd, DrmEventMetadata, DrmEventTime, DrmNode, DrmSurface, VrrSupport,
compositor::{BlitFrameResultError, FrameError, FrameFlags, PrimaryPlaneElement},
output::DrmOutput,
DrmDeviceFd, DrmEventMetadata, DrmEventTime, DrmNode, VrrSupport,
},
egl::EGLContext,
renderer::{
@ -57,17 +56,14 @@ use smithay::{
timer::{TimeoutAction, Timer},
EventLoop, LoopHandle, RegistrationToken,
},
drm::{
control::{connector, crtc, Mode},
Device as _,
},
drm::control::{connector, crtc},
wayland_protocols::wp::{
linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1,
presentation_time::server::wp_presentation_feedback,
},
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::{
dmabuf::{get_dmabuf, DmabufFeedbackBuilder},
presentation::Refresh,
@ -107,7 +103,7 @@ static NVIDIA_LOGO: &'static [u8] = include_bytes!("../../../../resources/icons/
#[derive(Debug)]
pub struct Surface {
pub(crate) connector: connector::Handle,
pub(super) _crtc: crtc::Handle,
pub(super) crtc: crtc::Handle,
pub(crate) output: Output,
known_nodes: HashSet<DrmNode>,
@ -130,7 +126,7 @@ pub struct SurfaceThreadState {
active: Arc<AtomicBool>,
vrr_mode: AdaptiveSync,
frame_flags: FrameFlags,
compositor: Option<GbmDrmCompositor>,
compositor: Option<GbmDrmOutput>,
state: QueueState,
timings: Timings,
@ -189,7 +185,7 @@ impl MirroringState {
}
}
pub type GbmDrmCompositor = DrmCompositor<
pub type GbmDrmOutput = DrmOutput<
GbmAllocator<DrmDeviceFd>,
GbmDevice<DrmDeviceFd>,
Option<(
@ -227,9 +223,7 @@ impl Default for QueueState {
pub enum ThreadCommand {
Suspend(SyncSender<()>),
Resume {
surface: DrmSurface,
gbm: GbmDevice<DrmDeviceFd>,
cursor_size: Size<u32, BufferCoords>,
compositor: GbmDrmOutput,
result: SyncSender<Result<()>>,
},
NodeAdded {
@ -243,9 +237,9 @@ pub enum ThreadCommand {
UpdateMirroring(Option<Output>),
VBlank(Option<DrmEventMetadata>),
ScheduleRender,
SetMode(Mode, SyncSender<Result<()>>),
AdaptiveSyncAvailable(SyncSender<Result<VrrSupport>>),
UseAdaptiveSync(AdaptiveSync),
AllowOverlayScanout(bool, SyncSender<()>),
End,
DpmsOff,
}
@ -351,7 +345,7 @@ impl Surface {
Ok(Surface {
connector,
_crtc: crtc,
crtc,
output: output.clone(),
known_nodes: HashSet::new(),
active,
@ -402,12 +396,6 @@ impl Surface {
.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> {
let (tx, rx) = std::sync::mpsc::sync_channel(1);
let _ = self
@ -441,31 +429,26 @@ impl Surface {
let _ = rx.recv();
}
pub fn resume(
&mut self,
surface: DrmSurface,
gbm: GbmDevice<DrmDeviceFd>,
cursor_size: Size<u32, BufferCoords>,
) -> Result<()> {
pub fn resume(&mut self, compositor: GbmDrmOutput) -> Result<()> {
let (tx, rx) = std::sync::mpsc::sync_channel(1);
self.plane_formats = surface
.plane_info()
.formats
.iter()
.copied()
.chain(
surface
.planes()
.overlay
.iter()
.flat_map(|p| p.formats.iter().cloned()),
)
.collect::<FormatSet>();
self.plane_formats = compositor.with_compositor(|c| {
c.surface()
.plane_info()
.formats
.iter()
.copied()
.chain(
c.surface()
.planes()
.overlay
.iter()
.flat_map(|p| p.formats.iter().cloned()),
)
.collect::<FormatSet>()
});
let _ = self.thread_command.send(ThreadCommand::Resume {
surface,
gbm,
cursor_size,
compositor,
result: tx,
});
@ -560,13 +543,8 @@ fn surface_thread(
.handle()
.insert_source(thread_receiver, move |command, _, state| match command {
Event::Msg(ThreadCommand::Suspend(tx)) => state.suspend(tx),
Event::Msg(ThreadCommand::Resume {
surface,
gbm,
cursor_size,
result,
}) => {
let _ = result.send(state.resume(surface, gbm, cursor_size));
Event::Msg(ThreadCommand::Resume { compositor, result }) => {
let _ = result.send(state.resume(compositor));
}
Event::Msg(ThreadCommand::NodeAdded { 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)) => {
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)) => {
if let Some(compositor) = state.compositor.as_mut() {
let _ = result.send(
compositor
.vrr_supported(
compositor.pending_connectors().into_iter().next().unwrap(),
)
.with_compositor(|c| {
c.vrr_supported(c.pending_connectors().into_iter().next().unwrap())
})
.map_err(Into::into),
);
} else {
@ -614,7 +585,7 @@ fn surface_thread(
}
Event::Msg(ThreadCommand::DpmsOff) => {
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);
}
match std::mem::replace(&mut state.state, QueueState::Idle) {
@ -670,69 +641,22 @@ impl SurfaceThreadState {
let _ = tx.send(());
}
fn resume(
&mut self,
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();
fn resume(&mut self, compositor: GbmDrmOutput) -> Result<()> {
let mode = compositor.with_compositor(|c| c.surface().pending_mode());
self.timings
.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(
&self.output,
surface,
Some(planes),
GbmAllocator::new(
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())
}
if crate::utils::env::bool_var("COSMIC_DISABLE_DIRECT_SCANOUT").unwrap_or(false) {
self.frame_flags.remove(FrameFlags::ALLOW_SCANOUT);
} else if crate::utils::env::bool_var("COSMIC_DISABLE_OVERLAY_SCANOUT").unwrap_or(false) {
self.frame_flags
.remove(FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT);
}
self.active.store(true, Ordering::SeqCst);
self.compositor = Some(compositor);
Ok(())
}
fn node_added(
@ -817,7 +741,7 @@ impl SurfaceThreadState {
if self
.compositor
.as_ref()
.is_some_and(|comp| comp.vrr_enabled()) =>
.is_some_and(|comp| comp.with_compositor(|c| c.vrr_enabled())) =>
{
Refresh::Variable(rate)
}
@ -975,47 +899,27 @@ impl SurfaceThreadState {
_ => false,
};
let mut elements = {
if self.vrr_mode == AdaptiveSync::Enabled {
let shell = self.shell.read().unwrap();
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 (previous_idx, idx) = shell.workspaces.active_num(&output);
let previous_workspace = previous_workspace
.zip(previous_idx)
.map(|((w, start), idx)| (w.handle, idx, start));
if self.vrr_mode == AdaptiveSync::Enabled {
vrr = workspace.get_fullscreen().is_some();
}
let workspace = (workspace.handle, idx);
std::mem::drop(shell);
let element_filter = if workspace_overview_is_open(output) {
ElementFilter::LayerShellOnly
} 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)
})?
};
let mut elements = output_elements(
Some(&render_node),
&mut renderer,
&self.shell,
self.clock.now(),
self.mirroring.as_ref().unwrap_or(&self.output),
CursorMode::All,
#[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.elements_done(&self.clock);
@ -1172,24 +1076,24 @@ impl SurfaceThreadState {
.collect::<Vec<_>>();
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);
}
compositor.render_frame(
&mut renderer,
&elements,
[0.0, 0.0, 0.0, 1.0],
FrameFlags::DEFAULT,
self.frame_flags,
)
} 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);
}
compositor.render_frame(
&mut renderer,
&elements,
CLEAR_COLOR, // TODO use a theme neutral color
FrameFlags::DEFAULT,
self.frame_flags,
)
};
self.timings.draw_done(&self.clock);