From e18be6521063ced6412d0e1a773259d099816333 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 13 Mar 2022 19:01:43 +0100 Subject: [PATCH] autocommit 2022-03-13 19:01:43 CET --- i4config/src/lib.rs | 2 - src/backends/metal.rs | 6 +- src/backends/metal/input.rs | 22 +- src/backends/metal/monitor.rs | 6 + src/backends/metal/video.rs | 432 +++++++++++++++++++++++------ src/drm/drm.rs | 45 +-- src/drm/drm/sys.rs | 27 ++ src/drm/gbm.rs | 5 +- src/drm/mod.rs | 1 + src/libinput/event.rs | 11 +- src/libinput/sys.rs | 9 +- src/render/renderer/framebuffer.rs | 12 + 12 files changed, 460 insertions(+), 118 deletions(-) diff --git a/i4config/src/lib.rs b/i4config/src/lib.rs index 13824a6f..7fc82139 100644 --- a/i4config/src/lib.rs +++ b/i4config/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(thread_local_const_init)] - use crate::keyboard::keymap::Keymap; use crate::keyboard::ModifiedKeySym; use bincode::{Decode, Encode}; diff --git a/src/backends/metal.rs b/src/backends/metal.rs index df914d67..7d0cd2ae 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -43,6 +43,8 @@ pub enum MetalError { UnexpectedTermination, #[error("Could not create GBM device")] GbmDevice(#[source] GbmError), + #[error("Could not update the drm properties")] + UpdateProperties(#[source] DrmError), #[error("Could not create a render context")] CreateRenderContex(#[source] RenderError), #[error("Cannot initialize connector because no CRTC is available")] @@ -57,8 +59,8 @@ pub enum MetalError { Framebuffer(#[source] DrmError), #[error("Could not import a framebuffer into EGL")] ImportFb(#[source] RenderError), - #[error("Could not configure connector chain")] - Configure(#[source] DrmError), + #[error("Could not perform modeset")] + Modeset(#[source] DrmError), #[error("Could not enable atomic modesetting")] AtomicModesetting(#[source] OsError), #[error("Could not inspect a plane")] diff --git a/src/backends/metal/input.rs b/src/backends/metal/input.rs index 7cf8ccca..5f31a36b 100644 --- a/src/backends/metal/input.rs +++ b/src/backends/metal/input.rs @@ -1,6 +1,9 @@ use crate::async_engine::FdStatus; use crate::backend::{InputEvent, KeyState, ScrollAxis}; -use crate::libinput::consts::{LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_KEY_STATE_PRESSED, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL}; +use crate::libinput::consts::{ + LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_KEY_STATE_PRESSED, + LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, +}; use crate::libinput::event::LibInputEvent; use crate::metal::MetalBackend; use crate::ErrorFmt; @@ -101,17 +104,26 @@ impl MetalBackend { const PX_PER_SCROLL: f64 = 15.0; const ONE_TWENTRY: f64 = 120.0; let (event, dev) = unpack!(self, event, pointer_event); - let hscroll = event.scroll_value_v120(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL) / ONE_TWENTRY + dev.hscroll.get(); - let vscroll = event.scroll_value_v120(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL) / ONE_TWENTRY + dev.vscroll.get(); + let hscroll = event.scroll_value_v120(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL) + / ONE_TWENTRY + + dev.hscroll.get(); + let vscroll = event.scroll_value_v120(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL) / ONE_TWENTRY + + dev.vscroll.get(); let hscroll_used = (PX_PER_SCROLL * hscroll).round(); let vscroll_used = (PX_PER_SCROLL * vscroll).round(); dev.hscroll.set(hscroll - hscroll_used / PX_PER_SCROLL); dev.vscroll.set(vscroll - vscroll_used / PX_PER_SCROLL); if hscroll_used != 0.0 { - dev.event(InputEvent::Scroll(hscroll_used as i32, ScrollAxis::Horizontal)); + dev.event(InputEvent::Scroll( + hscroll_used as i32, + ScrollAxis::Horizontal, + )); } if vscroll_used != 0.0 { - dev.event(InputEvent::Scroll(vscroll_used as i32, ScrollAxis::Vertical)); + dev.event(InputEvent::Scroll( + vscroll_used as i32, + ScrollAxis::Vertical, + )); } } diff --git a/src/backends/metal/monitor.rs b/src/backends/metal/monitor.rs index 3badcece..9ce8d92e 100644 --- a/src/backends/metal/monitor.rs +++ b/src/backends/metal/monitor.rs @@ -11,6 +11,7 @@ use crate::ErrorFmt; use bstr::ByteSlice; use std::cell::Cell; use std::rc::Rc; +use std::time::Instant; use uapi::{c, OwnedFd}; const DRM: &[u8] = b"drm"; @@ -84,6 +85,11 @@ impl MetalBackend { fn handle_drm_device_resume(self: &Rc, dev: &Rc, _fd: Rc) { log::info!("Device resumed: {}", dev.dev.devnode.to_bytes().as_bstr()); + let start = Instant::now(); + if let Err(e) = self.resume_drm_device(dev) { + log::error!("Could not resume drm device: {}", ErrorFmt(e)); + } + log::info!("resume took {:?}", start.elapsed()); } fn handle_input_device_resume(self: &Rc, dev: &Rc, fd: Rc) { diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 3970b01e..cefaa7a7 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -1,19 +1,26 @@ use crate::async_engine::{AsyncFd, SpawnedFuture}; use crate::backend::{BackendEvent, Output, OutputId}; -use crate::drm::drm::{ConnectorStatus, ConnectorType, DrmBlob, DrmConnector, DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFb, DrmFramebuffer, DrmMaster, DrmModeInfo, DrmObject, DrmPlane, DrmProperty, DrmPropertyDefinition, DrmPropertyType, PropBlob, DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT, Change}; +use crate::drm::drm::{ + drm_mode_modeinfo, Change, ConnectorStatus, ConnectorType, DrmBlob, DrmConnector, DrmCrtc, + DrmEncoder, DrmError, DrmEvent, DrmFramebuffer, DrmMaster, DrmModeInfo, DrmObject, DrmPlane, + DrmProperty, DrmPropertyDefinition, DrmPropertyType, PropBlob, DRM_CLIENT_CAP_ATOMIC, + DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT, +}; use crate::drm::gbm::{GbmDevice, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT}; use crate::drm::{ModifiedFormat, INVALID_MODIFIER}; use crate::format::{Format, XRGB8888}; use crate::metal::{DrmId, MetalBackend, MetalError}; use crate::render::{Framebuffer, RenderContext}; use crate::utils::bitflags::BitflagsExt; +use crate::utils::oserror::OsError; use crate::{CloneCell, ErrorFmt, NumCell, State}; -use ahash::AHashMap; +use ahash::{AHashMap, AHashSet}; use bstr::{BString, ByteSlice}; use std::cell::Cell; use std::ffi::CString; use std::fmt::{Debug, Formatter}; use std::rc::Rc; +use std::time::Instant; use uapi::c; pub struct PendingDrmDevice { @@ -62,6 +69,8 @@ pub struct MetalConnector { pub id: DrmConnector, pub master: Rc, + pub active: Cell, + pub output_id: OutputId, pub crtcs: AHashMap>, @@ -80,11 +89,9 @@ pub struct MetalConnector { pub subpixel: u32, pub primary_plane: CloneCell>>, - pub cursor_plane: Cell, pub crtc_id: MutableProperty, - - pub egl_fb: CloneCell>>, + pub crtc: CloneCell>>, pub on_change: OnChange, } @@ -143,6 +150,7 @@ pub struct MetalCrtc { pub active: MutableProperty, pub mode_id: MutableProperty, + pub out_fence_ptr: DrmProperty, pub mode_blob: CloneCell>>, } @@ -170,7 +178,6 @@ pub struct MetalPlane { pub possible_crtcs: u32, pub formats: AHashMap, - pub fb_id: MutableProperty, pub crtc_id: MutableProperty, pub crtc_x: MutableProperty, pub crtc_y: MutableProperty, @@ -180,10 +187,10 @@ pub struct MetalPlane { pub src_y: MutableProperty, pub src_w: MutableProperty, pub src_h: MutableProperty, + pub in_fence_fd: DrmProperty, + pub fb_id: DrmProperty, } -impl MetalDrmDevice {} - fn get_connectors( state: &State, dev: &MetalDrmDeviceStatic, @@ -219,10 +226,11 @@ fn create_connector( Ok(MetalConnector { id: connector, master: dev.master.clone(), + active: Cell::new(false), output_id: state.output_ids.next(), crtcs, + mode: CloneCell::new(info.modes.first().cloned().map(|m| Rc::new(m))), modes: info.modes, - mode: Default::default(), buffers: Default::default(), next_buffer: Default::default(), connector_type: info.connector_type.into(), @@ -232,9 +240,8 @@ fn create_connector( mm_height: info.mm_height, subpixel: info.subpixel, primary_plane: Default::default(), - cursor_plane: Default::default(), crtc_id: props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _)), - egl_fb: Default::default(), + crtc: Default::default(), on_change: Default::default(), }) } @@ -279,6 +286,7 @@ fn create_crtc( connector: Default::default(), active: props.get("ACTIVE")?.map(|v| v == 1), mode_id: props.get("MODE_ID")?.map(|v| DrmBlob(v as u32)), + out_fence_ptr: props.get("OUT_FENCE_PTR")?.id, mode_blob: Default::default(), }) } @@ -327,7 +335,7 @@ fn create_plane(plane: DrmPlane, master: &Rc) -> Result) -> Result( Ok(CollectedProperties { props }) } +fn collect_untyped_properties( + master: &Rc, + t: T, +) -> Result, DrmError> { + let mut props = AHashMap::new(); + for prop in master.get_properties(t)? { + props.insert(prop.id, prop.value); + } + Ok(props) +} + struct CollectedProperties { props: AHashMap, } @@ -464,14 +484,7 @@ impl MetalBackend { let slf = Rc::new(MetalDrmDevice { dev, connectors }); - let mut changes = master.change(DRM_MODE_ATOMIC_ALLOW_MODESET); - - self.reset_drm_device(&slf, &mut changes); - self.init_drm_device(&slf, &mut changes); - - if let Err(e) = changes.commit(0) { - return Err(MetalError::Configure(e)); - } + self.init_drm_device(&slf)?; for connector in slf.connectors.values() { if connector.primary_plane.get().is_some() { @@ -490,6 +503,47 @@ impl MetalBackend { Ok(slf) } + fn update_device_properties(&self, dev: &Rc) -> Result<(), DrmError> { + let get = |p: &AHashMap, k: DrmProperty| match p.get(&k) { + Some(v) => Ok(*v), + _ => todo!(), + }; + let master = &dev.dev.master; + for c in dev.connectors.values() { + let props = collect_untyped_properties(master, c.id)?; + c.crtc_id + .value + .set(DrmCrtc(get(&props, c.crtc_id.id)? as _)); + } + for c in dev.dev.crtcs.values() { + let props = collect_untyped_properties(master, c.id)?; + c.active.value.set(get(&props, c.active.id)? != 0); + c.mode_id + .value + .set(DrmBlob(get(&props, c.mode_id.id)? as _)); + } + for c in dev.dev.planes.values() { + let props = collect_untyped_properties(master, c.id)?; + c.crtc_id + .value + .set(DrmCrtc(get(&props, c.crtc_id.id)? as _)); + } + Ok(()) + } + + pub fn resume_drm_device(self: &Rc, dev: &Rc) -> Result<(), MetalError> { + if let Err(e) = self.update_device_properties(dev) { + return Err(MetalError::UpdateProperties(e)); + } + self.init_drm_device(dev)?; + for connector in dev.connectors.values() { + if connector.primary_plane.get().is_some() { + self.present(connector); + } + } + Ok(()) + } + async fn handle_drm_events(self: Rc, dev: Rc) { loop { if let Err(e) = dev.dev.async_fd.readable().await { @@ -539,60 +593,222 @@ impl MetalBackend { self.present(&connector); } - fn reset_drm_device(&self, dev: &MetalDrmDevice, changes: &mut Change) { + fn reuse_primary_planes(&self, dev: &MetalDrmDevice) -> AHashSet { + let mut crtc_primary_planes = AHashMap::new(); for connector in dev.connectors.values() { - if connector.crtc_id.value.take().is_some() { - changes.change_object(connector.id, |c| { - c.change(connector.crtc_id.id, 0); - }) + connector.active.set(false); + connector.primary_plane.set(None); + if let Some(crtc) = connector.crtc.get() { + crtc_primary_planes.insert(crtc.id, vec![]); } } for plane in dev.dev.planes.values() { - changes.change_object(plane.id, |c| { - if plane.crtc_id.value.take().is_some() { - c.change(plane.crtc_id.id, 0); + if plane.ty == PlaneType::Primary { + if let Some(ncp) = crtc_primary_planes.get_mut(&plane.crtc_id.value.get()) { + ncp.push(plane.clone()); } - if plane.fb_id.value.take().is_some() { - c.change(plane.fb_id.id, 0); - } - }) + } } - for crtc in dev.dev.crtcs.values() { - changes.change_object(crtc.id, |c| { - if crtc.active.value.take() { - c.change(crtc.active.id, 0); + let mut reuse_possible = true; + for planes in crtc_primary_planes.values() { + if planes.len() > 1 { + reuse_possible = false; + break; + } + if let Some(plane) = planes.first() { + if !plane.formats.contains_key(&XRGB8888.drm) { + reuse_possible = false; + break; } - if crtc.mode_id.value.take().is_some() { - c.change(crtc.mode_id.id, 0); + } + } + let mut preserve = AHashSet::new(); + if !reuse_possible { + log::warn!("Not reusing primary planes"); + return preserve; + } + for connector in dev.connectors.values() { + if let Some(planes) = crtc_primary_planes.get(&connector.crtc_id.value.get()) { + if let Some(plane) = planes.first() { + connector.primary_plane.set(Some(plane.clone())); + preserve.insert(plane.id); } + } + } + preserve + } + + fn reset_planes(&self, dev: &MetalDrmDevice, changes: &mut Change, preserve: &AHashSet) { + for plane in dev.dev.planes.values() { + if preserve.contains(&plane.id) { + continue; + } + plane.crtc_id.value.set(DrmCrtc::NONE); + changes.change_object(plane.id, |c| { + c.change(plane.crtc_id.id, 0); + c.change(plane.fb_id, 0); + c.change(plane.in_fence_fd, -1i32 as u64); }) } } - pub fn init_drm_device(&self, dev: &Rc, changes: &mut Change) { + fn reset_connectors_and_crtcs(&self, dev: &MetalDrmDevice, changes: &mut Change) { for connector in dev.connectors.values() { - if let Err(e) = self.init_drm_connector(dev, connector, changes) { - log::error!("Could not initialize drm connector: {}", ErrorFmt(e)); + connector.primary_plane.set(None); + connector.crtc.set(None); + connector.crtc_id.value.set(DrmCrtc::NONE); + changes.change_object(connector.id, |c| { + c.change(connector.crtc_id.id, 0); + }) + } + for crtc in dev.dev.crtcs.values() { + crtc.connector.set(None); + crtc.active.value.set(false); + crtc.mode_id.value.set(DrmBlob::NONE); + changes.change_object(crtc.id, |c| { + c.change(crtc.active.id, 0); + c.change(crtc.mode_id.id, 0); + c.change(crtc.out_fence_ptr, 0); + }) + } + } + + fn init_drm_device(&self, dev: &Rc) -> Result<(), MetalError> { + let mut flags = 0; + let mut changes = dev.dev.master.change(); + if !self.can_use_current_drm_mode(dev) { + log::warn!("Cannot use existing connector configuration. Trying to perform modeset."); + flags = DRM_MODE_ATOMIC_ALLOW_MODESET; + self.reset_connectors_and_crtcs(dev, &mut changes); + for connector in dev.connectors.values() { + if let Err(e) = self.assign_connector_crtc(connector, &mut changes) { + log::error!("Could not assign a crtc: {}", ErrorFmt(e)); + } } } + let preserve = self.reuse_primary_planes(dev); + self.reset_planes(dev, &mut changes, &preserve); + { + let mut connector: Vec<_> = dev.connectors.values().collect(); + connector.sort_by_key(|k| { + if k.primary_plane.get().is_some() { + 0 + } else { + 1 + } + }); + for connector in connector { + if let Err(e) = self.assign_connector_plane(dev, connector, &mut changes) { + log::error!("Could not assign a plane: {}", ErrorFmt(e)); + } + } + } + for connector in dev.connectors.values() { + if !connector.active.get() { + connector.primary_plane.set(None); + } + } + let mut start = Instant::now(); + if let Err(e) = changes.commit(flags, 0) { + return Err(MetalError::Modeset(e)); + } + log::info!("commit 2: {:?}", start.elapsed()); + Ok(()) + } + + fn can_use_current_drm_mode(&self, dev: &Rc) -> bool { + let mut used_crtcs = AHashSet::new(); + let mut used_planes = AHashSet::new(); + + for connector in dev.connectors.values() { + if connector.connection != ConnectorStatus::Connected { + if connector.crtc_id.value.get().is_some() { + log::debug!("Connector is not connected but has an assigned crtc"); + return false; + } + continue; + } + let crtc_id = connector.crtc_id.value.get(); + if crtc_id.is_none() { + log::debug!("Connector is connected but has no assigned crtc"); + return false; + } + used_crtcs.insert(crtc_id); + let crtc = dev.dev.crtcs.get(&crtc_id).unwrap(); + connector.crtc.set(Some(crtc.clone())); + crtc.connector.set(Some(connector.clone())); + if !crtc.active.value.get() { + log::debug!("Crtc is not active"); + return false; + } + let mode = match connector.mode.get() { + Some(m) => m, + _ => { + log::debug!("Connector has no assigned mode"); + return false; + } + }; + let current_mode = match dev + .dev + .master + .getblob::(crtc.mode_id.value.get()) + { + Ok(m) => m.into(), + _ => { + log::debug!("Could not retrieve current mode of connector"); + return false; + } + }; + if !modes_equal(&mode, ¤t_mode) { + log::debug!("Connector mode differs from desired mode"); + return false; + } + let mut have_primary_plane = false; + for plane in crtc.possible_planes.values() { + if plane.ty == PlaneType::Primary && used_planes.insert(plane.id) { + have_primary_plane = true; + break; + } + } + if !have_primary_plane { + log::debug!("Connector has no primary plane assigned"); + return false; + } + } + + let mut changes = dev.dev.master.change(); + let mut flags = 0; + for crtc in dev.dev.crtcs.values() { + changes.change_object(crtc.id, |c| { + if !used_crtcs.contains(&crtc.id) && crtc.active.value.take() { + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + c.change(crtc.active.id, 0); + } + c.change(crtc.out_fence_ptr, 0); + }); + } + if let Err(e) = changes.commit(flags, 0) { + log::debug!("Could not deactivate crtcs: {}", ErrorFmt(e)); + return false; + } + + true } fn create_scanout_buffers( &self, dev: &Rc, - connector: &Rc, format: &ModifiedFormat, width: i32, height: i32, ) -> Result<[RenderBuffer; 2], MetalError> { - let create = || self.create_scanout_buffer(dev, connector, format, width, height); + let create = || self.create_scanout_buffer(dev, format, width, height); Ok([create()?, create()?]) } fn create_scanout_buffer( &self, dev: &Rc, - connector: &Rc, format: &ModifiedFormat, width: i32, height: i32, @@ -607,7 +823,7 @@ impl MetalBackend { Ok(b) => b, Err(e) => return Err(MetalError::ScanoutBuffer(e)), }; - let drm_fb = match connector.master.add_fb(bo.dma()) { + let drm_fb = match dev.dev.master.add_fb(bo.dma()) { Ok(fb) => Rc::new(fb), Err(e) => return Err(MetalError::Framebuffer(e)), }; @@ -615,15 +831,15 @@ impl MetalBackend { Ok(fb) => fb, Err(e) => return Err(MetalError::ImportFb(e)), }; + egl_fb.clear(); Ok(RenderBuffer { drm: drm_fb, egl: egl_fb, }) } - fn init_drm_connector( + fn assign_connector_crtc( &self, - dev: &Rc, connector: &Rc, changes: &mut Change, ) -> Result<(), MetalError> { @@ -638,33 +854,11 @@ impl MetalBackend { } return Err(MetalError::NoCrtcForConnector); }; - let primary_plane = 'plane: { - for plane in crtc.possible_planes.values() { - if plane.ty == PlaneType::Primary - && plane.crtc_id.value.get().is_none() - && plane.formats.contains_key(&XRGB8888.drm) - { - break 'plane plane.clone(); - } - } - return Err(MetalError::NoPrimaryPlaneForConnector); - }; - let mode = match connector.modes.first() { + let mode = match connector.mode.get() { Some(m) => m, _ => return Err(MetalError::NoModeForConnector), }; let mode_blob = mode.create_blob(&connector.master)?; - let format = ModifiedFormat { - format: XRGB8888, - modifier: INVALID_MODIFIER, - }; - let buffers = self.create_scanout_buffers( - dev, - connector, - &format, - mode.hdisplay as _, - mode.vdisplay as _, - )?; changes.change_object(connector.id, |c| { c.change(connector.crtc_id.id, crtc.id.0 as _); }); @@ -672,8 +866,63 @@ impl MetalBackend { c.change(crtc.active.id, 1); c.change(crtc.mode_id.id, mode_blob.id().0 as _); }); + connector.crtc.set(Some(crtc.clone())); + connector.crtc_id.value.set(crtc.id); + crtc.connector.set(Some(connector.clone())); + crtc.active.value.set(true); + crtc.mode_id.value.set(mode_blob.id()); + crtc.mode_blob.set(Some(Rc::new(mode_blob))); + Ok(()) + } + + fn assign_connector_plane( + &self, + dev: &Rc, + connector: &Rc, + changes: &mut Change, + ) -> Result<(), MetalError> { + let crtc = match connector.crtc.get() { + Some(c) => c, + _ => return Ok(()), + }; + let mode = match connector.mode.get() { + Some(m) => m, + _ => { + log::error!("Connector has a crtc assigned but no mode"); + return Ok(()); + } + }; + let mut primary_plane = connector.primary_plane.get(); + if primary_plane.is_none() { + for plane in crtc.possible_planes.values() { + if plane.ty == PlaneType::Primary + && plane.crtc_id.value.get().is_none() + && plane.formats.contains_key(&XRGB8888.drm) + { + primary_plane = Some(plane.clone()); + break; + } + } + } + let primary_plane = match primary_plane { + Some(p) => p, + _ => return Err(MetalError::NoPrimaryPlaneForConnector), + }; + let format = ModifiedFormat { + format: XRGB8888, + modifier: INVALID_MODIFIER, + }; + let buffers = match connector.buffers.get() { + Some(b) => b, + None => Rc::new(self.create_scanout_buffers( + dev, + &format, + mode.hdisplay as _, + mode.vdisplay as _, + )?), + }; changes.change_object(primary_plane.id, |c| { - c.change(primary_plane.fb_id.id, buffers[0].drm.id().0 as _); + c.change(primary_plane.fb_id, buffers[0].drm.id().0 as _); c.change(primary_plane.crtc_id.id, crtc.id.0 as _); c.change(primary_plane.crtc_x.id, 0); c.change(primary_plane.crtc_y.id, 0); @@ -684,7 +933,6 @@ impl MetalBackend { c.change(primary_plane.src_w.id, (mode.hdisplay as u64) << 16); c.change(primary_plane.src_h.id, (mode.vdisplay as u64) << 16); }); - primary_plane.fb_id.value.set(buffers[0].drm.id()); primary_plane.crtc_id.value.set(crtc.id); primary_plane.crtc_x.value.set(0); primary_plane.crtc_y.value.set(0); @@ -694,14 +942,9 @@ impl MetalBackend { primary_plane.src_y.value.set(0); primary_plane.src_w.value.set((mode.hdisplay as u32) << 16); primary_plane.src_h.value.set((mode.vdisplay as u32) << 16); - connector.crtc_id.value.set(crtc.id); - connector.mode.set(Some(Rc::new(mode.clone()))); - connector.buffers.set(Some(Rc::new(buffers))); + connector.buffers.set(Some(buffers)); connector.primary_plane.set(Some(primary_plane.clone())); - crtc.connector.set(Some(connector.clone())); - crtc.active.value.set(true); - crtc.mode_id.value.set(mode_blob.id()); - crtc.mode_blob.set(Some(Rc::new(mode_blob))); + connector.active.set(true); Ok(()) } @@ -734,14 +977,17 @@ impl MetalBackend { .egl .render(&*node, &self.state, Some(node.position.get())); } - let mut changes = connector - .master - .change(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT); + let mut changes = connector.master.change(); changes.change_object(plane.id, |c| { - c.change(plane.fb_id.id, buffer.drm.id().0 as _); + c.change(plane.fb_id, buffer.drm.id().0 as _); }); - if let Err(e) = changes.commit(0) { - log::error!("Could not set plane framebuffer: {}", ErrorFmt(e)); + if let Err(e) = changes.commit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, 0) { + match e { + DrmError::Atomic(OsError(c::EACCES)) => { + log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master"); + } + _ => log::error!("Could not set plane framebuffer: {}", ErrorFmt(e)), + } } } } @@ -751,3 +997,19 @@ pub struct RenderBuffer { drm: Rc, egl: Rc, } + +fn modes_equal(a: &DrmModeInfo, b: &DrmModeInfo) -> bool { + true && a.clock == b.clock + && a.hdisplay == b.hdisplay + && a.hsync_start == b.hsync_start + && a.hsync_end == b.hsync_end + && a.htotal == b.htotal + && a.hskew == b.hskew + && a.vdisplay == b.vdisplay + && a.vsync_start == b.vsync_start + && a.vsync_end == b.vsync_end + && a.vtotal == b.vtotal + && a.vscan == b.vscan + && a.vrefresh == b.vrefresh + && a.flags == b.flags +} diff --git a/src/drm/drm.rs b/src/drm/drm.rs index 5d5c45c4..f99a935a 100644 --- a/src/drm/drm.rs +++ b/src/drm/drm.rs @@ -1,10 +1,10 @@ mod sys; use crate::drm::drm::sys::{ - create_lease, drm_event, drm_event_vblank, drm_mode_modeinfo, gem_close, get_cap, - get_device_name_from_fd2, get_minor_name_from_fd, get_node_type_from_fd, get_nodes, - mode_addfb2, mode_atomic, mode_create_blob, mode_destroy_blob, mode_get_resources, - mode_getconnector, mode_getencoder, mode_getplane, mode_getplaneresources, mode_getproperty, + create_lease, drm_event, drm_event_vblank, gem_close, get_cap, get_device_name_from_fd2, + get_minor_name_from_fd, get_node_type_from_fd, get_nodes, mode_addfb2, mode_atomic, + mode_create_blob, mode_destroy_blob, mode_get_resources, mode_getconnector, mode_getencoder, + mode_getplane, mode_getplaneresources, mode_getprobblob, mode_getproperty, mode_obj_getproperties, mode_rmfb, prime_fd_to_handle, set_client_cap, DRM_DISPLAY_MODE_LEN, DRM_MODE_ATOMIC_TEST_ONLY, DRM_MODE_FB_MODIFIERS, DRM_MODE_OBJECT_BLOB, DRM_MODE_OBJECT_CONNECTOR, DRM_MODE_OBJECT_CRTC, DRM_MODE_OBJECT_ENCODER, DRM_MODE_OBJECT_FB, @@ -21,17 +21,17 @@ use std::mem::MaybeUninit; use std::ops::Deref; use std::rc::{Rc, Weak}; use thiserror::Error; -use uapi::{c, Errno, OwnedFd, Ustring}; +use uapi::{c, Errno, OwnedFd, Pod, Ustring}; +use crate::drm::dma::DmaBuf; use crate::drm::INVALID_MODIFIER; use crate::utils::stack::Stack; use crate::utils::syncqueue::SyncQueue; use crate::ErrorFmt; pub use sys::{ - DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, - DRM_MODE_PAGE_FLIP_EVENT, + drm_mode_modeinfo, DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, + DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT, }; -use crate::drm::dma::DmaBuf; #[derive(Debug, Error)] pub enum DrmError { @@ -59,6 +59,10 @@ pub enum DrmError { CreateBlob(#[source] OsError), #[error("Could not perform drm getconnector ioctl")] GetConnector(#[source] OsError), + #[error("Could not perform drm getprobblob ioctl")] + GetPropBlob(#[source] OsError), + #[error("Property has an invalid size")] + InvalidProbSize, #[error("Could not perform drm properties ioctl")] GetProperties(#[source] OsError), #[error("Could not perform drm atomic ioctl")] @@ -222,10 +226,9 @@ impl DrmMaster { mode_getconnector(self.raw(), connector.0, force) } - pub fn change(self: &Rc, flags: u32) -> Change { + pub fn change(self: &Rc) -> Change { let mut res = Change { master: self.clone(), - flags, objects: self.u32_bufs.pop().unwrap_or_default(), object_lengths: self.u32_bufs.pop().unwrap_or_default(), props: self.u32_bufs.pop().unwrap_or_default(), @@ -304,6 +307,15 @@ impl DrmMaster { Ok(h) } + pub fn getblob(&self, blob: DrmBlob) -> Result { + let mut t = MaybeUninit::::uninit(); + match mode_getprobblob(self.raw(), blob.0, &mut t) { + Err(e) => Err(DrmError::GetPropBlob(e)), + Ok(n) if n != mem::size_of::() => Err(DrmError::InvalidProbSize), + _ => unsafe { Ok(t.assume_init()) }, + } + } + pub fn event(&self) -> Result, DrmError> { if self.events.is_empty() { let mut buf = self.buf.borrow_mut(); @@ -509,7 +521,7 @@ pub struct DrmEncoderInfo { pub possible_clones: u32, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct DrmModeInfo { pub clock: u32, pub hdisplay: u16, @@ -536,7 +548,7 @@ impl DrmModeInfo { master.create_blob(&raw) } - fn into_raw(&self) -> drm_mode_modeinfo { + pub fn into_raw(&self) -> drm_mode_modeinfo { let mut name = [0u8; DRM_DISPLAY_MODE_LEN]; let len = name.len().min(self.name.len()); name[..len].copy_from_slice(&self.name.as_bytes()[..len]); @@ -579,7 +591,6 @@ pub struct DrmConnectorInfo { pub struct Change { master: Rc, - flags: u32, objects: Vec, object_lengths: Vec, props: Vec, @@ -592,10 +603,10 @@ pub struct ObjectChange<'a> { impl Change { #[allow(dead_code)] - pub fn test(&self) -> Result<(), DrmError> { + pub fn test(&self, flags: u32) -> Result<(), DrmError> { mode_atomic( self.master.raw(), - self.flags | DRM_MODE_ATOMIC_TEST_ONLY, + flags | DRM_MODE_ATOMIC_TEST_ONLY, &self.objects, &self.object_lengths, &self.props, @@ -604,10 +615,10 @@ impl Change { ) } - pub fn commit(&self, user_data: u64) -> Result<(), DrmError> { + pub fn commit(&self, flags: u32, user_data: u64) -> Result<(), DrmError> { mode_atomic( self.master.raw(), - self.flags, + flags, &self.objects, &self.object_lengths, &self.props, diff --git a/src/drm/drm/sys.rs b/src/drm/drm/sys.rs index 5a1869ac..62b60729 100644 --- a/src/drm/drm/sys.rs +++ b/src/drm/drm/sys.rs @@ -686,6 +686,8 @@ pub struct drm_mode_modeinfo { pub name: [u8; DRM_DISPLAY_MODE_LEN], } +unsafe impl Pod for drm_mode_modeinfo {} + impl Into for drm_mode_modeinfo { fn into(self) -> DrmModeInfo { DrmModeInfo { @@ -1038,3 +1040,28 @@ pub struct drm_event_vblank { } unsafe impl Pod for drm_event_vblank {} + +#[repr(C)] +struct drm_mode_get_blob { + blob_id: u32, + length: u32, + data: u64, +} + +const DRM_IOCTL_MODE_GETPROPBLOB: u64 = drm_iowr::(0xac); + +pub fn mode_getprobblob( + fd: c::c_int, + blob_id: u32, + t: &mut T, +) -> Result { + let mut res = drm_mode_get_blob { + blob_id, + length: mem::size_of_val(t) as _, + data: t as *const T as *const u8 as _, + }; + unsafe { + ioctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &mut res)?; + } + Ok(res.length as _) +} diff --git a/src/drm/gbm.rs b/src/drm/gbm.rs index c26370d0..f5efaedb 100644 --- a/src/drm/gbm.rs +++ b/src/drm/gbm.rs @@ -155,10 +155,7 @@ impl GbmDevice { } let bo = BoHolder { bo }; let dma = export_bo(bo.bo)?; - Ok(GbmBo { - _bo: bo, - dma, - }) + Ok(GbmBo { _bo: bo, dma }) } } } diff --git a/src/drm/mod.rs b/src/drm/mod.rs index 9c9f4234..15947931 100644 --- a/src/drm/mod.rs +++ b/src/drm/mod.rs @@ -8,6 +8,7 @@ pub type Modifier = u64; pub const INVALID_MODIFIER: Modifier = 0x00ff_ffff_ffff_ffff; +#[derive(Copy, Clone)] pub struct ModifiedFormat { pub format: &'static Format, pub modifier: Modifier, diff --git a/src/libinput/event.rs b/src/libinput/event.rs index e0643c9a..190bb469 100644 --- a/src/libinput/event.rs +++ b/src/libinput/event.rs @@ -1,6 +1,15 @@ use crate::libinput::consts::{ButtonState, EventType, KeyState, PointerAxis}; use crate::libinput::device::LibInputDevice; -use crate::libinput::sys::{libinput_event, libinput_event_destroy, libinput_event_get_device, libinput_event_get_keyboard_event, libinput_event_get_pointer_event, libinput_event_get_type, libinput_event_keyboard, libinput_event_keyboard_get_key, libinput_event_keyboard_get_key_state, libinput_event_keyboard_get_time_usec, libinput_event_pointer, libinput_event_pointer_get_button, libinput_event_pointer_get_button_state, libinput_event_pointer_get_dx, libinput_event_pointer_get_dy, libinput_event_pointer_get_scroll_value_v120, libinput_event_pointer_get_time_usec}; +use crate::libinput::sys::{ + libinput_event, libinput_event_destroy, libinput_event_get_device, + libinput_event_get_keyboard_event, libinput_event_get_pointer_event, libinput_event_get_type, + libinput_event_keyboard, libinput_event_keyboard_get_key, + libinput_event_keyboard_get_key_state, libinput_event_keyboard_get_time_usec, + libinput_event_pointer, libinput_event_pointer_get_button, + libinput_event_pointer_get_button_state, libinput_event_pointer_get_dx, + libinput_event_pointer_get_dy, libinput_event_pointer_get_scroll_value_v120, + libinput_event_pointer_get_time_usec, +}; use std::marker::PhantomData; pub struct LibInputEvent<'a> { diff --git a/src/libinput/sys.rs b/src/libinput/sys.rs index e24ff903..1f98e4ad 100644 --- a/src/libinput/sys.rs +++ b/src/libinput/sys.rs @@ -60,8 +60,13 @@ extern "C" { pub fn libinput_event_pointer_get_dx(event: *mut libinput_event_pointer) -> f64; pub fn libinput_event_pointer_get_dy(event: *mut libinput_event_pointer) -> f64; pub fn libinput_event_pointer_get_button(event: *mut libinput_event_pointer) -> u32; - pub fn libinput_event_pointer_get_button_state(event: *mut libinput_event_pointer) -> libinput_button_state; - pub fn libinput_event_pointer_get_scroll_value_v120(event: *mut libinput_event_pointer, axis: libinput_pointer_axis) -> f64; + pub fn libinput_event_pointer_get_button_state( + event: *mut libinput_event_pointer, + ) -> libinput_button_state; + pub fn libinput_event_pointer_get_scroll_value_v120( + event: *mut libinput_event_pointer, + axis: libinput_pointer_axis, + ) -> f64; } #[repr(C)] diff --git a/src/render/renderer/framebuffer.rs b/src/render/renderer/framebuffer.rs index dd338a36..4a0433ed 100644 --- a/src/render/renderer/framebuffer.rs +++ b/src/render/renderer/framebuffer.rs @@ -24,6 +24,18 @@ impl Debug for Framebuffer { } impl Framebuffer { + pub fn clear(&self) { + let _ = self.ctx.ctx.with_current(|| { + unsafe { + glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo); + glViewport(0, 0, self.gl.width, self.gl.height); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + } + Ok(()) + }); + } + pub fn render(&self, node: &dyn Node, state: &State, cursor_rect: Option) { let _ = self.ctx.ctx.with_current(|| { if let Some(rd) = &self.ctx.renderdoc {