From 3d60cfc5dc7627280eb8d027ec5b5b3dfe8f60da Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sat, 14 Mar 2026 14:05:46 +0100 Subject: [PATCH] metal: add support for render-only devices --- src/backends/metal/video.rs | 94 +++++++++++++++++++------------------ src/video/drm.rs | 17 ++++--- src/video/drm/sys.rs | 24 ++++++++-- 3 files changed, 78 insertions(+), 57 deletions(-) diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 3a7e043c..b61a676c 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -44,10 +44,10 @@ use { INVALID_MODIFIER, Modifier, dmabuf::DmaBufId, drm::{ - ConnectorStatus, ConnectorType, DRM_CLIENT_CAP_ATOMIC, DrmBlob, DrmConnector, - DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFb, DrmLease, DrmMaster, DrmModeInfo, - DrmObject, DrmPlane, DrmProperty, DrmPropertyDefinition, DrmPropertyType, - DrmVersion, HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo, + ConnectorStatus, ConnectorType, DRM_CLIENT_CAP_ATOMIC, DrmBlob, DrmCardResources, + DrmConnector, DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFb, DrmLease, DrmMaster, + DrmModeInfo, DrmObject, DrmPlane, DrmProperty, DrmPropertyDefinition, + DrmPropertyType, DrmVersion, HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo, hdr_output_metadata, }, gbm::GbmDevice, @@ -105,13 +105,10 @@ pub struct MetalDrmDevice { pub devnum: c::dev_t, pub devnode: CString, pub master: Rc, + pub supports_kms: bool, pub crtcs: AHashMap>, pub encoders: AHashMap>, pub planes: AHashMap>, - pub _min_width: u32, - pub _max_width: u32, - pub _min_height: u32, - pub _max_height: u32, pub cursor_width: u64, pub cursor_height: u64, pub supports_async_commit: bool, @@ -1822,6 +1819,9 @@ impl MetalBackend { } fn handle_drm_change_(self: &Rc, dev: &Rc) -> Result<(), MetalError> { + if !dev.dev.supports_kms { + return Ok(()); + } if let Err(e) = self.update_device_properties(dev) { return Err(MetalError::UpdateProperties(e)); } @@ -1982,46 +1982,44 @@ impl MetalBackend { pending: PendingDrmDevice, master: &Rc, ) -> Result, MetalError> { - if let Err(e) = master.set_client_cap(DRM_CLIENT_CAP_ATOMIC, 2) { - return Err(MetalError::AtomicModesetting(e)); - } - let resources = master.get_resources()?; - - let (cursor_width, cursor_height) = match master.get_cursor_size() { - Ok(s) => s, - Err(e) => { + let mut resources = DrmCardResources::default(); + let mut planes = AHashMap::new(); + let mut crtcs = AHashMap::new(); + let mut encoders = AHashMap::new(); + let (mut cursor_width, mut cursor_height) = (1, 1); + let supports_kms = master.supports_get_resources()?; + if supports_kms { + if let Err(e) = master.set_client_cap(DRM_CLIENT_CAP_ATOMIC, 2) { + return Err(MetalError::AtomicModesetting(e)); + } + resources = master.get_resources()?; + (cursor_width, cursor_height) = master.get_cursor_size().unwrap_or_else(|e| { log::warn!("Can't determine size of cursor planes: {}", ErrorFmt(e)); (64, 64) - } - }; - - let mut planes = AHashMap::new(); - for plane in master.get_planes()? { - match create_plane(plane, master) { - Ok(p) => { - planes.insert(p.id, Rc::new(p)); + }); + for plane in master.get_planes()? { + match create_plane(plane, master) { + Ok(p) => { + planes.insert(p.id, Rc::new(p)); + } + Err(e) => return Err(MetalError::CreatePlane(e)), } - Err(e) => return Err(MetalError::CreatePlane(e)), } - } - - let mut crtcs = AHashMap::new(); - for (idx, crtc) in resources.crtcs.iter().copied().enumerate() { - match create_crtc(crtc, idx, master, &planes) { - Ok(c) => { - crtcs.insert(c.id, Rc::new(c)); + for (idx, crtc) in resources.crtcs.iter().copied().enumerate() { + match create_crtc(crtc, idx, master, &planes) { + Ok(c) => { + crtcs.insert(c.id, Rc::new(c)); + } + Err(e) => return Err(MetalError::CreateCrtc(e)), } - Err(e) => return Err(MetalError::CreateCrtc(e)), } - } - - let mut encoders = AHashMap::new(); - for encoder in resources.encoders { - match create_encoder(encoder, master, &crtcs) { - Ok(e) => { - encoders.insert(e.id, Rc::new(e)); + for encoder in resources.encoders { + match create_encoder(encoder, master, &crtcs) { + Ok(e) => { + encoders.insert(e.id, Rc::new(e)); + } + Err(e) => return Err(MetalError::CreateEncoder(e)), } - Err(e) => return Err(MetalError::CreateEncoder(e)), } } @@ -2072,13 +2070,10 @@ impl MetalBackend { devnum: pending.devnum, devnode: pending.devnode, master: master.clone(), + supports_kms, crtcs, encoders, planes, - _min_width: resources.min_width, - _max_width: resources.max_width, - _min_height: resources.min_height, - _max_height: resources.max_height, cursor_width, cursor_height, supports_async_commit: master.supports_async_commit(), @@ -2099,7 +2094,11 @@ impl MetalBackend { min_post_commit_margin: Cell::new(DEFAULT_POST_COMMIT_MARGIN), }); - let (connectors, futures) = get_connectors(self, &dev, &resources.connectors)?; + let mut connectors = CopyHashMap::new(); + let mut futures = CopyHashMap::new(); + if supports_kms { + (connectors, futures) = get_connectors(self, &dev, &resources.connectors)?; + } let slf = Rc::new(MetalDrmDeviceData { dev: dev.clone(), @@ -2574,6 +2573,9 @@ impl MetalBackend { fn init_drm_device(&self, dev: &Rc) -> Result<(), MetalError> { self.break_leases(dev); + if dev.connectors.is_empty() { + return Ok(()); + } enum Quirks { DirectScanout, NonDefaultFormat, diff --git a/src/video/drm.rs b/src/video/drm.rs index 80db5b5e..0ce27257 100644 --- a/src/video/drm.rs +++ b/src/video/drm.rs @@ -26,7 +26,8 @@ use { get_version, 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, queue_sequence, revoke_lease, set_client_cap, + mode_rmfb, mode_supports_get_resources, prime_fd_to_handle, queue_sequence, + revoke_lease, set_client_cap, }, }, }, @@ -339,6 +340,10 @@ impl DrmMaster { mode_get_resources(self.raw()) } + pub fn supports_get_resources(&self) -> Result { + mode_supports_get_resources(self.raw()) + } + pub fn get_cap(&self, cap: u64) -> Result { get_cap(self.raw(), cap) } @@ -751,12 +756,12 @@ drm_obj!(DrmFb, DRM_MODE_OBJECT_FB); drm_obj!(DrmBlob, DRM_MODE_OBJECT_BLOB); drm_obj!(DrmPlane, DRM_MODE_OBJECT_PLANE); -#[derive(Debug)] +#[derive(Debug, Default)] pub struct DrmCardResources { - pub min_width: u32, - pub max_width: u32, - pub min_height: u32, - pub max_height: u32, + pub _min_width: u32, + pub _max_width: u32, + pub _min_height: u32, + pub _max_height: u32, pub _fbs: Vec, pub crtcs: Vec, pub connectors: Vec, diff --git a/src/video/drm/sys.rs b/src/video/drm/sys.rs index e0a55ccf..ffc876da 100644 --- a/src/video/drm/sys.rs +++ b/src/video/drm/sys.rs @@ -17,7 +17,11 @@ use { ffi::CString, io::{BufRead, BufReader}, }, - uapi::{OwnedFd, Pod, Ustring, c, pod_zeroed}, + uapi::{ + OwnedFd, Pod, Ustring, + c::{self, c_int}, + pod_zeroed, + }, }; pub unsafe fn ioctl(fd: c::c_int, request: c::c_ulong, t: &mut T) -> Result { @@ -546,10 +550,10 @@ pub fn mode_get_resources(fd: c::c_int) -> Result { } Ok(DrmCardResources { - min_width: res.min_width, - max_width: res.max_width, - min_height: res.min_height, - max_height: res.max_height, + _min_width: res.min_width, + _max_width: res.max_width, + _min_height: res.min_height, + _max_height: res.max_height, _fbs: fbs, crtcs, connectors, @@ -557,6 +561,16 @@ pub fn mode_get_resources(fd: c::c_int) -> Result { }) } +pub fn mode_supports_get_resources(fd: c_int) -> Result { + let mut res = drm_mode_card_res::default(); + let res = unsafe { ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &mut res) }; + match res { + Ok(_) => Ok(true), + Err(e) if e.0 == c::EOPNOTSUPP => Ok(false), + Err(e) => Err(DrmError::GetResources(e)), + } +} + #[repr(C)] struct drm_mode_get_plane_res { plane_id_ptr: u64,