diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 6cbb2387..b068596b 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -28,7 +28,7 @@ use { DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT, }, gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT}, - ModifiedFormat, INVALID_MODIFIER, + Modifier, }, }, ahash::{AHashMap, AHashSet}, @@ -505,6 +505,12 @@ pub enum PlaneType { Cursor, } +#[derive(Debug)] +pub struct PlaneFormat { + _format: &'static Format, + modifiers: Vec, +} + #[derive(Debug)] pub struct MetalPlane { pub id: DrmPlane, @@ -513,7 +519,7 @@ pub struct MetalPlane { pub ty: PlaneType, pub possible_crtcs: u32, - pub formats: AHashMap, + pub formats: AHashMap, pub assigned: Cell, @@ -755,19 +761,36 @@ fn create_crtc( fn create_plane(plane: DrmPlane, master: &Rc) -> Result { let info = master.get_plane_info(plane)?; + let props = collect_properties(master, plane)?; let mut formats = AHashMap::new(); - for format in info.format_types { - if let Some(f) = crate::format::formats().get(&format) { - formats.insert(format, *f); - } else { - // log::warn!( - // "{:?} supports unknown format '{:?}'", - // plane, - // crate::format::debug(format) - // ); + if let Some((_, v)) = props.props.get(b"IN_FORMATS".as_bstr()) { + for format in master.get_in_formats(*v as _)? { + if format.modifiers.is_empty() { + continue; + } + if let Some(f) = crate::format::formats().get(&format.format) { + formats.insert( + format.format, + PlaneFormat { + _format: f, + modifiers: format.modifiers, + }, + ); + } + } + } else { + for format in info.format_types { + if let Some(f) = crate::format::formats().get(&format) { + formats.insert( + format, + PlaneFormat { + _format: f, + modifiers: vec![], + }, + ); + } } } - let props = collect_properties(master, plane)?; let ty = match props.props.get(b"type".as_bstr()) { Some((def, val)) => match &def.ty { DrmPropertyType::Enum { values, .. } => 'ty: { @@ -1569,30 +1592,33 @@ impl MetalBackend { fn create_scanout_buffers( &self, dev: &Rc, - format: &ModifiedFormat, + format: &Format, + modifiers: &[Modifier], width: i32, height: i32, ctx: &MetalRenderContext, cursor: bool, ) -> Result<[RenderBuffer; 2], MetalError> { - let create = || self.create_scanout_buffer(dev, format, width, height, ctx, cursor); + let create = + || self.create_scanout_buffer(dev, format, modifiers, width, height, ctx, cursor); Ok([create()?, create()?]) } fn create_scanout_buffer( &self, dev: &Rc, - format: &ModifiedFormat, + format: &Format, + modifiers: &[Modifier], width: i32, height: i32, render_ctx: &MetalRenderContext, cursor: bool, ) -> Result { let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT; - if cursor { + if cursor && modifiers.is_empty() { usage |= GBM_BO_USE_LINEAR; }; - let dev_bo = dev.gbm.create_bo(width, height, format, usage); + let dev_bo = dev.gbm.create_bo(width, height, format, modifiers, usage); let dev_bo = match dev_bo { Ok(b) => b, Err(e) => return Err(MetalError::ScanoutBuffer(e)), @@ -1619,7 +1645,10 @@ impl MetalBackend { } else { // Create a _bridge_ BO in the render device usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR; - let render_bo = render_ctx.gfx.gbm().create_bo(width, height, format, usage); + let render_bo = render_ctx + .gfx + .gbm() + .create_bo(width, height, format, &[], usage); let render_bo = match render_bo { Ok(b) => b, Err(e) => return Err(MetalError::ScanoutBuffer(e)), @@ -1716,46 +1745,45 @@ impl MetalBackend { return Ok(()); } }; - let primary_plane = 'primary_plane: { + let (primary_plane, primary_modifiers) = 'primary_plane: { for plane in crtc.possible_planes.values() { - if plane.ty == PlaneType::Primary - && !plane.assigned.get() - && plane.formats.contains_key(&XRGB8888.drm) - { - break 'primary_plane plane.clone(); + if plane.ty == PlaneType::Primary && !plane.assigned.get() { + if let Some(format) = plane.formats.get(&XRGB8888.drm) { + break 'primary_plane (plane.clone(), &format.modifiers); + } } } return Err(MetalError::NoPrimaryPlaneForConnector); }; let buffers = Rc::new(self.create_scanout_buffers( &connector.dev, - &ModifiedFormat { - format: XRGB8888, - modifier: INVALID_MODIFIER, - }, + XRGB8888, + primary_modifiers, mode.hdisplay as _, mode.vdisplay as _, ctx, false, )?); let mut cursor_plane = None; + let mut cursor_modifiers = &[][..]; for plane in crtc.possible_planes.values() { if plane.ty == PlaneType::Cursor && !plane.assigned.get() && plane.formats.contains_key(&ARGB8888.drm) { - cursor_plane = Some(plane.clone()); - break; + if let Some(format) = plane.formats.get(&ARGB8888.drm) { + cursor_plane = Some(plane.clone()); + cursor_modifiers = &format.modifiers[..]; + break; + } } } let mut cursor_buffers = None; if cursor_plane.is_some() { let res = self.create_scanout_buffers( &connector.dev, - &ModifiedFormat { - format: ARGB8888, - modifier: INVALID_MODIFIER, - }, + ARGB8888, + cursor_modifiers, connector.dev.cursor_width as _, connector.dev.cursor_height as _, ctx, diff --git a/src/backends/x.rs b/src/backends/x.rs index 52c26291..7d12aee3 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -21,7 +21,6 @@ use { video::{ drm::{ConnectorType, Drm, DrmError, DrmVersion}, gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING}, - ModifiedFormat, INVALID_MODIFIER, }, wire_xcon::{ ChangeProperty, ChangeWindowAttributes, ConfigureNotify, CreateCursor, CreatePixmap, @@ -376,15 +375,11 @@ impl XBackend { width: i32, height: i32, ) -> Result<[XImage; 2], XBackendError> { - let format = ModifiedFormat { - format: XRGB8888, - modifier: INVALID_MODIFIER, - }; let mut images = [None, None]; for image in &mut images { let bo = self .gbm - .create_bo(width, height, &format, GBM_BO_USE_RENDERING)?; + .create_bo(width, height, XRGB8888, &[], GBM_BO_USE_RENDERING)?; let dma = bo.dmabuf(); assert!(dma.planes.len() == 1); let plane = dma.planes.first().unwrap(); diff --git a/src/ifs/jay_screencast.rs b/src/ifs/jay_screencast.rs index 6097ac67..492c22c2 100644 --- a/src/ifs/jay_screencast.rs +++ b/src/ifs/jay_screencast.rs @@ -17,7 +17,6 @@ use { video::{ dmabuf::DmaBuf, gbm::{GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING}, - ModifiedFormat, INVALID_MODIFIER, }, wire::{jay_screencast::*, JayScreencastId}, }, @@ -199,17 +198,13 @@ impl JayScreencast { let mode = output.global.mode.get(); let num = 3; for _ in 0..num { - let format = ModifiedFormat { - format: XRGB8888, - modifier: INVALID_MODIFIER, - }; let mut flags = GBM_BO_USE_RENDERING; if self.linear.get() { flags |= GBM_BO_USE_LINEAR; } let buffer = ctx .gbm() - .create_bo(mode.width, mode.height, &format, flags)?; + .create_bo(mode.width, mode.height, XRGB8888, &[], flags)?; let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?; buffers.push(ScreencastBuffer { dmabuf: buffer.dmabuf().clone(), diff --git a/src/portal/ptr_gui.rs b/src/portal/ptr_gui.rs index b97cdd77..bb685923 100644 --- a/src/portal/ptr_gui.rs +++ b/src/portal/ptr_gui.rs @@ -15,7 +15,7 @@ use { asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, rc_eq::rc_eq, }, - video::{gbm::GBM_BO_USE_RENDERING, ModifiedFormat, INVALID_MODIFIER}, + video::gbm::GBM_BO_USE_RENDERING, wire::{ wp_fractional_scale_v1::PreferredScale, zwlr_layer_surface_v1::Configure, ZwpLinuxBufferParamsV1Id, @@ -694,21 +694,18 @@ impl WindowData { let width = (self.width.get() as f64 * self.scale.get().to_f64()).round() as i32; let height = (self.height.get() as f64 * self.scale.get().to_f64()).round() as i32; for _ in 0..NUM_BUFFERS { - let format = ModifiedFormat { - format: ARGB8888, - modifier: INVALID_MODIFIER, - }; - let bo = match ctx - .ctx - .gbm() - .create_bo(width, height, &format, GBM_BO_USE_RENDERING) - { - Ok(b) => b, - Err(e) => { - log::error!("Could not allocate dmabuf: {}", ErrorFmt(e)); - return; - } - }; + let bo = + match ctx + .ctx + .gbm() + .create_bo(width, height, ARGB8888, &[], GBM_BO_USE_RENDERING) + { + Ok(b) => b, + Err(e) => { + log::error!("Could not allocate dmabuf: {}", ErrorFmt(e)); + return; + } + }; let img = match ctx.ctx.clone().dmabuf_img(bo.dmabuf()) { Ok(b) => b, Err(e) => { diff --git a/src/screenshoter.rs b/src/screenshoter.rs index b42c7a15..25327861 100644 --- a/src/screenshoter.rs +++ b/src/screenshoter.rs @@ -7,7 +7,6 @@ use { video::{ drm::DrmError, gbm::{GbmBo, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING}, - ModifiedFormat, INVALID_MODIFIER, }, }, std::{ops::Deref, rc::Rc}, @@ -43,15 +42,12 @@ pub fn take_screenshot(state: &State) -> Result if extents.is_empty() { return Err(ScreenshooterError::EmptyDisplay); } - let format = ModifiedFormat { - format: XRGB8888, - modifier: INVALID_MODIFIER, - }; let gbm = ctx.gbm(); let bo = gbm.create_bo( extents.width(), extents.height(), - &format, + XRGB8888, + &[], GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR, )?; let fb = ctx.clone().dmabuf_fb(bo.dmabuf())?; diff --git a/src/video.rs b/src/video.rs index 68f27e97..3c4d318e 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,5 +1,3 @@ -use crate::format::Format; - pub mod dmabuf; pub mod drm; pub mod gbm; @@ -9,9 +7,3 @@ pub type Modifier = u64; pub const INVALID_MODIFIER: Modifier = 0x00ff_ffff_ffff_ffff; #[allow(dead_code)] pub const LINEAR_MODIFIER: Modifier = 0; - -#[derive(Copy, Clone)] -pub struct ModifiedFormat { - pub format: &'static Format, - pub modifier: Modifier, -} diff --git a/src/video/drm.rs b/src/video/drm.rs index 24d55df2..4968fa52 100644 --- a/src/video/drm.rs +++ b/src/video/drm.rs @@ -35,8 +35,11 @@ use crate::{ utils::{buf::Buf, errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt}, video::{ dmabuf::DmaBuf, - drm::sys::{get_version, DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH}, - INVALID_MODIFIER, + drm::sys::{ + drm_format_modifier, drm_format_modifier_blob, get_version, DRM_CAP_CURSOR_HEIGHT, + DRM_CAP_CURSOR_WIDTH, FORMAT_BLOB_CURRENT, + }, + Modifier, INVALID_MODIFIER, }, }; pub use sys::{ @@ -106,6 +109,8 @@ pub enum DrmError { InvalidRead, #[error("Could not determine the drm version")] Version(#[source] OsError), + #[error("Format of IN_FORMATS property is invalid")] + InFormats, } fn render_node_name(fd: c::c_int) -> Result { @@ -174,6 +179,11 @@ impl Drm { } } +pub struct InFormat { + pub format: u32, + pub modifiers: Vec, +} + pub struct DrmMaster { drm: Drm, u32_bufs: Stack>, @@ -373,6 +383,64 @@ impl DrmMaster { } } + pub fn get_in_formats(&self, property: u32) -> Result, DrmError> { + let blob = self.getblob_vec::(DrmBlob(property))?; + let header: drm_format_modifier_blob = match uapi::pod_read_init(blob.as_bytes()) { + Ok(h) => h, + Err(_) => { + log::error!("Header of IN_FORMATS blob doesn't fit in the blob"); + return Err(DrmError::InFormats); + } + }; + if header.version != FORMAT_BLOB_CURRENT { + log::error!( + "Header of IN_FORMATS has an invalid version: {}", + header.version + ); + return Err(DrmError::InFormats); + } + let formats_start = header.formats_offset as usize; + let formats_end = formats_start + .wrapping_add((header.count_formats as usize).wrapping_mul(mem::size_of::())); + let modifiers_start = header.modifiers_offset as usize; + let modifiers_end = modifiers_start.wrapping_add( + (header.count_modifiers as usize).wrapping_mul(mem::size_of::()), + ); + if blob.len() < formats_end || formats_end < formats_start { + log::error!("Formats of IN_FORMATS blob don't fit in the blob"); + return Err(DrmError::InFormats); + } + if blob.len() < modifiers_end || modifiers_end < modifiers_start { + log::error!("Formats of IN_FORMATS blob don't fit in the blob"); + return Err(DrmError::InFormats); + } + let mut formats: Vec<_> = uapi::pod_iter::(&blob[formats_start..formats_end]) + .unwrap() + .map(|f| InFormat { + format: f, + modifiers: vec![], + }) + .collect(); + let modifiers = + uapi::pod_iter::(&blob[modifiers_start..modifiers_end]) + .unwrap(); + for modifier in modifiers { + let offset = modifier.offset as usize; + let mut indices = modifier.formats; + while indices != 0 { + let idx = indices.trailing_zeros(); + indices &= !(1 << idx); + let idx = idx as usize + offset; + if idx >= formats.len() { + log::error!("Modifier offset is out of bounds"); + return Err(DrmError::InFormats); + } + formats[idx].modifiers.push(modifier.modifier); + } + } + Ok(formats) + } + #[allow(clippy::await_holding_refcell_ref)] pub async fn event(&self) -> Result, DrmError> { if self.events.is_empty() { diff --git a/src/video/drm/sys.rs b/src/video/drm/sys.rs index 3c792857..e33dd6c2 100644 --- a/src/video/drm/sys.rs +++ b/src/video/drm/sys.rs @@ -1132,3 +1132,27 @@ pub fn get_version(fd: c::c_int) -> Result { desc: desc.into(), }) } + +pub const FORMAT_BLOB_CURRENT: u32 = 1; + +#[repr(C)] +pub struct drm_format_modifier_blob { + pub version: u32, + pub flags: u32, + pub count_formats: u32, + pub formats_offset: u32, + pub count_modifiers: u32, + pub modifiers_offset: u32, +} + +unsafe impl Pod for drm_format_modifier_blob {} + +#[repr(C)] +pub struct drm_format_modifier { + pub formats: u64, + pub offset: u32, + pub pad: u32, + pub modifier: u64, +} + +unsafe impl Pod for drm_format_modifier {} diff --git a/src/video/gbm.rs b/src/video/gbm.rs index 840bfad9..c0f5fc1a 100644 --- a/src/video/gbm.rs +++ b/src/video/gbm.rs @@ -2,12 +2,12 @@ use { crate::{ - format::formats, + format::{formats, Format}, utils::oserror::OsError, video::{ dmabuf::{DmaBuf, DmaBufPlane, PlaneVec}, drm::{Drm, DrmError}, - ModifiedFormat, INVALID_MODIFIER, + Modifier, }, }, std::{ @@ -27,7 +27,7 @@ pub enum GbmError { #[error("Cloud not create a gbm device")] CreateDevice, #[error("Cloud not create a gbm buffer")] - CreateBo, + CreateBo(#[source] OsError), #[error("gbm buffer has an unknown format")] UnknownFormat, #[error("Could not retrieve a drm-buf fd")] @@ -199,26 +199,27 @@ impl GbmDevice { &self, width: i32, height: i32, - format: &ModifiedFormat, + format: &Format, + modifiers: &[Modifier], usage: u32, ) -> Result { unsafe { - let (modifiers, n_modifiers) = if format.modifier == INVALID_MODIFIER { + let (modifiers, n_modifiers) = if modifiers.is_empty() { (ptr::null(), 0) } else { - (&format.modifier as _, 1) + (modifiers.as_ptr() as _, modifiers.len() as _) }; let bo = gbm_bo_create_with_modifiers2( self.dev, width as _, height as _, - format.format.drm, + format.drm, modifiers, n_modifiers, usage, ); if bo.is_null() { - return Err(GbmError::CreateBo); + return Err(GbmError::CreateBo(OsError::default())); } let bo = BoHolder { bo }; let dma = export_bo(bo.bo)?; @@ -250,7 +251,7 @@ impl GbmDevice { usage, ); if bo.is_null() { - return Err(GbmError::CreateBo); + return Err(GbmError::CreateBo(OsError::default())); } let bo = BoHolder { bo }; Ok(GbmBo {