cosmic-comp/src/backend/kms/mod.rs

1540 lines
61 KiB
Rust
Raw Normal View History

2022-01-20 19:51:46 +01:00
// SPDX-License-Identifier: GPL-3.0-only
2023-03-07 22:20:44 +01:00
#[cfg(feature = "debug")]
use crate::backend::render::element::AsGlowRenderer;
2022-01-25 15:45:15 +01:00
use crate::{
2023-03-07 22:20:44 +01:00
backend::render::{workspace_elements, CLEAR_COLOR},
2022-04-14 22:16:37 +02:00
config::OutputConfig,
shell::Shell,
2023-03-31 13:57:37 +02:00
state::{BackendData, ClientState, Common, Data, Fps, SurfaceDmabufFeedback},
utils::prelude::*,
2022-11-03 18:51:27 +01:00
wayland::{
2023-03-07 16:37:11 +01:00
handlers::screencopy::{render_session, UserdataExt},
2022-11-03 18:51:27 +01:00
protocols::screencopy::{BufferParams, Session as ScreencopySession},
},
2022-01-25 15:45:15 +01:00
};
2022-02-04 21:23:27 +01:00
2022-01-25 15:45:15 +01:00
use anyhow::{Context, Result};
2023-03-07 16:37:11 +01:00
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::FailureReason;
2022-01-25 15:45:15 +01:00
use smithay::{
backend::{
allocator::{
2023-05-02 17:24:18 +02:00
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
gbm::{GbmAllocator, GbmBufferFlags, GbmDevice},
vulkan::{ImageUsageFlags, VulkanAllocator},
2023-03-31 13:57:37 +02:00
Allocator, Format, Fourcc,
},
2022-12-27 18:27:29 +01:00
drm::{
2023-05-02 17:24:18 +02:00
compositor::{BlitFrameResultError, DrmCompositor, FrameError},
2023-03-07 16:37:11 +01:00
DrmDevice, DrmDeviceFd, DrmEvent, DrmEventTime, DrmNode, NodeType,
2022-12-27 18:27:29 +01:00
},
2022-02-04 21:23:27 +01:00
egl::{EGLContext, EGLDevice, EGLDisplay},
2022-04-25 23:00:30 +02:00
input::InputEvent,
2022-01-25 15:45:15 +01:00
libinput::{LibinputInputBackend, LibinputSessionInterface},
2022-03-16 20:06:31 +01:00
renderer::{
2023-03-07 16:37:11 +01:00
buffer_dimensions,
2023-06-28 22:20:06 +02:00
damage::{Error as RenderError, OutputNoMode, RenderOutputResult},
2023-05-02 17:24:18 +02:00
element::Element,
gles::{GlesRenderbuffer, GlesTexture},
2022-11-17 20:32:54 +01:00
glow::GlowRenderer,
2023-03-07 16:37:11 +01:00
multigpu::{gbm::GbmGlesBackend, Error as MultiError, GpuManager},
2023-06-28 22:20:06 +02:00
sync::SyncPoint,
2023-05-02 17:24:18 +02:00
Bind, ImportDma, Offscreen,
2022-03-16 20:06:31 +01:00
},
2023-01-03 19:17:51 +01:00
session::{libseat::LibSeatSession, Event as SessionEvent, Session},
2022-03-16 20:06:31 +01:00
udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent},
vulkan::{version::Version, Instance, PhysicalDevice},
2022-01-25 15:45:15 +01:00
},
2022-11-17 20:32:54 +01:00
desktop::utils::OutputPresentationFeedback,
input::Seat,
output::{Mode as OutputMode, Output, PhysicalProperties, Subpixel},
2022-01-25 15:45:15 +01:00
reexports::{
2022-02-04 21:23:27 +01:00
calloop::{
timer::{TimeoutAction, Timer},
Dispatcher, EventLoop, InsertError, LoopHandle, RegistrationToken,
2022-01-25 15:45:15 +01:00
},
2023-03-07 16:37:11 +01:00
drm::{
control::{connector, crtc, Device as ControlDevice, ModeTypeFlags},
Device as _,
},
2022-01-25 15:45:15 +01:00
input::Libinput,
2022-02-04 21:23:27 +01:00
nix::{fcntl::OFlag, sys::stat::dev_t},
2023-03-31 13:57:37 +02:00
wayland_protocols::wp::{
linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1,
presentation_time::server::wp_presentation_feedback,
},
wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource},
2022-01-25 15:45:15 +01:00
},
2023-01-03 19:17:51 +01:00
utils::{DeviceFd, Size, Transform},
wayland::{
2023-03-31 13:57:37 +02:00
dmabuf::{get_dmabuf, DmabufFeedbackBuilder, DmabufGlobal},
2023-03-07 16:37:11 +01:00
relative_pointer::RelativePointerManagerState,
seat::WaylandFocus,
shm::{shm_format_to_fourcc, with_buffer_contents},
},
2023-01-24 19:32:57 +01:00
xwayland::XWaylandClientData,
2022-01-25 15:45:15 +01:00
};
use tracing::{error, info, trace, warn};
2022-02-04 21:23:27 +01:00
2022-01-25 15:45:15 +01:00
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
ffi::CStr,
2022-12-27 18:27:29 +01:00
os::unix::io::FromRawFd,
2022-01-25 15:45:15 +01:00
path::PathBuf,
2022-09-28 12:01:29 +02:00
time::Duration,
2022-01-25 15:45:15 +01:00
};
2022-01-20 19:51:46 +01:00
2022-02-01 13:59:39 +01:00
mod drm_helpers;
mod socket;
use socket::*;
2022-01-25 15:45:15 +01:00
2023-02-25 00:17:54 +01:00
use super::render::{init_shaders, CursorMode, GlMultiRenderer};
// for now we assume we need at least 3ms
const MIN_RENDER_TIME: Duration = Duration::from_millis(3);
2022-09-28 12:01:29 +02:00
2022-01-25 15:45:15 +01:00
pub struct KmsState {
2022-03-16 20:06:31 +01:00
devices: HashMap<DrmNode, Device>,
pub api: GpuManager<GbmGlesBackend<GlowRenderer>>,
pub primary: DrmNode,
2023-01-03 19:17:51 +01:00
session: LibSeatSession,
_tokens: Vec<RegistrationToken>,
2022-01-25 15:45:15 +01:00
}
pub struct Device {
2022-03-16 20:06:31 +01:00
render_node: DrmNode,
2022-01-25 15:45:15 +01:00
surfaces: HashMap<crtc::Handle, Surface>,
2023-02-27 23:53:27 +01:00
drm: DrmDevice,
gbm: GbmDevice<DrmDeviceFd>,
allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
2022-01-25 15:45:15 +01:00
formats: HashSet<Format>,
supports_atomic: bool,
event_token: Option<RegistrationToken>,
socket: Option<Socket>,
2022-01-25 15:45:15 +01:00
}
pub struct Surface {
2023-03-07 16:37:11 +01:00
surface: Option<GbmDrmCompositor>,
connector: connector::Handle,
2022-01-25 15:45:15 +01:00
output: Output,
refresh_rate: u32,
vrr: bool,
scheduled: bool,
2022-01-25 15:45:15 +01:00
pending: bool,
2022-11-09 12:39:47 +01:00
dirty: bool,
2022-01-25 15:45:15 +01:00
render_timer_token: Option<RegistrationToken>,
2022-02-04 21:04:17 +01:00
fps: Fps,
2023-03-31 13:57:37 +02:00
feedback: HashMap<DrmNode, SurfaceDmabufFeedback>,
2022-01-25 15:45:15 +01:00
}
2023-03-07 16:37:11 +01:00
pub type GbmDrmCompositor = DrmCompositor<
GbmAllocator<DrmDeviceFd>,
GbmDevice<DrmDeviceFd>,
Option<OutputPresentationFeedback>,
DrmDeviceFd,
>;
2022-07-04 16:00:29 +02:00
pub fn init_backend(
dh: &DisplayHandle,
event_loop: &mut EventLoop<'static, Data>,
state: &mut State,
) -> Result<()> {
let (session, notifier) = LibSeatSession::new().context("Failed to acquire session")?;
2022-01-25 15:45:15 +01:00
let udev_backend = UdevBackend::new(session.seat())?;
2022-02-04 21:23:27 +01:00
let mut libinput_context =
2023-01-03 19:17:51 +01:00
Libinput::new_with_udev::<LibinputSessionInterface<LibSeatSession>>(session.clone().into());
2022-02-04 21:23:27 +01:00
libinput_context
.udev_assign_seat(&session.seat())
.map_err(|_| anyhow::anyhow!("Failed to assign seat to libinput"))?;
let libinput_backend = LibinputInputBackend::new(libinput_context.clone());
2022-01-25 15:45:15 +01:00
let libinput_event_source = event_loop
.handle()
.insert_source(libinput_backend, move |mut event, _, data| {
2022-04-25 23:00:30 +02:00
if let &mut InputEvent::DeviceAdded { ref mut device } = &mut event {
data.state.common.config.read_device(device);
2022-04-25 23:00:30 +02:00
}
2023-07-06 18:20:10 +02:00
data.state.process_input_event(event, true);
for output in data.state.common.shell.outputs() {
2022-11-03 18:51:27 +01:00
if let Err(err) = data.state.backend.kms().schedule_render(
&data.state.common.event_loop_handle,
output,
None,
2022-11-17 20:32:54 +01:00
None,
2022-11-03 18:51:27 +01:00
) {
error!(
?err,
"Error scheduling event loop for output {}.",
2022-07-04 16:00:29 +02:00
output.name(),
);
}
2022-01-25 15:45:15 +01:00
}
})
.map_err(|err| err.error)
.context("Failed to initialize libinput event source")?;
let api = GpuManager::new(GbmGlesBackend::<GlowRenderer>::default())
2022-09-28 12:01:29 +02:00
.context("Failed to initialize renderers")?;
2022-03-16 20:06:31 +01:00
// TODO get this info from system76-power, if available and setup a watcher
let primary = if let Some(path) = std::env::var("COSMIC_RENDER_DEVICE")
.ok()
.and_then(|x| DrmNode::from_path(x).ok())
{
path
} else {
let primary_node = primary_gpu(session.seat())
2022-03-16 20:06:31 +01:00
.ok()
.flatten()
.and_then(|x| DrmNode::from_path(x).ok());
primary_node
2022-03-16 20:06:31 +01:00
.and_then(|x| x.node_with_type(NodeType::Render).and_then(Result::ok))
.unwrap_or_else(|| {
for dev in all_gpus(session.seat()).expect("No GPU found") {
if let Some(node) = DrmNode::from_path(dev)
.ok()
.and_then(|x| x.node_with_type(NodeType::Render).and_then(Result::ok))
{
return node;
}
}
// If we find no render nodes, use primary node
if let Some(primary_node) = primary_node {
return primary_node;
} else {
panic!("Failed to initialize any GPU");
}
2022-03-16 20:06:31 +01:00
})
};
info!("Using {} as primary gpu for rendering.", primary);
2022-03-16 20:06:31 +01:00
let udev_dispatcher = Dispatcher::new(udev_backend, move |event, _, data: &mut Data| {
match match event {
2022-07-04 16:00:29 +02:00
UdevEvent::Added { device_id, path } => data
.state
2023-02-14 23:15:58 +01:00
.device_added(device_id, path, &data.display.handle(), true)
.with_context(|| format!("Failed to add drm device: {}", device_id)),
2022-07-04 16:00:29 +02:00
UdevEvent::Changed { device_id } => data
.state
.device_changed(device_id)
.with_context(|| format!("Failed to update drm device: {}", device_id)),
2022-07-04 16:00:29 +02:00
UdevEvent::Removed { device_id } => data
.state
.device_removed(device_id, &data.display.handle())
.with_context(|| format!("Failed to remove drm device: {}", device_id)),
} {
Ok(()) => {
trace!("Successfully handled udev event.")
}
Err(err) => {
error!(?err, "Error while handling udev event.")
}
}
});
let udev_event_source = event_loop
.handle()
.register_dispatcher(udev_dispatcher.clone())
.unwrap();
2022-05-03 13:37:51 +02:00
let handle = event_loop.handle();
2022-04-27 13:25:17 +02:00
let loop_signal = state.common.event_loop_signal.clone();
let dispatcher = udev_dispatcher.clone();
2023-01-03 19:17:51 +01:00
let session_event_source = event_loop
.handle()
.insert_source(notifier, move |event, &mut (), data| match event {
SessionEvent::ActivateSession => {
if let Err(err) = libinput_context.resume() {
error!(?err, "Failed to resume libinput context.");
2023-01-03 19:17:51 +01:00
}
2023-01-25 18:43:35 +01:00
for device in data.state.backend.kms().devices.values() {
2023-02-27 23:53:27 +01:00
device.drm.activate();
2023-01-25 18:43:35 +01:00
}
2023-01-03 19:17:51 +01:00
let dispatcher = dispatcher.clone();
handle.insert_idle(move |data| {
for (dev, path) in dispatcher.as_source_ref().device_list() {
let drm_node = match DrmNode::from_dev_id(dev) {
Ok(node) => node,
Err(err) => {
error!(?err, "Failed to read drm device {}.", path.display(),);
2023-01-03 19:17:51 +01:00
continue;
}
};
if data.state.backend.kms().devices.contains_key(&drm_node) {
if let Err(err) = data.state.device_changed(dev) {
error!(?err, "Failed to update drm device {}.", path.display(),);
2023-01-03 19:17:51 +01:00
}
} else {
if let Err(err) = data.state.device_added(
dev,
path.into(),
&data.display.handle(),
true,
) {
error!(?err, "Failed to add drm device {}.", path.display(),);
2023-01-03 19:17:51 +01:00
}
}
}
2023-01-03 19:17:51 +01:00
let seats = data.state.common.seats().cloned().collect::<Vec<_>>();
data.state.common.config.read_outputs(
&mut data.state.common.output_configuration_state,
&mut data.state.backend,
&mut data.state.common.shell,
seats.into_iter(),
2022-11-03 18:51:27 +01:00
&data.state.common.event_loop_handle,
2023-01-03 19:17:51 +01:00
);
for surface in data
.state
.backend
.kms()
.devices
.values_mut()
.flat_map(|d| d.surfaces.values_mut())
{
surface.scheduled = false;
2023-01-03 19:17:51 +01:00
surface.pending = false;
}
2023-01-03 19:17:51 +01:00
for output in data.state.common.shell.outputs() {
let sessions = output.pending_buffers().collect::<Vec<_>>();
if let Err(err) = data.state.backend.kms().schedule_render(
&data.state.common.event_loop_handle,
output,
None,
if !sessions.is_empty() {
Some(sessions)
} else {
None
},
) {
error!(
?err,
"Error scheduling event loop for output {}.",
2023-01-03 19:17:51 +01:00
output.name(),
);
}
}
});
loop_signal.wakeup();
}
SessionEvent::PauseSession => {
libinput_context.suspend();
for device in data.state.backend.kms().devices.values_mut() {
2023-02-27 23:53:27 +01:00
device.drm.pause();
for surface in device.surfaces.values_mut() {
surface.surface = None;
if let Some(token) = surface.render_timer_token.take() {
data.state.common.event_loop_handle.remove(token);
}
surface.scheduled = false;
}
}
2023-01-03 19:17:51 +01:00
}
})
.map_err(|err| err.error)
.context("Failed to initialize session event source")?;
2022-05-03 13:37:51 +02:00
2022-01-25 15:45:15 +01:00
state.backend = BackendData::Kms(KmsState {
2022-03-16 20:06:31 +01:00
api,
2022-05-03 13:37:51 +02:00
_tokens: vec![
libinput_event_source,
session_event_source,
udev_event_source,
],
2022-03-16 20:06:31 +01:00
primary,
2022-01-25 15:45:15 +01:00
session,
devices: HashMap::new(),
});
// Create relative pointer global
RelativePointerManagerState::new::<State>(&dh);
2023-01-18 20:23:41 +01:00
state.launch_xwayland(Some(primary));
for (dev, path) in udev_dispatcher.as_source_ref().device_list() {
2022-02-04 21:23:27 +01:00
state
2023-02-14 23:15:58 +01:00
.device_added(dev, path.into(), dh, false)
2022-01-25 15:45:15 +01:00
.with_context(|| format!("Failed to add drm device: {}", path.display()))?;
}
2023-02-14 23:15:58 +01:00
// HACK: amdgpu doesn't like us initializing vulkan too early..
// so lets do that delayed until mesa fixes that.
let devices = state
.backend
.kms()
.devices
.iter()
.map(|(drm_node, device)| (*drm_node, device.render_node))
.collect::<Vec<_>>();
2023-02-14 23:15:58 +01:00
for (drm_node, render_node) in devices {
state.init_vulkan(drm_node, render_node);
}
2022-01-25 15:45:15 +01:00
Ok(())
}
impl State {
fn device_added(
&mut self,
dev: dev_t,
path: PathBuf,
dh: &DisplayHandle,
try_vulkan: bool,
) -> Result<()> {
if !self.backend.kms().session.is_active() {
return Ok(());
}
2022-05-03 13:37:51 +02:00
let fd = DrmDeviceFd::new(unsafe {
DeviceFd::from_raw_fd(
self.backend
.kms()
.session
.open(
&path,
OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_NONBLOCK,
)
.with_context(|| {
format!(
"Failed to optain file descriptor for drm device: {}",
path.display()
2022-09-28 15:18:04 +02:00
)
})?,
)
});
2023-02-27 23:53:27 +01:00
let (drm, notifier) = DrmDevice::new(fd.clone(), false)
2022-01-25 15:45:15 +01:00
.with_context(|| format!("Failed to initialize drm device for: {}", path.display()))?;
2022-03-16 20:06:31 +01:00
let drm_node = DrmNode::from_dev_id(dev)?;
2022-01-25 15:45:15 +01:00
let supports_atomic = drm.is_atomic();
2022-03-16 20:06:31 +01:00
let gbm = GbmDevice::new(fd)
.with_context(|| format!("Failed to initialize GBM device for {}", path.display()))?;
2023-02-14 23:15:58 +01:00
let (render_node, formats) = {
let egl_display = EGLDisplay::new(gbm.clone()).with_context(|| {
2023-02-14 23:15:58 +01:00
format!("Failed to create EGLDisplay for device: {}", path.display())
})?;
let egl_device = EGLDevice::device_for_display(&egl_display).with_context(|| {
format!("Unable to find matching egl device for {}", path.display())
})?;
let render_node = egl_device
.try_get_render_node()
.ok()
.and_then(std::convert::identity)
.unwrap_or(drm_node);
let egl_context = EGLContext::new(&egl_display).with_context(|| {
2023-02-14 23:15:58 +01:00
format!(
"Failed to create EGLContext for device {:?}:{}",
egl_device,
path.display()
)
})?;
2023-03-31 13:57:37 +02:00
let formats = egl_context.dmabuf_texture_formats().clone();
2023-02-14 23:15:58 +01:00
(render_node, formats)
// NOTE: We need the to drop the EGL types here again,
// otherwise the EGLDisplay created below might share the same GBM context
};
2022-01-25 15:45:15 +01:00
2023-02-27 23:53:27 +01:00
let token = self
.common
.event_loop_handle
.insert_source(
notifier,
move |event, metadata, data: &mut Data| match event {
DrmEvent::VBlank(crtc) => {
let rescheduled = if let Some(device) =
data.state.backend.kms().devices.get_mut(&drm_node)
{
2022-11-09 12:39:47 +01:00
if let Some(surface) = device.surfaces.get_mut(&crtc) {
2022-11-18 17:20:52 +01:00
#[cfg(feature = "debug")]
surface.fps.displayed();
2022-11-09 12:39:47 +01:00
match surface.surface.as_mut().map(|x| x.frame_submitted()) {
2022-11-17 20:32:54 +01:00
Some(Ok(feedback)) => {
if let Some(mut feedback) = feedback.flatten() {
let submit_time =
match metadata.take().map(|data| data.time) {
Some(DrmEventTime::Monotonic(tp)) => Some(tp),
_ => None,
};
let seq = metadata
.as_ref()
.map(|metadata| metadata.sequence)
.unwrap_or(0);
let (clock, flags) = if let Some(tp) = submit_time {
(
tp.into(),
wp_presentation_feedback::Kind::Vsync
| wp_presentation_feedback::Kind::HwClock
| wp_presentation_feedback::Kind::HwCompletion,
)
} else {
(
data.state.common.clock.now(),
wp_presentation_feedback::Kind::Vsync,
)
};
feedback.presented(
clock,
surface
.output
.current_mode()
.map(|mode| mode.refresh as u32)
.unwrap_or_default(),
seq as u64,
flags,
);
}
2022-11-09 12:39:47 +01:00
surface.pending = false;
2023-05-12 20:01:37 +02:00
(surface.dirty
|| data.state.common.shell.animations_going())
.then(|| {
(surface.output.clone(), surface.fps.avg_rendertime(5))
})
2022-11-09 12:39:47 +01:00
}
Some(Err(err)) => {
warn!(?err, "Failed to submit frame.");
2022-11-09 12:39:47 +01:00
None
}
_ => None, // got disabled
}
2022-11-09 12:39:47 +01:00
} else {
None
}
} else {
None
};
2023-02-27 23:53:27 +01:00
if let Some((output, avg_rendertime)) = rescheduled {
let mut scheduled_sessions =
data.state.workspace_session_for_output(&output);
let mut output_sessions = output.pending_buffers().peekable();
if output_sessions.peek().is_some() {
scheduled_sessions
.get_or_insert_with(Vec::new)
.extend(output_sessions);
}
2022-11-09 12:39:47 +01:00
let estimated_rendertime =
std::cmp::max(avg_rendertime, MIN_RENDER_TIME);
2023-02-27 23:53:27 +01:00
if let Err(err) = data.state.backend.kms().schedule_render(
&data.state.common.event_loop_handle,
&output,
Some(estimated_rendertime),
2023-02-27 23:53:27 +01:00
scheduled_sessions,
) {
warn!(?err, "Failed to schedule render.");
}
2022-01-25 15:45:15 +01:00
}
}
2023-02-27 23:53:27 +01:00
DrmEvent::Error(err) => {
warn!(?err, "Failed to read events of device {:?}.", dev);
}
},
)
2022-01-25 15:45:15 +01:00
.with_context(|| format!("Failed to add drm device to event loop: {}", dev))?;
let socket = match self.create_socket(dh, render_node, formats.clone().into_iter()) {
Ok(socket) => Some(socket),
Err(err) => {
warn!(
?err,
"Failed to initialize hardware-acceleration for clients on {}.", render_node,
);
None
}
};
let allocator = Box::new(DmabufAllocator(GbmAllocator::new(
gbm.clone(),
GbmBufferFlags::RENDERING,
)));
2022-01-25 15:45:15 +01:00
let mut device = Device {
2022-03-16 20:06:31 +01:00
render_node,
2022-01-25 15:45:15 +01:00
surfaces: HashMap::new(),
2023-02-14 23:15:58 +01:00
gbm: gbm.clone(),
allocator,
2023-02-27 23:53:27 +01:00
drm,
2022-01-25 15:45:15 +01:00
formats,
supports_atomic,
event_token: Some(token),
socket,
2022-01-25 15:45:15 +01:00
};
let outputs = device.enumerate_surfaces()?.added; // There are no removed outputs on newly added devices
let mut wl_outputs = Vec::new();
2022-04-14 22:16:37 +02:00
let mut w = self.common.shell.global_space().size.w;
{
let backend = self.backend.kms();
2023-02-14 23:15:58 +01:00
backend
.api
.as_mut()
.add_node(render_node, gbm)
.with_context(|| {
format!(
"Failed to initialize renderer for device: {}, skipping",
render_node
)
})?;
2023-02-25 00:17:54 +01:00
let mut renderer = match backend.api.single_renderer(&render_node) {
Ok(renderer) => renderer,
Err(err) => {
warn!(?err, "Failed to initialize output.");
backend.api.as_mut().remove_node(&render_node);
return Ok(());
}
};
init_shaders(&mut renderer).expect("Failed to initialize renderer");
for (crtc, conn) in outputs {
match device.setup_surface(crtc, conn, (w, 0), &mut renderer) {
Ok(output) => {
w += output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow()
.mode_size()
.w;
wl_outputs.push(output);
}
Err(err) => warn!(?err, "Failed to initialize output."),
};
}
backend.devices.insert(drm_node, device);
2022-01-25 15:45:15 +01:00
}
2022-05-03 13:37:51 +02:00
self.common
.output_configuration_state
2022-07-04 16:00:29 +02:00
.add_heads(wl_outputs.iter());
let seats = self.common.seats().cloned().collect::<Vec<_>>();
2022-05-03 13:37:51 +02:00
self.common.config.read_outputs(
2022-11-11 21:36:42 +01:00
&mut self.common.output_configuration_state,
2022-05-03 13:37:51 +02:00
&mut self.backend,
&mut self.common.shell,
seats.into_iter(),
&self.common.event_loop_handle,
2022-05-03 13:37:51 +02:00
);
2022-01-25 15:45:15 +01:00
2023-02-14 23:15:58 +01:00
if try_vulkan {
self.init_vulkan(drm_node, render_node);
}
Ok(())
}
fn init_vulkan(&mut self, drm_node: DrmNode, render_node: DrmNode) {
if let Ok(instance) = Instance::new(Version::VERSION_1_2, None) {
if let Some(physical_device) =
PhysicalDevice::enumerate(&instance)
.ok()
.and_then(|devices| {
devices
.filter(|phd| {
phd.has_device_extension(unsafe {
CStr::from_bytes_with_nul_unchecked(
b"VK_EXT_physical_device_drm\0",
)
})
})
.find(|phd| {
phd.primary_node().unwrap() == Some(render_node)
|| phd.render_node().unwrap() == Some(render_node)
})
})
{
match VulkanAllocator::new(
&physical_device,
ImageUsageFlags::COLOR_ATTACHMENT | ImageUsageFlags::SAMPLED,
) {
Ok(allocator) => {
self.backend
.kms()
.devices
.get_mut(&drm_node)
.unwrap()
.allocator = Box::new(DmabufAllocator(allocator));
}
Err(err) => {
warn!(?err, "Failed to create vulkan allocator.");
}
}
}
}
2022-01-25 15:45:15 +01:00
}
fn device_changed(&mut self, dev: dev_t) -> Result<()> {
if !self.backend.kms().session.is_active() {
return Ok(());
}
2022-05-03 13:37:51 +02:00
2022-03-16 20:06:31 +01:00
let drm_node = DrmNode::from_dev_id(dev)?;
let mut outputs_removed = Vec::new();
let mut outputs_added = Vec::new();
{
let backend = self.backend.kms();
if let Some(device) = backend.devices.get_mut(&drm_node) {
let changes = device.enumerate_surfaces()?;
let mut w = self.common.shell.global_space().size.w;
for crtc in changes.removed {
if let Some(surface) = device.surfaces.remove(&crtc) {
if let Some(token) = surface.render_timer_token {
self.common.event_loop_handle.remove(token);
}
w -= surface.output.current_mode().map(|m| m.size.w).unwrap_or(0);
outputs_removed.push(surface.output.clone());
2022-01-25 15:45:15 +01:00
}
}
for (crtc, conn) in changes.added {
let mut renderer = match backend.api.single_renderer(&device.render_node) {
Ok(renderer) => renderer,
Err(err) => {
warn!(?err, "Failed to initialize output.");
continue;
}
};
match device.setup_surface(crtc, conn, (w, 0), &mut renderer) {
Ok(output) => {
w += output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow()
.mode_size()
.w;
outputs_added.push(output);
}
Err(err) => warn!(?err, "Failed to initialize output."),
};
}
2022-01-25 15:45:15 +01:00
}
}
2022-07-04 16:00:29 +02:00
self.common
.output_configuration_state
.remove_heads(outputs_removed.iter());
self.common
.output_configuration_state
.add_heads(outputs_added.iter());
let seats = self.common.seats().cloned().collect::<Vec<_>>();
2022-05-03 13:37:51 +02:00
self.common.config.read_outputs(
2022-11-11 21:36:42 +01:00
&mut self.common.output_configuration_state,
2022-05-03 13:37:51 +02:00
&mut self.backend,
&mut self.common.shell,
seats.iter().cloned(),
&self.common.event_loop_handle,
2022-05-03 13:37:51 +02:00
);
// 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.
// If we have 0 outputs at some point, we won't quit, but shell doesn't know where to move
// windows and workspaces to.
for output in outputs_removed {
self.common
.shell
.remove_output(&output, seats.iter().cloned());
}
2022-04-14 22:16:37 +02:00
2022-01-25 15:45:15 +01:00
Ok(())
}
fn device_removed(&mut self, dev: dev_t, dh: &DisplayHandle) -> Result<()> {
2022-03-16 20:06:31 +01:00
let drm_node = DrmNode::from_dev_id(dev)?;
let mut outputs_removed = Vec::new();
2023-02-14 23:15:58 +01:00
let backend = self.backend.kms();
if let Some(mut device) = backend.devices.remove(&drm_node) {
backend.api.as_mut().remove_node(&device.render_node);
2022-01-25 15:45:15 +01:00
for surface in device.surfaces.values_mut() {
if let Some(token) = surface.render_timer_token.take() {
self.common.event_loop_handle.remove(token);
}
outputs_removed.push(surface.output.clone());
2022-01-25 15:45:15 +01:00
}
if let Some(token) = device.event_token.take() {
self.common.event_loop_handle.remove(token);
}
if let Some(socket) = device.socket.take() {
self.common.event_loop_handle.remove(socket.token);
2022-07-04 16:00:29 +02:00
self.common
.dmabuf_state
.destroy_global::<State>(dh, socket.dmabuf_global);
2022-07-18 18:04:02 +02:00
dh.remove_global::<State>(socket.drm_global);
}
2022-01-25 15:45:15 +01:00
}
self.common
.output_configuration_state
2022-07-04 16:00:29 +02:00
.remove_heads(outputs_removed.iter());
let seats = self.common.seats().cloned().collect::<Vec<_>>();
if self.backend.kms().session.is_active() {
2022-08-11 17:13:56 +02:00
for output in outputs_removed {
self.common
.shell
.remove_output(&output, seats.iter().cloned());
2022-08-11 17:13:56 +02:00
}
2022-05-03 13:37:51 +02:00
self.common.config.read_outputs(
2022-11-11 21:36:42 +01:00
&mut self.common.output_configuration_state,
2022-05-03 13:37:51 +02:00
&mut self.backend,
&mut self.common.shell,
seats.into_iter(),
&self.common.event_loop_handle,
2022-05-03 13:37:51 +02:00
);
2022-11-11 21:36:42 +01:00
} else {
self.common.output_configuration_state.update();
}
2022-01-25 15:45:15 +01:00
Ok(())
}
}
pub struct OutputChanges {
pub added: Vec<(crtc::Handle, connector::Handle)>,
2022-02-04 21:23:27 +01:00
pub removed: Vec<crtc::Handle>,
2022-01-25 15:45:15 +01:00
}
impl Device {
pub fn enumerate_surfaces(&mut self) -> Result<OutputChanges> {
// enumerate our outputs
2023-02-27 23:53:27 +01:00
let config = drm_helpers::display_configuration(&mut self.drm, self.supports_atomic)?;
2022-01-25 15:45:15 +01:00
2022-02-04 21:23:27 +01:00
let surfaces = self
.surfaces
.iter()
.map(|(c, s)| (*c, s.connector))
.collect::<HashMap<crtc::Handle, connector::Handle>>();
2022-02-04 21:23:27 +01:00
let added = config
.iter()
.filter(|(conn, crtc)| surfaces.get(&crtc).map(|c| c != *conn).unwrap_or(true))
2022-01-25 15:45:15 +01:00
.map(|(conn, crtc)| (crtc, conn))
.map(|(crtc, conn)| (*crtc, *conn))
.collect::<Vec<_>>();
2022-02-04 21:23:27 +01:00
let removed = surfaces
.iter()
.filter(|(crtc, conn)| config.get(conn).map(|c| c != *crtc).unwrap_or(true))
2022-02-04 21:23:27 +01:00
.map(|(crtc, _)| *crtc)
2022-01-25 15:45:15 +01:00
.collect::<Vec<_>>();
2022-02-04 21:23:27 +01:00
Ok(OutputChanges { added, removed })
2022-01-25 15:45:15 +01:00
}
fn setup_surface(
&mut self,
crtc: crtc::Handle,
conn: connector::Handle,
2022-04-14 22:16:37 +02:00
position: (i32, i32),
renderer: &mut GlMultiRenderer<'_, '_>,
2022-01-25 15:45:15 +01:00
) -> Result<Output> {
2023-02-27 23:53:27 +01:00
let drm = &mut self.drm;
2022-01-25 15:45:15 +01:00
let crtc_info = drm.get_crtc(crtc)?;
2022-08-11 17:13:56 +02:00
let conn_info = drm.get_connector(conn, false)?;
2023-04-18 19:14:04 +02:00
let vrr = drm_helpers::set_vrr(drm, crtc, conn, false).unwrap_or(false);
2023-04-18 19:14:31 +02:00
let max_bpc = drm_helpers::get_max_bpc(drm, conn)?.map(|(_val, range)| range.end.min(16));
2022-02-01 13:59:39 +01:00
let interface = drm_helpers::interface_name(drm, conn)?;
let edid_info = drm_helpers::edid_info(drm, conn);
let mode = crtc_info.mode().unwrap_or_else(|| {
conn_info
.modes()
.iter()
.find(|mode| mode.mode_type().contains(ModeTypeFlags::PREFERRED))
.copied()
.unwrap_or(conn_info.modes()[0])
});
let refresh_rate = drm_helpers::calculate_refresh_rate(mode);
2022-01-25 15:45:15 +01:00
let output_mode = OutputMode {
size: (mode.size().0 as i32, mode.size().1 as i32).into(),
refresh: refresh_rate as i32,
2022-01-25 15:45:15 +01:00
};
let (phys_w, phys_h) = conn_info.size().unwrap_or((0, 0));
let output = Output::new(
2022-01-25 15:45:15 +01:00
interface,
PhysicalProperties {
size: (phys_w as i32, phys_h as i32).into(),
// TODO: We need to read that from the connector properties
subpixel: Subpixel::Unknown,
make: edid_info
.as_ref()
.map(|info| info.manufacturer.clone())
.unwrap_or_else(|_| String::from("Unknown")),
model: edid_info
.as_ref()
.map(|info| info.model.clone())
.unwrap_or_else(|_| String::from("Unknown")),
2022-01-25 15:45:15 +01:00
},
);
for mode in conn_info.modes() {
let refresh_rate = drm_helpers::calculate_refresh_rate(*mode);
let mode = OutputMode {
size: (mode.size().0 as i32, mode.size().1 as i32).into(),
refresh: refresh_rate as i32,
};
output.add_mode(mode);
}
output.set_preferred(output_mode);
2022-01-25 15:45:15 +01:00
output.change_current_state(
Some(output_mode),
2022-02-04 21:23:27 +01:00
// TODO: Readout property for monitor rotation
Some(Transform::Normal),
2022-01-25 15:45:15 +01:00
None,
2022-04-14 22:16:37 +02:00
Some(position.into()),
2022-01-25 15:45:15 +01:00
);
output.user_data().insert_if_missing(|| {
RefCell::new(OutputConfig {
mode: ((output_mode.size.w, output_mode.size.h), Some(refresh_rate)),
vrr,
2022-04-14 22:16:37 +02:00
position,
2023-04-18 19:14:31 +02:00
max_bpc,
..Default::default()
})
});
2022-01-25 15:45:15 +01:00
let data = Surface {
output: output.clone(),
2022-04-14 22:16:37 +02:00
surface: None,
connector: conn,
2022-01-25 15:45:15 +01:00
vrr,
refresh_rate,
scheduled: false,
2022-04-14 22:16:37 +02:00
pending: false,
2022-11-09 12:39:47 +01:00
dirty: false,
render_timer_token: None,
fps: Fps::new(renderer.as_mut()),
2023-03-31 13:57:37 +02:00
feedback: HashMap::new(),
2022-01-25 15:45:15 +01:00
};
self.surfaces.insert(crtc, data);
Ok(output)
}
}
2023-03-31 13:57:37 +02:00
pub fn source_node_for_surface(w: &WlSurface, dh: &DisplayHandle) -> Option<DrmNode> {
// Lets check the global drm-node the client got either through default-feedback or wl_drm
let client = dh.get_client(w.id()).ok()?;
if let Some(normal_client) = client.get_data::<ClientState>() {
return normal_client.drm_node.clone();
}
// last but not least all xwayland-surfaces should also share a single node
if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() {
return xwayland_client.user_data().get::<DrmNode>().cloned();
}
None
}
2022-07-04 16:00:29 +02:00
fn render_node_for_output(
dh: &DisplayHandle,
output: &Output,
target_node: DrmNode,
shell: &Shell,
) -> DrmNode {
let workspace = shell.active_space(output);
2022-07-04 16:00:29 +02:00
let nodes = workspace
.get_fullscreen(output)
2022-09-28 12:01:29 +02:00
.map(|w| vec![w.clone()])
.unwrap_or_else(|| workspace.windows().collect::<Vec<_>>())
2022-07-04 16:00:29 +02:00
.into_iter()
2023-03-31 13:57:37 +02:00
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s, dh)))
2022-07-04 16:00:29 +02:00
.collect::<Vec<_>>();
if nodes.contains(&target_node) || nodes.is_empty() {
2022-07-04 16:00:29 +02:00
target_node
} else {
nodes
.iter()
.fold(HashMap::new(), |mut count_map, node| {
let count = count_map.entry(node).or_insert(0);
*count += 1;
count_map
})
2022-07-04 16:00:29 +02:00
.into_iter()
.reduce(|a, b| if a.1 > b.1 { a } else { b })
.map(|(node, _)| *node)
.unwrap_or(target_node)
}
}
2023-03-31 13:57:37 +02:00
fn get_surface_dmabuf_feedback(
render_node: DrmNode,
render_formats: HashSet<Format>,
target_formats: HashSet<Format>,
compositor: &GbmDrmCompositor,
) -> SurfaceDmabufFeedback {
let combined_formats = render_formats
.intersection(&target_formats)
.copied()
.collect::<HashSet<_>>();
let surface = compositor.surface();
let planes = surface.planes().unwrap();
// We limit the scan-out trache to formats we can also render from
// so that there is always a fallback render path available in case
// the supplied buffer can not be scanned out directly
let planes_formats = surface
.supported_formats(planes.primary.handle)
.unwrap()
.into_iter()
.chain(
planes
.overlay
.iter()
.flat_map(|p| surface.supported_formats(p.handle).unwrap()),
)
.collect::<HashSet<_>>()
.intersection(&combined_formats)
.copied()
.collect::<Vec<_>>();
let target_node = surface.device_fd().dev_id().unwrap();
let mut builder = DmabufFeedbackBuilder::new(render_node.dev_id(), render_formats);
if target_node != render_node.dev_id() && !combined_formats.is_empty() {
builder = builder.add_preference_tranche(target_node, None, combined_formats);
};
let render_feedback = builder.clone().build().unwrap();
let scanout_feedback = builder
.add_preference_tranche(
target_node,
Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout),
planes_formats,
)
.build()
.unwrap();
SurfaceDmabufFeedback {
render_feedback,
scanout_feedback,
}
}
impl Surface {
pub fn render_output(
&mut self,
api: &mut GpuManager<GbmGlesBackend<GlowRenderer>>,
render_node: Option<(
&DrmNode,
&mut dyn Allocator<Buffer = Dmabuf, Error = AnyError>,
)>,
target_node: &DrmNode,
state: &mut Common,
2022-11-03 18:51:27 +01:00
screencopy: Option<&[(ScreencopySession, BufferParams)]>,
) -> Result<()> {
2023-03-07 16:37:11 +01:00
#[cfg(feature = "debug")]
puffin::profile_function!();
if self.surface.is_none() {
return Ok(());
}
2023-03-07 16:37:11 +01:00
let compositor = self.surface.as_mut().unwrap();
let (render_node, mut renderer) = match render_node {
Some((render_node, allocator)) => (
render_node,
2023-03-07 16:37:11 +01:00
api.renderer(&render_node, &target_node, allocator, compositor.format())
.unwrap(),
),
None => (target_node, api.single_renderer(&target_node).unwrap()),
};
2023-03-07 22:20:44 +01:00
self.fps.start();
#[cfg(feature = "debug")]
if let Some(rd) = self.fps.rd.as_mut() {
rd.start_frame_capture(
renderer.glow_renderer().egl_context().get_context_handle(),
std::ptr::null(),
);
}
let (previous_workspace, workspace) = state.shell.workspaces.active(&self.output);
let (previous_idx, idx) = state.shell.workspaces.active_num(&self.output);
let previous_workspace = previous_workspace
.zip(previous_idx)
.map(|((w, start), idx)| (w.handle, idx, start));
let workspace = (workspace.handle, idx);
2023-03-07 22:20:44 +01:00
let elements = workspace_elements(
Some(&render_node),
&mut renderer,
2022-02-04 21:04:17 +01:00
state,
2022-01-25 15:45:15 +01:00
&self.output,
previous_workspace,
workspace,
2022-11-03 18:51:27 +01:00
CursorMode::All,
2023-03-07 16:37:11 +01:00
&mut Some(&mut self.fps),
false,
2023-03-07 22:20:44 +01:00
)
.map_err(|err| {
anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err)
})?;
2023-03-07 16:37:11 +01:00
self.fps.elements();
2023-05-02 17:24:18 +02:00
let res =
compositor.render_frame::<_, _, GlesTexture>(&mut renderer, &elements, CLEAR_COLOR);
2023-03-07 16:37:11 +01:00
self.fps.render();
match res {
Ok(frame_result) => {
2023-05-02 17:24:18 +02:00
let feedback = if frame_result.damage.is_some() {
Some(state.take_presentation_feedback(&self.output, &frame_result.states))
} else {
None
};
match compositor.queue_frame(feedback) {
Ok(()) | Err(FrameError::EmptyFrame) => {}
Err(err) => {
return Err(err).with_context(|| "Failed to submit result for display")
}
};
2023-03-07 16:37:11 +01:00
if let Some(screencopy) = screencopy {
for (session, params) in screencopy {
match render_session(
Some(*render_node),
&mut renderer,
&session,
params,
self.output.current_transform(),
2023-03-31 14:04:47 +02:00
|_node, buffer, renderer, dt, age| {
let res = dt.damage_output(age, &elements)?;
2023-03-07 16:37:11 +01:00
2023-06-28 22:20:06 +02:00
let mut sync = SyncPoint::default();
2023-03-07 16:37:11 +01:00
if let (Some(ref damage), _) = &res {
if let Ok(dmabuf) = get_dmabuf(buffer) {
renderer.bind(dmabuf).map_err(RenderError::Rendering)?;
} else {
2023-05-02 17:24:18 +02:00
let size = buffer_dimensions(buffer).ok_or(
RenderError::Rendering(MultiError::ImportFailed),
)?;
let format =
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
.map_err(|_| OutputNoMode)? // eh, we have to do some error
.expect("We should be able to convert all hardcoded shm screencopy formats");
2023-03-07 16:37:11 +01:00
let render_buffer =
Offscreen::<GlesRenderbuffer>::create_buffer(
renderer, format, size,
2023-03-07 16:37:11 +01:00
)
.map_err(RenderError::Rendering)?;
renderer
.bind(render_buffer)
.map_err(RenderError::Rendering)?;
}
let (output_size, output_scale, output_transform) = (
self.output.current_mode().ok_or(OutputNoMode)?.size,
self.output.current_scale().fractional_scale(),
self.output.current_transform(),
);
2023-06-28 22:20:06 +02:00
sync = frame_result
2023-05-02 17:24:18 +02:00
.blit_frame_result(
output_size,
output_transform,
2023-03-07 16:37:11 +01:00
output_scale,
2023-05-02 17:24:18 +02:00
renderer,
damage.iter().copied(),
// TODO: Filter cursor element
elements.iter().map(|e| e.id().clone()),
2023-03-07 16:37:11 +01:00
)
2023-05-02 17:24:18 +02:00
.map_err(|err| match err {
BlitFrameResultError::Rendering(err) => {
RenderError::Rendering(err)
}
BlitFrameResultError::Export(_) => {
RenderError::Rendering(MultiError::DeviceMissing)
}
})?;
2023-03-07 16:37:11 +01:00
}
2023-06-28 22:20:06 +02:00
Ok(RenderOutputResult {
damage: res.0,
states: res.1,
sync,
})
2023-03-07 16:37:11 +01:00
},
) {
Ok(true) => {} // success
Ok(false) => state.still_pending(session.clone(), params.clone()),
Err(err) => {
warn!(?err, "Error rendering to screencopy session.");
session.failed(FailureReason::Unspec);
}
}
}
self.fps.screencopy();
}
2023-03-31 13:57:37 +02:00
state.send_frames(&self.output, &frame_result.states, |source_node| {
Some(
self.feedback
.entry(source_node)
.or_insert_with(|| {
let render_formats = api
.single_renderer(&source_node)
.unwrap()
.dmabuf_formats()
.collect::<HashSet<_>>();
let target_formats = api
.single_renderer(target_node)
.unwrap()
.dmabuf_formats()
.collect::<HashSet<_>>();
get_surface_dmabuf_feedback(
source_node,
render_formats,
target_formats,
compositor,
)
})
.clone(),
)
});
2022-01-25 15:45:15 +01:00
}
Err(err) => {
2023-03-07 16:37:11 +01:00
compositor.reset_buffers();
2022-01-25 15:45:15 +01:00
anyhow::bail!("Rendering failed: {}", err);
}
2023-03-07 16:37:11 +01:00
}
2022-01-25 15:45:15 +01:00
Ok(())
}
2022-01-20 19:51:46 +01:00
}
impl KmsState {
2022-05-03 13:37:51 +02:00
pub fn switch_vt(&mut self, num: i32) -> Result<(), anyhow::Error> {
2022-04-25 12:35:55 +02:00
self.session.change_vt(num).map_err(Into::into)
}
pub fn apply_config_for_output(
&mut self,
output: &Output,
seats: impl Iterator<Item = Seat<State>>,
shell: &mut Shell,
test_only: bool,
loop_handle: &LoopHandle<'_, Data>,
) -> Result<(), anyhow::Error> {
let recreated = if let Some(device) = self
.devices
.values_mut()
.find(|dev| dev.surfaces.values().any(|s| s.output == *output))
{
2023-05-19 19:47:59 +02:00
let (crtc, surface) = device
.surfaces
.iter_mut()
.find(|(_, s)| s.output == *output)
.unwrap();
let output_config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
if !output_config.enabled {
if !test_only {
shell.remove_output(output, seats);
if surface.surface.take().is_some() {
// just drop it
surface.pending = false;
2022-11-11 21:36:42 +01:00
surface.dirty = false;
}
}
false
} else {
2023-02-27 23:53:27 +01:00
let drm = &mut device.drm;
let conn = surface.connector;
2022-08-11 17:13:56 +02:00
let conn_info = drm.get_connector(conn, false)?;
let mode = conn_info
.modes()
.iter()
2022-07-19 14:41:04 +02:00
// match the size
.filter(|mode| {
let (x, y) = mode.size();
Size::from((x as i32, y as i32)) == output_config.mode_size()
})
// and then select the closest refresh rate (e.g. to match 59.98 as 60)
.min_by_key(|mode| {
let refresh_rate = drm_helpers::calculate_refresh_rate(**mode);
(output_config.mode.1.unwrap() as i32 - refresh_rate as i32).abs()
})
.ok_or(anyhow::anyhow!("Unknown mode"))?;
if !test_only {
2023-03-07 16:37:11 +01:00
let res = if let Some(compositor) = surface.surface.as_mut() {
if output_config.vrr != surface.vrr {
surface.vrr = drm_helpers::set_vrr(
drm,
*crtc,
conn_info.handle(),
output_config.vrr,
)?;
}
2023-03-07 16:37:11 +01:00
compositor.use_mode(*mode).unwrap();
false
} else {
surface.vrr = drm_helpers::set_vrr(drm, *crtc, conn, output_config.vrr)
.unwrap_or(false);
2023-04-18 19:14:31 +02:00
if let Some(bpc) = output_config.max_bpc {
if let Err(err) = drm_helpers::set_max_bpc(drm, conn, bpc) {
warn!(
?bpc,
?err,
"Failed to set max_bpc on connector: {}",
output.name()
);
}
}
surface.refresh_rate = drm_helpers::calculate_refresh_rate(*mode);
2023-01-03 19:17:51 +01:00
let drm_surface = drm.create_surface(*crtc, *mode, &[conn])?;
2023-03-07 16:37:11 +01:00
let driver = drm
.get_driver()
.with_context(|| "Failed to query drm driver")?;
let mut planes = drm_surface
.planes()
.with_context(|| "Failed to query drm planes")?;
// QUIRK: Using an overlay plane on a nvidia card breaks the display controller (wtf...)
if driver
.name()
.to_string_lossy()
.to_lowercase()
.contains("nvidia")
{
planes.overlay = vec![];
}
let target = DrmCompositor::new(
&surface.output,
drm_surface,
2023-03-07 16:37:11 +01:00
Some(planes),
GbmAllocator::new(
device.gbm.clone(),
GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT,
),
2023-03-07 16:37:11 +01:00
device.gbm.clone(),
&[
Fourcc::Abgr2101010,
Fourcc::Argb2101010,
Fourcc::Abgr8888,
Fourcc::Argb8888,
],
device.formats.clone(),
2023-03-07 16:37:11 +01:00
drm.cursor_size(),
Some(device.gbm.clone()),
)
.with_context(|| {
format!(
2023-03-07 16:37:11 +01:00
"Failed to initialize drm surface for {}",
drm_helpers::interface_name(drm, conn)
.unwrap_or_else(|_| String::from("Unknown"))
)
})?;
surface.surface = Some(target);
true
2022-11-11 21:36:42 +01:00
};
shell.add_output(output);
res
} else {
false
}
}
} else {
false
};
2022-04-20 16:06:37 +02:00
shell.refresh_outputs();
if recreated {
2022-11-03 18:51:27 +01:00
let sessions = output.pending_buffers().collect::<Vec<_>>();
if let Err(err) = self.schedule_render(
loop_handle,
output,
2022-11-17 20:32:54 +01:00
None,
2022-11-03 18:51:27 +01:00
if !sessions.is_empty() {
Some(sessions)
} else {
None
},
) {
error!(
?err,
"Error scheduling event loop for output {}.",
2022-07-04 16:00:29 +02:00
output.name(),
);
}
}
Ok(())
2022-03-30 13:47:06 +02:00
}
pub fn target_node_for_output(&self, output: &Output) -> Option<DrmNode> {
2022-07-04 16:00:29 +02:00
self.devices
2022-08-03 16:41:47 +02:00
.values()
.find(|dev| dev.surfaces.values().any(|s| s.output == *output))
.map(|dev| &dev.render_node)
2022-07-04 16:00:29 +02:00
.copied()
}
2022-07-04 16:00:29 +02:00
pub fn try_early_import(
&mut self,
dh: &DisplayHandle,
surface: &WlSurface,
output: &Output,
target: DrmNode,
shell: &Shell,
) {
let render = render_node_for_output(dh, &output, target, &shell);
if let Err(err) = self.api.early_import(
2023-01-24 19:32:57 +01:00
if let Some(client) = dh.get_client(surface.id()).ok() {
if let Some(normal_client) = client.get_data::<ClientState>() {
normal_client.drm_node.clone()
} else if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() {
xwayland_client.user_data().get::<DrmNode>().cloned()
} else {
None
}
} else {
None
},
render,
surface,
) {
trace!(?err, "Early import failed.");
}
}
2022-08-31 13:01:23 +02:00
pub fn dmabuf_imported(&mut self, global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<()> {
for device in self.devices.values() {
2022-07-04 16:00:29 +02:00
if device
.socket
.as_ref()
.map(|s| &s.dmabuf_global == global)
.unwrap_or(false)
{
return self
.api
.single_renderer(&device.render_node)?
.import_dmabuf(&dmabuf, None)
.map(|_| ())
.map_err(Into::into);
}
}
unreachable!()
}
pub fn schedule_render(
&mut self,
loop_handle: &LoopHandle<'_, Data>,
output: &Output,
estimated_rendertime: Option<Duration>,
2022-11-03 18:51:27 +01:00
mut screencopy_sessions: Option<Vec<(ScreencopySession, BufferParams)>>,
) -> Result<(), InsertError<Timer>> {
if let Some((device, crtc, surface)) = self
2022-02-04 21:23:27 +01:00
.devices
2022-03-16 20:06:31 +01:00
.iter_mut()
.flat_map(|(node, d)| d.surfaces.iter_mut().map(move |(c, s)| (node, c, s)))
.find(|(_, _, s)| s.output == *output)
2022-01-25 15:45:15 +01:00
{
if surface.surface.is_none() {
2022-12-03 00:06:20 +01:00
if let Some(sessions) = screencopy_sessions {
loop_handle.insert_idle(move |data| {
for (session, params) in sessions.into_iter() {
data.state.common.still_pending(session, params);
}
});
}
return Ok(());
}
if !surface.scheduled {
let device = *device;
let crtc = *crtc;
2022-08-17 17:14:01 +02:00
if let Some(token) = surface.render_timer_token.take() {
loop_handle.remove(token);
}
surface.render_timer_token = Some(loop_handle.insert_source(
if surface.vrr || estimated_rendertime.is_none() {
2022-11-17 20:32:54 +01:00
Timer::immediate()
} else {
Timer::from_duration(
Duration::from_secs_f64(1000.0 / surface.refresh_rate as f64)
.saturating_sub(estimated_rendertime.unwrap()),
)
2022-11-17 20:32:54 +01:00
},
move |_time, _, data| {
let backend = data.state.backend.kms();
let (mut device, mut other) = backend
.devices
.iter_mut()
.partition::<Vec<_>, _>(|(key, _val)| *key == &device);
let target_device = &mut device[0].1;
if let Some(surface) = target_device.surfaces.get_mut(&crtc) {
let target_node = target_device.render_node;
let render_node = render_node_for_output(
&data.display.handle(),
&surface.output,
target_node,
&data.state.common.shell,
);
let state = &mut data.state.common;
let result = if render_node != target_node {
let render_device = &mut other
.iter_mut()
.find(|(_, val)| val.render_node == render_node)
.unwrap()
.1;
surface.render_output(
&mut backend.api,
Some((
&render_device.render_node,
render_device.allocator.as_mut(),
)),
&target_node,
state,
2022-11-03 18:51:27 +01:00
screencopy_sessions.as_deref(),
)
} else {
surface.render_output(
&mut backend.api,
None,
&target_node,
state,
screencopy_sessions.as_deref(),
)
};
match result {
Ok(_) => {
surface.dirty = false;
surface.pending = true;
surface.scheduled = false;
return TimeoutAction::Drop;
}
Err(err) => {
if backend.session.is_active() {
error!(?err, "Error rendering.");
return TimeoutAction::ToDuration(Duration::from_secs_f64(
(1000.0 / surface.refresh_rate as f64) - 0.003,
));
2022-11-03 18:51:27 +01:00
}
}
};
2022-12-03 00:06:20 +01:00
}
if let Some(sessions) = screencopy_sessions.as_mut() {
for (session, params) in sessions.drain(..) {
data.state.common.still_pending(session, params);
}
}
TimeoutAction::Drop
},
)?);
surface.scheduled = true;
2022-11-09 12:39:47 +01:00
} else {
2022-12-03 00:06:20 +01:00
if let Some(sessions) = screencopy_sessions {
loop_handle.insert_idle(|data| {
for (session, params) in sessions.into_iter() {
data.state.common.still_pending(session, params);
}
});
}
2022-11-09 12:39:47 +01:00
surface.dirty = true;
2022-01-25 15:45:15 +01:00
}
}
Ok(())
}
2022-02-04 21:23:27 +01:00
}