kms: Use new DrmOutput api
This commit is contained in:
parent
e356e3c589
commit
8b87d6524e
10 changed files with 347 additions and 217 deletions
|
|
@ -1,28 +1,40 @@
|
|||
// 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::*,
|
||||
wayland::protocols::screencopy::Frame as ScreencopyFrame,
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use libc::dev_t;
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::gbm::GbmDevice,
|
||||
drm::{DrmDevice, DrmDeviceFd, DrmEvent, DrmNode},
|
||||
allocator::{
|
||||
gbm::{GbmAllocator, GbmDevice},
|
||||
Fourcc,
|
||||
},
|
||||
drm::{
|
||||
compositor::FrameFlags, output::DrmOutputManager, DrmDevice, DrmDeviceFd, DrmEvent,
|
||||
DrmNode,
|
||||
},
|
||||
egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay},
|
||||
session::Session,
|
||||
},
|
||||
desktop::utils::OutputPresentationFeedback,
|
||||
output::{Mode as OutputMode, Output, PhysicalProperties, Scale, Subpixel},
|
||||
reexports::{
|
||||
calloop::{LoopHandle, RegistrationToken},
|
||||
drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags},
|
||||
gbm::BufferObjectFlags as GbmBufferFlags,
|
||||
rustix::fs::OFlags,
|
||||
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},
|
||||
};
|
||||
use tracing::{error, info, warn};
|
||||
|
|
@ -32,7 +44,7 @@ use std::{
|
|||
collections::{HashMap, HashSet},
|
||||
fmt,
|
||||
path::{Path, PathBuf},
|
||||
sync::{atomic::AtomicBool, Arc, RwLock},
|
||||
sync::{atomic::AtomicBool, mpsc::Receiver, Arc, RwLock},
|
||||
};
|
||||
|
||||
use super::{drm_helpers, socket::Socket, surface::Surface};
|
||||
|
|
@ -44,6 +56,16 @@ pub struct EGLInternals {
|
|||
pub context: EGLContext,
|
||||
}
|
||||
|
||||
pub type GbmDrmOutputManager = DrmOutputManager<
|
||||
GbmAllocator<DrmDeviceFd>,
|
||||
GbmDevice<DrmDeviceFd>,
|
||||
Option<(
|
||||
OutputPresentationFeedback,
|
||||
Receiver<(ScreencopyFrame, Vec<Rectangle<i32, BufferCoords>>)>,
|
||||
)>,
|
||||
DrmDeviceFd,
|
||||
>;
|
||||
|
||||
pub struct Device {
|
||||
pub dev_node: DrmNode,
|
||||
pub render_node: DrmNode,
|
||||
|
|
@ -51,8 +73,8 @@ pub struct Device {
|
|||
|
||||
pub outputs: HashMap<connector::Handle, Output>,
|
||||
pub surfaces: HashMap<crtc::Handle, Surface>,
|
||||
pub drm: GbmDrmOutputManager,
|
||||
pub gbm: GbmDevice<DrmDeviceFd>,
|
||||
pub drm: DrmDevice,
|
||||
|
||||
supports_atomic: bool,
|
||||
|
||||
|
|
@ -72,8 +94,7 @@ impl fmt::Debug for Device {
|
|||
.field("render_node", &self.render_node)
|
||||
.field("outputs", &self.outputs)
|
||||
.field("surfaces", &self.surfaces)
|
||||
.field("drm", &self.drm)
|
||||
.field("gbm", &self.gbm)
|
||||
.field("drm", &"..")
|
||||
.field("egl", &self.egl)
|
||||
.field("supports_atomic", &self.supports_atomic)
|
||||
.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 {
|
||||
dev_node: drm_node,
|
||||
render_node,
|
||||
|
|
@ -197,8 +235,8 @@ impl State {
|
|||
|
||||
outputs: HashMap::new(),
|
||||
surfaces: HashMap::new(),
|
||||
gbm: gbm.clone(),
|
||||
drm,
|
||||
gbm,
|
||||
|
||||
supports_atomic,
|
||||
|
||||
|
|
@ -264,6 +302,7 @@ impl State {
|
|||
&mut self.common.workspace_state.update(),
|
||||
&self.common.xdg_activation_state,
|
||||
self.common.startup_done.clone(),
|
||||
&self.common.clock,
|
||||
);
|
||||
|
||||
self.backend.kms().refresh_used_devices()?;
|
||||
|
|
@ -364,6 +403,7 @@ impl State {
|
|||
&mut self.common.workspace_state.update(),
|
||||
&self.common.xdg_activation_state,
|
||||
self.common.startup_done.clone(),
|
||||
&self.common.clock,
|
||||
);
|
||||
// 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.
|
||||
|
|
@ -417,6 +457,7 @@ impl State {
|
|||
&mut self.common.workspace_state.update(),
|
||||
&self.common.xdg_activation_state,
|
||||
self.common.startup_done.clone(),
|
||||
&self.common.clock,
|
||||
);
|
||||
self.common.refresh();
|
||||
} else {
|
||||
|
|
@ -435,7 +476,8 @@ pub struct OutputChanges {
|
|||
impl Device {
|
||||
pub fn enumerate_surfaces(&mut self) -> Result<OutputChanges> {
|
||||
// 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
|
||||
.surfaces
|
||||
|
|
@ -486,19 +528,20 @@ impl Device {
|
|||
.get(&conn)
|
||||
.cloned()
|
||||
.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`")?;
|
||||
|
||||
let non_desktop = match drm_helpers::get_property_val(&self.drm, conn, "non-desktop") {
|
||||
Ok((val_type, value)) => val_type.convert_value(value).as_boolean().unwrap(),
|
||||
Err(err) => {
|
||||
warn!(
|
||||
?err,
|
||||
"Failed to determine if connector is meant desktop usage, assuming so."
|
||||
);
|
||||
false
|
||||
}
|
||||
};
|
||||
let non_desktop =
|
||||
match drm_helpers::get_property_val(self.drm.device_mut(), conn, "non-desktop") {
|
||||
Ok((val_type, value)) => val_type.convert_value(value).as_boolean().unwrap(),
|
||||
Err(err) => {
|
||||
warn!(
|
||||
?err,
|
||||
"Failed to determine if connector is meant desktop usage, assuming so."
|
||||
);
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if non_desktop {
|
||||
if let Some(crtc) = maybe_crtc {
|
||||
|
|
@ -528,7 +571,7 @@ impl Device {
|
|||
.user_data()
|
||||
.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")?;
|
||||
|
||||
let has_surface = if let Some(crtc) = maybe_crtc {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use smithay::{
|
|||
dmabuf::Dmabuf,
|
||||
gbm::{GbmAllocator, GbmBufferFlags},
|
||||
},
|
||||
drm::{DrmDeviceFd, DrmNode, NodeType},
|
||||
drm::{output::DrmOutputRenderElements, DrmDeviceFd, DrmNode, NodeType},
|
||||
egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay},
|
||||
input::InputEvent,
|
||||
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
||||
|
|
@ -27,13 +27,17 @@ use smithay::{
|
|||
output::Output,
|
||||
reexports::{
|
||||
calloop::{Dispatcher, EventLoop, LoopHandle},
|
||||
drm::control::{crtc, Device as _},
|
||||
drm::{
|
||||
control::{crtc, Device as _},
|
||||
Device as _,
|
||||
},
|
||||
input::{self, Libinput},
|
||||
wayland_server::{Client, DisplayHandle},
|
||||
},
|
||||
utils::{DevPath, Size},
|
||||
utils::{Clock, DevPath, Monotonic, Size},
|
||||
wayland::{dmabuf::DmabufGlobal, relative_pointer::RelativePointerManagerState},
|
||||
};
|
||||
use surface::GbmDrmOutput;
|
||||
use tracing::{error, info, trace, warn};
|
||||
|
||||
use std::{
|
||||
|
|
@ -53,7 +57,7 @@ pub(crate) use surface::Surface;
|
|||
use device::*;
|
||||
pub use surface::Timings;
|
||||
|
||||
use super::render::init_shaders;
|
||||
use super::render::{init_shaders, output_elements, CursorMode, CLEAR_COLOR};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsState {
|
||||
|
|
@ -321,6 +325,7 @@ impl State {
|
|||
&mut state.common.workspace_state.update(),
|
||||
&state.common.xdg_activation_state,
|
||||
state.common.startup_done.clone(),
|
||||
&state.common.clock,
|
||||
);
|
||||
state.common.refresh();
|
||||
});
|
||||
|
|
@ -528,6 +533,7 @@ impl KmsState {
|
|||
loop_handle: &LoopHandle<'static, State>,
|
||||
shell: Arc<RwLock<Shell>>,
|
||||
startup_done: Arc<AtomicBool>,
|
||||
clock: &Clock<Monotonic>,
|
||||
) -> Result<Vec<Output>, anyhow::Error> {
|
||||
if !self.session.is_active() {
|
||||
return Ok(Vec::new());
|
||||
|
|
@ -554,7 +560,7 @@ impl KmsState {
|
|||
// TODO: Right now we always keep crtcs of already enabled 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
|
||||
.crtcs()
|
||||
.iter()
|
||||
|
|
@ -579,16 +585,20 @@ impl KmsState {
|
|||
});
|
||||
|
||||
for conn in open_conns {
|
||||
let conn_info = device.drm.get_connector(conn, false).with_context(|| {
|
||||
format!(
|
||||
"Failed to query drm device: {:?}",
|
||||
device.drm.dev_path().as_deref().map(Path::display),
|
||||
)
|
||||
})?;
|
||||
let conn_info = device
|
||||
.drm
|
||||
.device()
|
||||
.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
|
||||
.encoders()
|
||||
.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()) {
|
||||
if free_crtcs.contains(&crtc) {
|
||||
|
|
@ -638,13 +648,20 @@ impl KmsState {
|
|||
|
||||
let mut all_outputs = Vec::new();
|
||||
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() {
|
||||
let output_config = surface.output.config();
|
||||
|
||||
let drm = &mut device.drm;
|
||||
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
|
||||
.modes()
|
||||
.iter()
|
||||
|
|
@ -662,14 +679,64 @@ impl KmsState {
|
|||
|
||||
if !test_only {
|
||||
if !surface.is_active() {
|
||||
let drm_surface = drm
|
||||
.create_surface(*crtc, *mode, &[conn])
|
||||
.with_context(|| "Failed to create drm surface")?;
|
||||
let gbm = device.gbm.clone();
|
||||
let cursor_size = drm.cursor_size();
|
||||
let compositor: GbmDrmOutput = {
|
||||
let mut planes = drm
|
||||
.device()
|
||||
.planes(crtc)
|
||||
.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 Err(err) = drm_helpers::set_max_bpc(drm, conn, bpc) {
|
||||
if let Err(err) = drm_helpers::set_max_bpc(drm.device(), conn, bpc) {
|
||||
warn!(
|
||||
?bpc,
|
||||
?err,
|
||||
|
|
@ -682,7 +749,7 @@ impl KmsState {
|
|||
let vrr = output_config.vrr;
|
||||
std::mem::drop(output_config);
|
||||
|
||||
match surface.resume(drm_surface, gbm, cursor_size) {
|
||||
match surface.resume(compositor) {
|
||||
Ok(_) => {
|
||||
surface.output.set_adaptive_sync_support(
|
||||
surface.adaptive_sync_support().ok(),
|
||||
|
|
@ -711,8 +778,29 @@ impl KmsState {
|
|||
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")?;
|
||||
}
|
||||
}
|
||||
|
|
@ -731,6 +819,36 @@ impl KmsState {
|
|||
.map(|(_, output)| output.clone())
|
||||
.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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -499,6 +499,53 @@ pub enum ElementFilter {
|
|||
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]
|
||||
pub fn workspace_elements<R>(
|
||||
_gpu: Option<&DrmNode>,
|
||||
|
|
|
|||
|
|
@ -228,6 +228,7 @@ pub fn init_backend(
|
|||
&mut state.common.workspace_state.update(),
|
||||
&state.common.xdg_activation_state,
|
||||
state.common.startup_done.clone(),
|
||||
&state.common.clock,
|
||||
);
|
||||
state.common.refresh();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -363,6 +363,7 @@ pub fn init_backend(
|
|||
&mut state.common.workspace_state.update(),
|
||||
&state.common.xdg_activation_state,
|
||||
state.common.startup_done.clone(),
|
||||
&state.common.clock,
|
||||
);
|
||||
state.common.refresh();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use cosmic_config::{ConfigGet, CosmicConfigEntry};
|
|||
use cosmic_settings_config::window_rules::ApplicationException;
|
||||
use cosmic_settings_config::{shortcuts, window_rules, Shortcuts};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smithay::wayland::xdg_activation::XdgActivationState;
|
||||
pub use smithay::{
|
||||
backend::input::KeyState,
|
||||
input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState},
|
||||
|
|
@ -25,6 +24,10 @@ pub use smithay::{
|
|||
},
|
||||
utils::{Logical, Physical, Point, Size, Transform},
|
||||
};
|
||||
use smithay::{
|
||||
utils::{Clock, Monotonic},
|
||||
wayland::xdg_activation::XdgActivationState,
|
||||
};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{BTreeMap, HashMap},
|
||||
|
|
@ -381,6 +384,7 @@ impl Config {
|
|||
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
||||
xdg_activation_state: &XdgActivationState,
|
||||
startup_done: Arc<AtomicBool>,
|
||||
clock: &Clock<Monotonic>,
|
||||
) {
|
||||
let outputs = output_state.outputs().collect::<Vec<_>>();
|
||||
let mut infos = outputs
|
||||
|
|
@ -422,6 +426,7 @@ impl Config {
|
|||
workspace_state,
|
||||
xdg_activation_state,
|
||||
startup_done.clone(),
|
||||
clock,
|
||||
) {
|
||||
warn!(?err, "Failed to set new config.");
|
||||
found_outputs.clear();
|
||||
|
|
@ -446,6 +451,7 @@ impl Config {
|
|||
workspace_state,
|
||||
xdg_activation_state,
|
||||
startup_done,
|
||||
clock,
|
||||
) {
|
||||
error!(?err, "Failed to reset config.");
|
||||
} else {
|
||||
|
|
@ -477,6 +483,7 @@ impl Config {
|
|||
workspace_state,
|
||||
xdg_activation_state,
|
||||
startup_done,
|
||||
clock,
|
||||
) {
|
||||
warn!(?err, "Failed to set new config.",);
|
||||
} else {
|
||||
|
|
|
|||
11
src/state.rs
11
src/state.rs
|
|
@ -297,11 +297,16 @@ impl BackendData {
|
|||
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
||||
xdg_activation_state: &XdgActivationState,
|
||||
startup_done: Arc<AtomicBool>,
|
||||
clock: &Clock<Monotonic>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let result = match self {
|
||||
BackendData::Kms(ref mut state) => {
|
||||
state.apply_config_for_outputs(test_only, loop_handle, shell.clone(), startup_done)
|
||||
}
|
||||
BackendData::Kms(ref mut state) => state.apply_config_for_outputs(
|
||||
test_only,
|
||||
loop_handle,
|
||||
shell.clone(),
|
||||
startup_done,
|
||||
clock,
|
||||
),
|
||||
BackendData::Winit(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"),
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@ impl DrmLeaseHandler for State {
|
|||
.drm_devices
|
||||
.get_mut(&node)
|
||||
.ok_or(LeaseRejected::default())?;
|
||||
|
||||
let mut builder = DrmLeaseBuilder::new(&backend.drm);
|
||||
let mut builder = DrmLeaseBuilder::new(backend.drm.device());
|
||||
for conn in request.connectors {
|
||||
if let Some((_, crtc)) = backend
|
||||
.leased_connectors
|
||||
|
|
@ -44,6 +43,7 @@ impl DrmLeaseHandler for State {
|
|||
builder.add_crtc(*crtc);
|
||||
let planes = backend
|
||||
.drm
|
||||
.device()
|
||||
.planes(crtc)
|
||||
.map_err(LeaseRejected::with_cause)?;
|
||||
let (primary_plane, primary_plane_claim) = planes
|
||||
|
|
@ -52,6 +52,7 @@ impl DrmLeaseHandler for State {
|
|||
.find_map(|plane| {
|
||||
backend
|
||||
.drm
|
||||
.device_mut()
|
||||
.claim_plane(plane.handle, *crtc)
|
||||
.map(|claim| (plane, claim))
|
||||
})
|
||||
|
|
@ -60,6 +61,7 @@ impl DrmLeaseHandler for State {
|
|||
if let Some((cursor, claim)) = planes.cursor.into_iter().find_map(|plane| {
|
||||
backend
|
||||
.drm
|
||||
.device_mut()
|
||||
.claim_plane(plane.handle, *crtc)
|
||||
.map(|claim| (plane, claim))
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ impl State {
|
|||
&mut self.common.workspace_state.update(),
|
||||
&self.common.xdg_activation_state,
|
||||
self.common.startup_done.clone(),
|
||||
&self.common.clock,
|
||||
);
|
||||
if let Err(err) = res {
|
||||
warn!(?err, "Failed to apply config. Resetting");
|
||||
|
|
@ -161,6 +162,7 @@ impl State {
|
|||
&mut self.common.workspace_state.update(),
|
||||
&self.common.xdg_activation_state,
|
||||
self.common.startup_done.clone(),
|
||||
&self.common.clock,
|
||||
) {
|
||||
error!(?err, "Failed to reset output config.");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue