1
0
Fork 0
forked from wry/wry

Merge pull request #798 from mahkoh/jorth/render-only

metal: add support for render-only devices
This commit is contained in:
mahkoh 2026-03-14 15:07:03 +01:00 committed by GitHub
commit 6e400655c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 156 additions and 110 deletions

View file

@ -44,10 +44,10 @@ use {
INVALID_MODIFIER, Modifier, INVALID_MODIFIER, Modifier,
dmabuf::DmaBufId, dmabuf::DmaBufId,
drm::{ drm::{
ConnectorStatus, ConnectorType, DRM_CLIENT_CAP_ATOMIC, DrmBlob, DrmConnector, ConnectorStatus, ConnectorType, DRM_CLIENT_CAP_ATOMIC, DrmBlob, DrmCardResources,
DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFb, DrmLease, DrmMaster, DrmModeInfo, DrmConnector, DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFb, DrmLease, DrmMaster,
DrmObject, DrmPlane, DrmProperty, DrmPropertyDefinition, DrmPropertyType, DrmModeInfo, DrmObject, DrmPlane, DrmProperty, DrmPropertyDefinition,
DrmVersion, HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo, DrmPropertyType, DrmVersion, HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo,
hdr_output_metadata, hdr_output_metadata,
}, },
gbm::GbmDevice, gbm::GbmDevice,
@ -105,13 +105,10 @@ pub struct MetalDrmDevice {
pub devnum: c::dev_t, pub devnum: c::dev_t,
pub devnode: CString, pub devnode: CString,
pub master: Rc<DrmMaster>, pub master: Rc<DrmMaster>,
pub supports_kms: bool,
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>, pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
pub encoders: AHashMap<DrmEncoder, Rc<MetalEncoder>>, pub encoders: AHashMap<DrmEncoder, Rc<MetalEncoder>>,
pub planes: AHashMap<DrmPlane, Rc<MetalPlane>>, pub planes: AHashMap<DrmPlane, Rc<MetalPlane>>,
pub _min_width: u32,
pub _max_width: u32,
pub _min_height: u32,
pub _max_height: u32,
pub cursor_width: u64, pub cursor_width: u64,
pub cursor_height: u64, pub cursor_height: u64,
pub supports_async_commit: bool, pub supports_async_commit: bool,
@ -1822,6 +1819,9 @@ impl MetalBackend {
} }
fn handle_drm_change_(self: &Rc<Self>, dev: &Rc<MetalDrmDeviceData>) -> Result<(), MetalError> { fn handle_drm_change_(self: &Rc<Self>, dev: &Rc<MetalDrmDeviceData>) -> Result<(), MetalError> {
if !dev.dev.supports_kms {
return Ok(());
}
if let Err(e) = self.update_device_properties(dev) { if let Err(e) = self.update_device_properties(dev) {
return Err(MetalError::UpdateProperties(e)); return Err(MetalError::UpdateProperties(e));
} }
@ -1982,46 +1982,44 @@ impl MetalBackend {
pending: PendingDrmDevice, pending: PendingDrmDevice,
master: &Rc<DrmMaster>, master: &Rc<DrmMaster>,
) -> Result<Rc<MetalDrmDeviceData>, MetalError> { ) -> Result<Rc<MetalDrmDeviceData>, MetalError> {
if let Err(e) = master.set_client_cap(DRM_CLIENT_CAP_ATOMIC, 2) { let mut resources = DrmCardResources::default();
return Err(MetalError::AtomicModesetting(e)); let mut planes = AHashMap::new();
} let mut crtcs = AHashMap::new();
let resources = master.get_resources()?; let mut encoders = AHashMap::new();
let (mut cursor_width, mut cursor_height) = (1, 1);
let (cursor_width, cursor_height) = match master.get_cursor_size() { let supports_kms = master.supports_get_resources()?;
Ok(s) => s, if supports_kms {
Err(e) => { 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)); log::warn!("Can't determine size of cursor planes: {}", ErrorFmt(e));
(64, 64) (64, 64)
} });
}; for plane in master.get_planes()? {
match create_plane(plane, master) {
let mut planes = AHashMap::new(); Ok(p) => {
for plane in master.get_planes()? { planes.insert(p.id, Rc::new(p));
match create_plane(plane, master) { }
Ok(p) => { Err(e) => return Err(MetalError::CreatePlane(e)),
planes.insert(p.id, Rc::new(p));
} }
Err(e) => return Err(MetalError::CreatePlane(e)),
} }
} for (idx, crtc) in resources.crtcs.iter().copied().enumerate() {
match create_crtc(crtc, idx, master, &planes) {
let mut crtcs = AHashMap::new(); Ok(c) => {
for (idx, crtc) in resources.crtcs.iter().copied().enumerate() { crtcs.insert(c.id, Rc::new(c));
match create_crtc(crtc, idx, master, &planes) { }
Ok(c) => { Err(e) => return Err(MetalError::CreateCrtc(e)),
crtcs.insert(c.id, Rc::new(c));
} }
Err(e) => return Err(MetalError::CreateCrtc(e)),
} }
} for encoder in resources.encoders {
match create_encoder(encoder, master, &crtcs) {
let mut encoders = AHashMap::new(); Ok(e) => {
for encoder in resources.encoders { encoders.insert(e.id, Rc::new(e));
match create_encoder(encoder, master, &crtcs) { }
Ok(e) => { Err(e) => return Err(MetalError::CreateEncoder(e)),
encoders.insert(e.id, Rc::new(e));
} }
Err(e) => return Err(MetalError::CreateEncoder(e)),
} }
} }
@ -2072,13 +2070,10 @@ impl MetalBackend {
devnum: pending.devnum, devnum: pending.devnum,
devnode: pending.devnode, devnode: pending.devnode,
master: master.clone(), master: master.clone(),
supports_kms,
crtcs, crtcs,
encoders, encoders,
planes, planes,
_min_width: resources.min_width,
_max_width: resources.max_width,
_min_height: resources.min_height,
_max_height: resources.max_height,
cursor_width, cursor_width,
cursor_height, cursor_height,
supports_async_commit: master.supports_async_commit(), supports_async_commit: master.supports_async_commit(),
@ -2099,7 +2094,11 @@ impl MetalBackend {
min_post_commit_margin: Cell::new(DEFAULT_POST_COMMIT_MARGIN), 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 { let slf = Rc::new(MetalDrmDeviceData {
dev: dev.clone(), dev: dev.clone(),
@ -2574,6 +2573,9 @@ impl MetalBackend {
fn init_drm_device(&self, dev: &Rc<MetalDrmDeviceData>) -> Result<(), MetalError> { fn init_drm_device(&self, dev: &Rc<MetalDrmDeviceData>) -> Result<(), MetalError> {
self.break_leases(dev); self.break_leases(dev);
if dev.connectors.is_empty() {
return Ok(());
}
enum Quirks { enum Quirks {
DirectScanout, DirectScanout,
NonDefaultFormat, NonDefaultFormat,

View file

@ -408,50 +408,57 @@ impl EgvRenderer {
.enumerate_physical_devices() .enumerate_physical_devices()
.map_err(EgvError::EnumeratePhysicalDevice)? .map_err(EgvError::EnumeratePhysicalDevice)?
}; };
'outer: for phy in devices { for software in [false, true] {
let res = unsafe { instance.enumerate_device_extension_properties(phy) }; 'outer: for &phy in &devices {
let exts = match res { let res = unsafe { instance.enumerate_device_extension_properties(phy) };
Ok(res) => map_extension_properties(res), let exts = match res {
Err(e) => { Ok(res) => map_extension_properties(res),
log::error!( Err(e) => {
"Could not enumerate extensions of physical device: {}", log::error!(
ErrorFmt(e), "Could not enumerate extensions of physical device: {}",
); ErrorFmt(e),
continue; );
continue;
}
};
let mut drm_props = PhysicalDeviceDrmPropertiesEXT::default();
let mut props = PhysicalDeviceProperties2::default().push_next(&mut drm_props);
unsafe {
instance.get_physical_device_properties2(phy, &mut props);
} }
}; let props = props.properties;
let mut drm_props = PhysicalDeviceDrmPropertiesEXT::default(); physical_device = phy;
let mut props = PhysicalDeviceProperties2::default().push_next(&mut drm_props); device_extensions = exts;
unsafe { device_properties = props;
instance.get_physical_device_properties2(phy, &mut props); if let Some(dev) = dev
} && !software
let props = props.properties; {
physical_device = phy; if device_extensions.not_contains_key(physical_device_drm::NAME) {
device_extensions = exts; continue 'outer;
device_properties = props; }
if let Some(dev) = dev { let major = uapi::major(dev) as i64;
if device_extensions.not_contains_key(physical_device_drm::NAME) { let minor = uapi::minor(dev) as i64;
continue 'outer; let matches = (drm_props.has_primary == vk::TRUE
} && drm_props.primary_major == major
let major = uapi::major(dev) as i64; && drm_props.primary_minor == minor)
let minor = uapi::minor(dev) as i64; || (drm_props.has_render == vk::TRUE
let matches = (drm_props.has_primary == vk::TRUE && drm_props.render_major == major
&& drm_props.primary_major == major && drm_props.render_minor == minor);
&& drm_props.primary_minor == minor) if matches {
|| (drm_props.has_render == vk::TRUE break 'find_device;
&& drm_props.render_major == major }
&& drm_props.render_minor == minor); } else {
if matches { if device_properties.device_type == PhysicalDeviceType::CPU {
break 'find_device; break 'find_device;
} }
} else {
if device_properties.device_type == PhysicalDeviceType::CPU {
break 'find_device;
} }
} }
} }
return Err(EgvError::NoVulkanDevice); return Err(EgvError::NoVulkanDevice);
} }
if device_properties.device_type == PhysicalDeviceType::CPU && dev.is_some() {
log::warn!("Using software rendering");
}
if device_properties.api_version < VULKAN_API_VERSION { if device_properties.api_version < VULKAN_API_VERSION {
return Err(EgvError::NoVulkan13); return Err(EgvError::NoVulkan13);
} }

View file

@ -26,7 +26,8 @@ use {
get_version, mode_addfb2, mode_atomic, mode_create_blob, mode_destroy_blob, get_version, mode_addfb2, mode_atomic, mode_create_blob, mode_destroy_blob,
mode_get_resources, mode_getconnector, mode_getencoder, mode_getplane, mode_get_resources, mode_getconnector, mode_getencoder, mode_getplane,
mode_getplaneresources, mode_getprobblob, mode_getproperty, mode_obj_getproperties, 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,
}, },
}, },
}, },
@ -168,11 +169,13 @@ fn reopen(fd: c::c_int, need_primary: bool) -> Result<Rc<OwnedFd>, DrmError> {
return Ok(Rc::new(fd)); return Ok(Rc::new(fd));
} }
let path = 'path: { let path = 'path: {
if get_node_type_from_fd(fd).map_err(DrmError::GetDeviceType)? == NodeType::Render { if !need_primary {
break 'path uapi::format_ustr!("/proc/self/fd/{}", fd); if get_node_type_from_fd(fd).map_err(DrmError::GetDeviceType)? == NodeType::Render {
} break 'path uapi::format_ustr!("/proc/self/fd/{}", fd);
if !need_primary && let Ok(path) = render_node_name(fd) { }
break 'path path; if let Ok(path) = render_node_name(fd) {
break 'path path;
}
} }
device_node_name(fd)? device_node_name(fd)?
}; };
@ -212,6 +215,10 @@ impl Drm {
self.fd.raw() self.fd.raw()
} }
pub fn dup_primary(&self) -> Result<Self, DrmError> {
Self::reopen(self.fd.raw(), true)
}
pub fn dup_render(&self) -> Result<Self, DrmError> { pub fn dup_render(&self) -> Result<Self, DrmError> {
Self::reopen(self.fd.raw(), false) Self::reopen(self.fd.raw(), false)
} }
@ -333,6 +340,10 @@ impl DrmMaster {
mode_get_resources(self.raw()) mode_get_resources(self.raw())
} }
pub fn supports_get_resources(&self) -> Result<bool, DrmError> {
mode_supports_get_resources(self.raw())
}
pub fn get_cap(&self, cap: u64) -> Result<u64, OsError> { pub fn get_cap(&self, cap: u64) -> Result<u64, OsError> {
get_cap(self.raw(), cap) get_cap(self.raw(), cap)
} }
@ -745,12 +756,12 @@ drm_obj!(DrmFb, DRM_MODE_OBJECT_FB);
drm_obj!(DrmBlob, DRM_MODE_OBJECT_BLOB); drm_obj!(DrmBlob, DRM_MODE_OBJECT_BLOB);
drm_obj!(DrmPlane, DRM_MODE_OBJECT_PLANE); drm_obj!(DrmPlane, DRM_MODE_OBJECT_PLANE);
#[derive(Debug)] #[derive(Debug, Default)]
pub struct DrmCardResources { pub struct DrmCardResources {
pub min_width: u32, pub _min_width: u32,
pub max_width: u32, pub _max_width: u32,
pub min_height: u32, pub _min_height: u32,
pub max_height: u32, pub _max_height: u32,
pub _fbs: Vec<DrmFb>, pub _fbs: Vec<DrmFb>,
pub crtcs: Vec<DrmCrtc>, pub crtcs: Vec<DrmCrtc>,
pub connectors: Vec<DrmConnector>, pub connectors: Vec<DrmConnector>,

View file

@ -17,7 +17,11 @@ use {
ffi::CString, ffi::CString,
io::{BufRead, BufReader}, io::{BufRead, BufReader},
}, },
uapi::{OwnedFd, Pod, Ustring, c, pod_zeroed}, uapi::{
OwnedFd, Pod, Ustring,
c::{self, c_int},
pod_zeroed,
},
}; };
pub unsafe fn ioctl<T>(fd: c::c_int, request: c::c_ulong, t: &mut T) -> Result<c::c_int, OsError> { pub unsafe fn ioctl<T>(fd: c::c_int, request: c::c_ulong, t: &mut T) -> Result<c::c_int, OsError> {
@ -546,10 +550,10 @@ pub fn mode_get_resources(fd: c::c_int) -> Result<DrmCardResources, DrmError> {
} }
Ok(DrmCardResources { Ok(DrmCardResources {
min_width: res.min_width, _min_width: res.min_width,
max_width: res.max_width, _max_width: res.max_width,
min_height: res.min_height, _min_height: res.min_height,
max_height: res.max_height, _max_height: res.max_height,
_fbs: fbs, _fbs: fbs,
crtcs, crtcs,
connectors, connectors,
@ -557,6 +561,16 @@ pub fn mode_get_resources(fd: c::c_int) -> Result<DrmCardResources, DrmError> {
}) })
} }
pub fn mode_supports_get_resources(fd: c_int) -> Result<bool, DrmError> {
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)] #[repr(C)]
struct drm_mode_get_plane_res { struct drm_mode_get_plane_res {
plane_id_ptr: u64, plane_id_ptr: u64,

View file

@ -7,8 +7,8 @@ use {
BO_USE_RENDERING, BO_USE_SCANOUT, BO_USE_WRITE, BufferObject, BufferUsage, BO_USE_RENDERING, BO_USE_SCANOUT, BO_USE_WRITE, BufferObject, BufferUsage,
MappedBuffer, MappedBuffer,
}, },
format::{Format, formats}, format::{Format, XRGB8888, formats},
utils::oserror::OsError, utils::{errorfmt::ErrorFmt, oserror::OsError},
video::{ video::{
INVALID_MODIFIER, Modifier, INVALID_MODIFIER, Modifier,
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec}, dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
@ -214,14 +214,26 @@ unsafe fn export_bo(dmabuf_ids: &DmaBufIds, bo: *mut Bo) -> Result<DmaBuf, GbmEr
impl GbmDevice { impl GbmDevice {
pub fn new(drm: &Drm) -> Result<Self, GbmError> { pub fn new(drm: &Drm) -> Result<Self, GbmError> {
let drm = drm.dup_render()?; let open = |drm: Drm| {
let dev = unsafe { gbm_create_device(drm.raw()) }; let dev = unsafe { gbm_create_device(drm.raw()) };
if dev.is_null() { if dev.is_null() {
Err(GbmError::CreateDevice) Err(GbmError::CreateDevice)
} else { } else {
let dev = Rc::new(DeviceHolder { dev }); let dev = Rc::new(DeviceHolder { dev });
Ok(Self { drm, dev }) Ok(Self { drm, dev })
} }
};
let dma_buf_ids = DmaBufIds::default();
let create_bo =
|gbm: &GbmDevice| gbm.create_bo(&dma_buf_ids, 1, 1, XRGB8888, &[INVALID_MODIFIER], 0);
let gbm = open(drm.dup_render()?)?;
match create_bo(&gbm) {
Ok(..) => return Ok(gbm),
Err(e) => log::warn!("Render node cannot allocate buffers: {}", ErrorFmt(e)),
};
let gbm = open(drm.dup_primary()?)?;
create_bo(&gbm)?;
Ok(gbm)
} }
pub fn raw(&self) -> *mut Device { pub fn raw(&self) -> *mut Device {