2022-01-20 19:51:46 +01:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
|
|
2022-02-04 21:04:17 +01:00
|
|
|
#[cfg(feature = "debug")]
|
|
|
|
|
use crate::state::Fps;
|
2022-03-16 20:06:31 +01:00
|
|
|
|
2022-01-25 15:45:15 +01:00
|
|
|
use crate::{
|
2022-02-04 21:04:17 +01:00
|
|
|
backend::render,
|
2022-01-25 15:45:15 +01:00
|
|
|
state::{BackendData, Common, State},
|
|
|
|
|
utils::GlobalDrop,
|
|
|
|
|
};
|
2022-02-04 21:23:27 +01:00
|
|
|
|
2022-01-25 15:45:15 +01:00
|
|
|
use anyhow::{Context, Result};
|
|
|
|
|
use smithay::{
|
|
|
|
|
backend::{
|
|
|
|
|
allocator::{gbm::GbmDevice, Format},
|
2022-03-16 20:06:31 +01:00
|
|
|
drm::{DrmDevice, DrmEvent, DrmEventTime, DrmNode, GbmBufferedSurface, NodeType},
|
2022-02-04 21:23:27 +01:00
|
|
|
egl::{EGLContext, EGLDevice, EGLDisplay},
|
2022-01-25 15:45:15 +01:00
|
|
|
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
2022-03-16 20:06:31 +01:00
|
|
|
renderer::{
|
|
|
|
|
multigpu::{egl::EglGlesBackend, GpuManager},
|
|
|
|
|
Bind,
|
|
|
|
|
},
|
|
|
|
|
session::{auto::AutoSession, Session, Signal},
|
|
|
|
|
udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent},
|
2022-01-25 15:45:15 +01:00
|
|
|
},
|
|
|
|
|
reexports::{
|
2022-02-04 21:23:27 +01:00
|
|
|
calloop::{
|
|
|
|
|
timer::{Timer, TimerHandle},
|
|
|
|
|
Dispatcher, EventLoop, LoopHandle, RegistrationToken,
|
2022-01-25 15:45:15 +01:00
|
|
|
},
|
2022-02-04 21:23:27 +01:00
|
|
|
drm::control::{connector, crtc, Device as ControlDevice},
|
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},
|
|
|
|
|
wayland_server::{protocol::wl_output, Display},
|
2022-01-25 15:45:15 +01:00
|
|
|
},
|
2022-02-04 21:23:27 +01:00
|
|
|
utils::signaling::{Linkable, Signaler},
|
|
|
|
|
wayland::output::{Mode as OutputMode, Output, PhysicalProperties},
|
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 std::{
|
|
|
|
|
cell::RefCell,
|
|
|
|
|
collections::{HashMap, HashSet},
|
|
|
|
|
path::PathBuf,
|
|
|
|
|
rc::Rc,
|
|
|
|
|
time::{Duration, Instant, SystemTime},
|
|
|
|
|
};
|
2022-01-20 19:51:46 +01:00
|
|
|
|
2022-02-01 13:59:39 +01:00
|
|
|
mod drm_helpers;
|
2022-01-25 15:45:15 +01:00
|
|
|
mod session_fd;
|
|
|
|
|
use session_fd::*;
|
|
|
|
|
|
|
|
|
|
pub struct KmsState {
|
2022-03-16 20:06:31 +01:00
|
|
|
devices: HashMap<DrmNode, Device>,
|
|
|
|
|
api: GpuManager<EglGlesBackend>,
|
|
|
|
|
primary: DrmNode,
|
2022-01-25 15:45:15 +01:00
|
|
|
session: AutoSession,
|
|
|
|
|
signaler: Signaler<Signal>,
|
|
|
|
|
tokens: Vec<RegistrationToken>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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>,
|
|
|
|
|
allocator: Rc<RefCell<GbmDevice<SessionFd>>>,
|
|
|
|
|
drm: Dispatcher<'static, DrmDevice<SessionFd>, State>,
|
|
|
|
|
formats: HashSet<Format>,
|
|
|
|
|
supports_atomic: bool,
|
|
|
|
|
event_token: Option<RegistrationToken>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Surface {
|
|
|
|
|
surface: GbmBufferedSurface<Rc<RefCell<GbmDevice<SessionFd>>>, SessionFd>,
|
|
|
|
|
output: Output,
|
|
|
|
|
_global: GlobalDrop<wl_output::WlOutput>,
|
|
|
|
|
last_submit: Option<DrmEventTime>,
|
|
|
|
|
refresh_rate: u32,
|
|
|
|
|
vrr: bool,
|
|
|
|
|
pending: bool,
|
2022-03-16 20:06:31 +01:00
|
|
|
render_timer: TimerHandle<(DrmNode, crtc::Handle)>,
|
2022-01-25 15:45:15 +01:00
|
|
|
render_timer_token: Option<RegistrationToken>,
|
2022-02-04 21:04:17 +01:00
|
|
|
#[cfg(feature = "debug")]
|
|
|
|
|
fps: Fps,
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 19:51:46 +01:00
|
|
|
pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Result<()> {
|
2022-01-25 15:45:15 +01:00
|
|
|
let (session, notifier) = AutoSession::new(None).context("Failed to acquire session")?;
|
|
|
|
|
let signaler = notifier.signaler();
|
|
|
|
|
|
|
|
|
|
let udev_backend = UdevBackend::new(session.seat(), None)?;
|
2022-02-04 21:23:27 +01:00
|
|
|
let mut libinput_context =
|
|
|
|
|
Libinput::new_with_udev::<LibinputSessionInterface<AutoSession>>(session.clone().into());
|
|
|
|
|
libinput_context
|
|
|
|
|
.udev_assign_seat(&session.seat())
|
|
|
|
|
.map_err(|_| anyhow::anyhow!("Failed to assign seat to libinput"))?;
|
2022-01-25 15:45:15 +01:00
|
|
|
let mut libinput_backend = LibinputInputBackend::new(libinput_context, None);
|
|
|
|
|
libinput_backend.link(signaler.clone());
|
|
|
|
|
|
|
|
|
|
let libinput_event_source = event_loop
|
|
|
|
|
.handle()
|
|
|
|
|
.insert_source(libinput_backend, move |event, _, state| {
|
|
|
|
|
state.common.process_input_event(event);
|
|
|
|
|
for output in state.common.spaces.outputs() {
|
|
|
|
|
state.backend.kms().schedule_render(output);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.map_err(|err| err.error)
|
|
|
|
|
.context("Failed to initialize libinput event source")?;
|
|
|
|
|
let session_event_source = event_loop
|
|
|
|
|
.handle()
|
|
|
|
|
.insert_source(notifier, |(), &mut (), _state| {})
|
|
|
|
|
.map_err(|err| err.error)
|
|
|
|
|
.context("Failed to initialize session event source")?;
|
|
|
|
|
|
2022-03-16 20:06:31 +01:00
|
|
|
let api = GpuManager::new(EglGlesBackend, None).context("Failed to initialize renderers")?;
|
|
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
|
primary_gpu(session.seat())
|
|
|
|
|
.ok()
|
|
|
|
|
.flatten()
|
|
|
|
|
.and_then(|x| DrmNode::from_path(x).ok())
|
|
|
|
|
.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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
panic!("Failed to initialize any GPU");
|
|
|
|
|
})
|
|
|
|
|
};
|
|
|
|
|
slog_scope::info!("Using {} as primary gpu for rendering", primary);
|
|
|
|
|
|
2022-01-25 15:45:15 +01:00
|
|
|
state.backend = BackendData::Kms(KmsState {
|
2022-03-16 20:06:31 +01:00
|
|
|
api,
|
2022-02-04 21:23:27 +01:00
|
|
|
tokens: vec![libinput_event_source, session_event_source],
|
2022-03-16 20:06:31 +01:00
|
|
|
primary,
|
2022-01-25 15:45:15 +01:00
|
|
|
session,
|
|
|
|
|
signaler,
|
|
|
|
|
devices: HashMap::new(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (dev, path) in udev_backend.device_list() {
|
2022-02-04 21:23:27 +01:00
|
|
|
state
|
|
|
|
|
.device_added(dev, path.into())
|
2022-01-25 15:45:15 +01:00
|
|
|
.with_context(|| format!("Failed to add drm device: {}", path.display()))?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let udev_event_source = event_loop
|
|
|
|
|
.handle()
|
2022-02-04 21:23:27 +01:00
|
|
|
.insert_source(udev_backend, move |event, _, state| {
|
|
|
|
|
match match event {
|
|
|
|
|
UdevEvent::Added { device_id, path } => state
|
|
|
|
|
.device_added(device_id, path)
|
2022-01-25 15:45:15 +01:00
|
|
|
.with_context(|| format!("Failed to add drm device: {}", device_id)),
|
2022-02-04 21:23:27 +01:00
|
|
|
UdevEvent::Changed { device_id } => state
|
|
|
|
|
.device_changed(device_id)
|
2022-01-25 15:45:15 +01:00
|
|
|
.with_context(|| format!("Failed to update drm device: {}", device_id)),
|
2022-02-04 21:23:27 +01:00
|
|
|
UdevEvent::Removed { device_id } => state
|
|
|
|
|
.device_removed(device_id)
|
2022-01-25 15:45:15 +01:00
|
|
|
.with_context(|| format!("Failed to remove drm device: {}", device_id)),
|
2022-02-04 21:23:27 +01:00
|
|
|
} {
|
|
|
|
|
Ok(()) => {
|
|
|
|
|
slog_scope::debug!("Successfully handled udev event")
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
slog_scope::error!("Error while handling udev event: {}", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.unwrap();
|
2022-01-25 15:45:15 +01:00
|
|
|
state.backend.kms().tokens.push(udev_event_source);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl State {
|
|
|
|
|
fn device_added(&mut self, dev: dev_t, path: PathBuf) -> Result<()> {
|
2022-02-04 21:23:27 +01:00
|
|
|
let fd = SessionFd::new(
|
|
|
|
|
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-01-25 15:45:15 +01:00
|
|
|
let mut drm = DrmDevice::new(fd.clone(), false, None)
|
|
|
|
|
.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()))?;
|
|
|
|
|
let egl_display = EGLDisplay::new(&gbm, None).with_context(|| {
|
|
|
|
|
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)
|
2022-02-04 21:23:27 +01:00
|
|
|
.with_context(|| {
|
2022-03-16 20:06:31 +01:00
|
|
|
format!(
|
|
|
|
|
"Failed to determine path of egl device for {}",
|
|
|
|
|
path.display()
|
|
|
|
|
)
|
2022-02-04 21:23:27 +01:00
|
|
|
})?;
|
|
|
|
|
let egl_context = EGLContext::new(&egl_display, None).with_context(|| {
|
|
|
|
|
format!(
|
|
|
|
|
"Failed to create EGLContext for device {:?}:{}",
|
|
|
|
|
egl_device,
|
|
|
|
|
path.display()
|
|
|
|
|
)
|
|
|
|
|
})?;
|
2022-01-25 15:45:15 +01:00
|
|
|
let formats = egl_context.dmabuf_render_formats().clone();
|
|
|
|
|
|
|
|
|
|
drm.link(self.backend.kms().signaler.clone());
|
2022-02-04 21:23:27 +01:00
|
|
|
let dispatcher =
|
|
|
|
|
Dispatcher::new(drm, move |event, metadata, state: &mut State| match event {
|
2022-01-25 15:45:15 +01:00
|
|
|
DrmEvent::VBlank(crtc) => {
|
2022-03-16 20:06:31 +01:00
|
|
|
if let Some(device) = state.backend.kms().devices.get_mut(&drm_node) {
|
2022-01-25 15:45:15 +01:00
|
|
|
if let Some(surface) = device.surfaces.get_mut(&crtc) {
|
|
|
|
|
match surface.surface.frame_submitted() {
|
|
|
|
|
Ok(_) => {
|
|
|
|
|
surface.last_submit = metadata.take().map(|data| data.time);
|
|
|
|
|
surface.pending = false;
|
2022-02-04 21:23:27 +01:00
|
|
|
state
|
|
|
|
|
.common
|
|
|
|
|
.spaces
|
|
|
|
|
.active_space_mut(&surface.output)
|
|
|
|
|
.send_frames(
|
|
|
|
|
state.common.start_time.elapsed().as_millis() as u32,
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-01-25 15:45:15 +01:00
|
|
|
Err(err) => slog_scope::warn!("Failed to submit frame: {}", err),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-04 21:23:27 +01:00
|
|
|
}
|
2022-01-25 15:45:15 +01:00
|
|
|
DrmEvent::Error(err) => {
|
|
|
|
|
slog_scope::warn!("Failed to read events of device {:?}: {}", dev, err);
|
|
|
|
|
}
|
2022-02-04 21:23:27 +01:00
|
|
|
});
|
|
|
|
|
let token = self
|
|
|
|
|
.common
|
|
|
|
|
.event_loop_handle
|
|
|
|
|
.register_dispatcher(dispatcher.clone())
|
2022-01-25 15:45:15 +01:00
|
|
|
.with_context(|| format!("Failed to add drm device to event loop: {}", dev))?;
|
|
|
|
|
|
|
|
|
|
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(),
|
|
|
|
|
allocator: Rc::new(RefCell::new(gbm)),
|
|
|
|
|
drm: dispatcher,
|
|
|
|
|
formats,
|
|
|
|
|
supports_atomic,
|
|
|
|
|
event_token: Some(token),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let outputs = device.enumerate_surfaces()?.added; // There are no removed outputs on newly added devices
|
|
|
|
|
for (crtc, conn) in outputs {
|
2022-02-04 21:23:27 +01:00
|
|
|
match device.setup_surface(
|
2022-03-16 20:06:31 +01:00
|
|
|
&drm_node,
|
2022-02-04 21:23:27 +01:00
|
|
|
crtc,
|
|
|
|
|
conn,
|
|
|
|
|
self.backend.kms().signaler.clone(),
|
|
|
|
|
&mut self.common.display.borrow_mut(),
|
|
|
|
|
&mut self.common.event_loop_handle,
|
|
|
|
|
) {
|
2022-01-25 15:45:15 +01:00
|
|
|
Ok(output) => self.common.spaces.map_output(&output),
|
|
|
|
|
Err(err) => slog_scope::warn!("Failed to initialize output: {}", err),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-16 20:06:31 +01:00
|
|
|
self.backend.kms().devices.insert(drm_node, device);
|
2022-01-25 15:45:15 +01:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn device_changed(&mut self, dev: dev_t) -> Result<()> {
|
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 signaler = self.backend.kms().signaler.clone();
|
2022-03-16 20:06:31 +01:00
|
|
|
if let Some(device) = self.backend.kms().devices.get_mut(&drm_node) {
|
2022-01-25 15:45:15 +01:00
|
|
|
let changes = device.enumerate_surfaces()?;
|
2022-02-04 21:23:27 +01:00
|
|
|
for crtc in changes.removed {
|
2022-01-25 15:45:15 +01:00
|
|
|
if let Some(surface) = device.surfaces.get_mut(&crtc) {
|
|
|
|
|
if let Some(token) = surface.render_timer_token.take() {
|
|
|
|
|
self.common.event_loop_handle.remove(token);
|
|
|
|
|
}
|
|
|
|
|
self.common.spaces.unmap_output(&surface.output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (crtc, conn) in changes.added {
|
2022-02-04 21:23:27 +01:00
|
|
|
match device.setup_surface(
|
2022-03-16 20:06:31 +01:00
|
|
|
&drm_node,
|
2022-02-04 21:23:27 +01:00
|
|
|
crtc,
|
|
|
|
|
conn,
|
|
|
|
|
signaler.clone(),
|
|
|
|
|
&mut self.common.display.borrow_mut(),
|
|
|
|
|
&mut self.common.event_loop_handle,
|
|
|
|
|
) {
|
2022-01-25 15:45:15 +01:00
|
|
|
Ok(output) => self.common.spaces.map_output(&output),
|
|
|
|
|
Err(err) => slog_scope::warn!("Failed to initialize output: {}", err),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn device_removed(&mut self, dev: dev_t) -> Result<()> {
|
2022-03-16 20:06:31 +01:00
|
|
|
let drm_node = DrmNode::from_dev_id(dev)?;
|
|
|
|
|
if let Some(device) = self.backend.kms().devices.get_mut(&drm_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);
|
|
|
|
|
}
|
|
|
|
|
self.common.spaces.unmap_output(&surface.output);
|
|
|
|
|
}
|
|
|
|
|
if let Some(token) = device.event_token.take() {
|
|
|
|
|
self.common.event_loop_handle.remove(token);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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> {
|
|
|
|
|
let drm = &mut *self.drm.as_source_mut();
|
|
|
|
|
|
|
|
|
|
// enumerate our outputs
|
2022-02-01 13:59:39 +01:00
|
|
|
let config = drm_helpers::display_configuration(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.surface.current_connectors().into_iter().next()))
|
|
|
|
|
.collect::<HashMap<crtc::Handle, Option<connector::Handle>>>();
|
|
|
|
|
|
|
|
|
|
let added = config
|
|
|
|
|
.iter()
|
2022-03-16 20:06:31 +01:00
|
|
|
.filter(|(conn, crtc)| {
|
|
|
|
|
surfaces
|
|
|
|
|
.get(&crtc)
|
|
|
|
|
.map(|c| c.as_ref() != Some(*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()
|
2022-03-16 20:06:31 +01:00
|
|
|
.filter(|(crtc, conn)| {
|
|
|
|
|
if let Some(conn) = conn {
|
|
|
|
|
config.get(conn).map(|c| c != *crtc).unwrap_or(true)
|
|
|
|
|
} else {
|
|
|
|
|
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,
|
2022-03-16 20:06:31 +01:00
|
|
|
drm_node: &DrmNode,
|
2022-01-25 15:45:15 +01:00
|
|
|
crtc: crtc::Handle,
|
|
|
|
|
conn: connector::Handle,
|
|
|
|
|
signaler: Signaler<Signal>,
|
|
|
|
|
display: &mut Display,
|
|
|
|
|
loop_handle: &mut LoopHandle<'static, State>,
|
|
|
|
|
) -> Result<Output> {
|
|
|
|
|
let drm = &mut *self.drm.as_source_mut();
|
|
|
|
|
let crtc_info = drm.get_crtc(crtc)?;
|
|
|
|
|
let conn_info = drm.get_connector(conn)?;
|
2022-02-04 21:23:27 +01:00
|
|
|
let vrr = drm_helpers::set_vrr(drm, crtc, conn, true).unwrap_or(false);
|
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)?;
|
2022-01-25 15:45:15 +01:00
|
|
|
let mode = crtc_info.mode().unwrap_or(conn_info.modes()[0]);
|
|
|
|
|
let mut surface = drm.create_surface(crtc, mode, &[conn])?;
|
|
|
|
|
surface.link(signaler);
|
|
|
|
|
|
2022-02-04 21:23:27 +01:00
|
|
|
let target =
|
|
|
|
|
GbmBufferedSurface::new(surface, self.allocator.clone(), self.formats.clone(), None)
|
|
|
|
|
.with_context(|| format!("Failed to initialize Gbm surface for {}", interface))?;
|
2022-01-25 15:45:15 +01:00
|
|
|
|
2022-02-04 21:23:27 +01:00
|
|
|
//let is_nvidia = driver(drm.device_id()).ok().flatten().map(|x| x == "nvidia").unwrap_or(false);
|
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: (mode.vrefresh() * 1000) as i32,
|
|
|
|
|
};
|
|
|
|
|
let (phys_w, phys_h) = conn_info.size().unwrap_or((0, 0));
|
|
|
|
|
let (output, output_global) = Output::new(
|
|
|
|
|
display,
|
|
|
|
|
interface,
|
|
|
|
|
PhysicalProperties {
|
|
|
|
|
size: (phys_w as i32, phys_h as i32).into(),
|
|
|
|
|
// TODO: We need to read that from the connector properties
|
|
|
|
|
subpixel: wl_output::Subpixel::Unknown,
|
|
|
|
|
make: edid_info.manufacturer,
|
|
|
|
|
model: edid_info.model,
|
|
|
|
|
},
|
2022-02-04 21:23:27 +01:00
|
|
|
None,
|
2022-01-25 15:45:15 +01:00
|
|
|
);
|
|
|
|
|
output.set_preferred(output_mode.clone());
|
|
|
|
|
output.change_current_state(
|
|
|
|
|
Some(output_mode),
|
2022-02-04 21:23:27 +01:00
|
|
|
// TODO: Readout property for monitor rotation
|
|
|
|
|
Some(wl_output::Transform::Normal),
|
2022-01-25 15:45:15 +01:00
|
|
|
None,
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let timer = Timer::new()?;
|
|
|
|
|
let timer_handle = timer.handle();
|
|
|
|
|
// render timer
|
|
|
|
|
let timer_token = loop_handle
|
|
|
|
|
.insert_source(timer, |(dev_id, crtc), _, state| {
|
2022-03-16 20:06:31 +01:00
|
|
|
let backend = state.backend.kms();
|
|
|
|
|
if let Some(device) = backend.devices.get_mut(&dev_id) {
|
2022-01-25 15:45:15 +01:00
|
|
|
if let Some(surface) = device.surfaces.get_mut(&crtc) {
|
2022-03-22 12:36:03 +01:00
|
|
|
if let Err(err) = surface.render_output(&mut backend.api, &backend.primary, &device.render_node, &mut state.common) {
|
2022-01-25 15:45:15 +01:00
|
|
|
slog_scope::error!("Error rendering: {}", err);
|
|
|
|
|
// TODO re-schedule?
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.unwrap();
|
2022-03-16 20:06:31 +01:00
|
|
|
timer_handle.add_timeout(Duration::ZERO, (*drm_node, crtc));
|
2022-01-25 15:45:15 +01:00
|
|
|
|
|
|
|
|
let data = Surface {
|
|
|
|
|
output: output.clone(),
|
|
|
|
|
_global: output_global.into(),
|
|
|
|
|
surface: target,
|
|
|
|
|
vrr,
|
2022-02-01 13:59:39 +01:00
|
|
|
refresh_rate: drm_helpers::calculate_refresh_rate(mode),
|
2022-01-25 15:45:15 +01:00
|
|
|
last_submit: None,
|
|
|
|
|
pending: true,
|
|
|
|
|
render_timer: timer_handle,
|
|
|
|
|
render_timer_token: Some(timer_token),
|
2022-02-04 21:04:17 +01:00
|
|
|
#[cfg(feature = "debug")]
|
|
|
|
|
fps: Fps::default(),
|
2022-01-25 15:45:15 +01:00
|
|
|
};
|
|
|
|
|
self.surfaces.insert(crtc, data);
|
|
|
|
|
|
|
|
|
|
Ok(output)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Surface {
|
|
|
|
|
pub fn render_output(
|
|
|
|
|
&mut self,
|
2022-03-22 12:36:03 +01:00
|
|
|
api: &mut GpuManager<EglGlesBackend>,
|
|
|
|
|
render_node: &DrmNode,
|
|
|
|
|
target_node: &DrmNode,
|
2022-01-25 15:45:15 +01:00
|
|
|
state: &mut Common,
|
|
|
|
|
) -> Result<()> {
|
2022-03-22 12:36:03 +01:00
|
|
|
let mut renderer = api
|
|
|
|
|
.renderer(render_node, target_node)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2022-01-25 15:45:15 +01:00
|
|
|
let (buffer, age) = self
|
|
|
|
|
.surface
|
|
|
|
|
.next_buffer()
|
|
|
|
|
.with_context(|| "Failed to allocate buffer")?;
|
2022-02-04 21:04:17 +01:00
|
|
|
|
2022-01-25 15:45:15 +01:00
|
|
|
renderer
|
|
|
|
|
.bind(buffer)
|
|
|
|
|
.with_context(|| "Failed to bind buffer")?;
|
2022-02-04 21:04:17 +01:00
|
|
|
|
|
|
|
|
match render::render_output(
|
2022-03-22 12:36:03 +01:00
|
|
|
Some(&render_node),
|
|
|
|
|
&mut renderer,
|
2022-02-04 21:04:17 +01:00
|
|
|
age,
|
|
|
|
|
state,
|
2022-01-25 15:45:15 +01:00
|
|
|
&self.output,
|
2022-02-04 21:04:17 +01:00
|
|
|
false,
|
|
|
|
|
#[cfg(feature = "debug")]
|
2022-03-16 20:06:31 +01:00
|
|
|
&mut self.fps,
|
2022-01-25 15:45:15 +01:00
|
|
|
) {
|
|
|
|
|
Ok(_) => {
|
|
|
|
|
self.surface
|
|
|
|
|
.queue_buffer()
|
|
|
|
|
.with_context(|| "Failed to submit buffer for display")?;
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
self.surface.reset_buffers();
|
|
|
|
|
anyhow::bail!("Rendering failed: {}", err);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2022-01-20 19:51:46 +01:00
|
|
|
}
|
2022-01-28 18:54:25 +01:00
|
|
|
|
|
|
|
|
impl KmsState {
|
|
|
|
|
pub fn schedule_render(&mut self, output: &Output) {
|
2022-02-04 21:23:27 +01:00
|
|
|
if let Some((device, surface)) = self
|
|
|
|
|
.devices
|
2022-03-16 20:06:31 +01:00
|
|
|
.iter_mut()
|
|
|
|
|
.flat_map(|(node, d)| d.surfaces.values_mut().map(move |s| (node, s)))
|
2022-01-25 15:45:15 +01:00
|
|
|
.find(|(_, s)| s.output == *output)
|
|
|
|
|
{
|
|
|
|
|
if !surface.pending {
|
|
|
|
|
surface.pending = true;
|
2022-03-16 20:06:31 +01:00
|
|
|
let duration = surface
|
|
|
|
|
.last_submit
|
|
|
|
|
.as_ref()
|
|
|
|
|
.and_then(|x| match x {
|
|
|
|
|
DrmEventTime::Monotonic(instant) => {
|
|
|
|
|
instant.checked_duration_since(Instant::now())
|
|
|
|
|
}
|
|
|
|
|
DrmEventTime::Realtime(time) => time.duration_since(SystemTime::now()).ok(),
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(Duration::ZERO); // + Duration::from_secs_f64((1.0 / surface.refresh_rate as f64) - 20.0);
|
|
|
|
|
let data = (*device, surface.surface.crtc());
|
2022-02-04 21:23:27 +01:00
|
|
|
if surface.vrr {
|
2022-01-25 15:45:15 +01:00
|
|
|
surface.render_timer.add_timeout(Duration::ZERO, data);
|
|
|
|
|
} else {
|
2022-02-04 21:23:27 +01:00
|
|
|
surface.render_timer.add_timeout(duration, data);
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-28 18:54:25 +01:00
|
|
|
}
|
2022-02-04 21:23:27 +01:00
|
|
|
}
|