1
0
Fork 0
forked from wry/wry

metal: use IN_FORMATS plane property

This commit is contained in:
Julian Orth 2023-10-29 16:58:16 +01:00
parent d022d96fbf
commit e0ed29038e
9 changed files with 183 additions and 87 deletions

View file

@ -28,7 +28,7 @@ use {
DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT, DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT,
}, },
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT}, gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT},
ModifiedFormat, INVALID_MODIFIER, Modifier,
}, },
}, },
ahash::{AHashMap, AHashSet}, ahash::{AHashMap, AHashSet},
@ -505,6 +505,12 @@ pub enum PlaneType {
Cursor, Cursor,
} }
#[derive(Debug)]
pub struct PlaneFormat {
_format: &'static Format,
modifiers: Vec<Modifier>,
}
#[derive(Debug)] #[derive(Debug)]
pub struct MetalPlane { pub struct MetalPlane {
pub id: DrmPlane, pub id: DrmPlane,
@ -513,7 +519,7 @@ pub struct MetalPlane {
pub ty: PlaneType, pub ty: PlaneType,
pub possible_crtcs: u32, pub possible_crtcs: u32,
pub formats: AHashMap<u32, &'static Format>, pub formats: AHashMap<u32, PlaneFormat>,
pub assigned: Cell<bool>, pub assigned: Cell<bool>,
@ -755,19 +761,36 @@ fn create_crtc(
fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, DrmError> { fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, DrmError> {
let info = master.get_plane_info(plane)?; let info = master.get_plane_info(plane)?;
let props = collect_properties(master, plane)?;
let mut formats = AHashMap::new(); let mut formats = AHashMap::new();
for format in info.format_types { if let Some((_, v)) = props.props.get(b"IN_FORMATS".as_bstr()) {
if let Some(f) = crate::format::formats().get(&format) { for format in master.get_in_formats(*v as _)? {
formats.insert(format, *f); if format.modifiers.is_empty() {
} else { continue;
// log::warn!( }
// "{:?} supports unknown format '{:?}'", if let Some(f) = crate::format::formats().get(&format.format) {
// plane, formats.insert(
// crate::format::debug(format) 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()) { let ty = match props.props.get(b"type".as_bstr()) {
Some((def, val)) => match &def.ty { Some((def, val)) => match &def.ty {
DrmPropertyType::Enum { values, .. } => 'ty: { DrmPropertyType::Enum { values, .. } => 'ty: {
@ -1569,30 +1592,33 @@ impl MetalBackend {
fn create_scanout_buffers( fn create_scanout_buffers(
&self, &self,
dev: &Rc<MetalDrmDevice>, dev: &Rc<MetalDrmDevice>,
format: &ModifiedFormat, format: &Format,
modifiers: &[Modifier],
width: i32, width: i32,
height: i32, height: i32,
ctx: &MetalRenderContext, ctx: &MetalRenderContext,
cursor: bool, cursor: bool,
) -> Result<[RenderBuffer; 2], MetalError> { ) -> 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()?]) Ok([create()?, create()?])
} }
fn create_scanout_buffer( fn create_scanout_buffer(
&self, &self,
dev: &Rc<MetalDrmDevice>, dev: &Rc<MetalDrmDevice>,
format: &ModifiedFormat, format: &Format,
modifiers: &[Modifier],
width: i32, width: i32,
height: i32, height: i32,
render_ctx: &MetalRenderContext, render_ctx: &MetalRenderContext,
cursor: bool, cursor: bool,
) -> Result<RenderBuffer, MetalError> { ) -> Result<RenderBuffer, MetalError> {
let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT; let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
if cursor { if cursor && modifiers.is_empty() {
usage |= GBM_BO_USE_LINEAR; 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 { let dev_bo = match dev_bo {
Ok(b) => b, Ok(b) => b,
Err(e) => return Err(MetalError::ScanoutBuffer(e)), Err(e) => return Err(MetalError::ScanoutBuffer(e)),
@ -1619,7 +1645,10 @@ impl MetalBackend {
} else { } else {
// Create a _bridge_ BO in the render device // Create a _bridge_ BO in the render device
usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR; 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 { let render_bo = match render_bo {
Ok(b) => b, Ok(b) => b,
Err(e) => return Err(MetalError::ScanoutBuffer(e)), Err(e) => return Err(MetalError::ScanoutBuffer(e)),
@ -1716,46 +1745,45 @@ impl MetalBackend {
return Ok(()); return Ok(());
} }
}; };
let primary_plane = 'primary_plane: { let (primary_plane, primary_modifiers) = 'primary_plane: {
for plane in crtc.possible_planes.values() { for plane in crtc.possible_planes.values() {
if plane.ty == PlaneType::Primary if plane.ty == PlaneType::Primary && !plane.assigned.get() {
&& !plane.assigned.get() if let Some(format) = plane.formats.get(&XRGB8888.drm) {
&& plane.formats.contains_key(&XRGB8888.drm) break 'primary_plane (plane.clone(), &format.modifiers);
{ }
break 'primary_plane plane.clone();
} }
} }
return Err(MetalError::NoPrimaryPlaneForConnector); return Err(MetalError::NoPrimaryPlaneForConnector);
}; };
let buffers = Rc::new(self.create_scanout_buffers( let buffers = Rc::new(self.create_scanout_buffers(
&connector.dev, &connector.dev,
&ModifiedFormat { XRGB8888,
format: XRGB8888, primary_modifiers,
modifier: INVALID_MODIFIER,
},
mode.hdisplay as _, mode.hdisplay as _,
mode.vdisplay as _, mode.vdisplay as _,
ctx, ctx,
false, false,
)?); )?);
let mut cursor_plane = None; let mut cursor_plane = None;
let mut cursor_modifiers = &[][..];
for plane in crtc.possible_planes.values() { for plane in crtc.possible_planes.values() {
if plane.ty == PlaneType::Cursor if plane.ty == PlaneType::Cursor
&& !plane.assigned.get() && !plane.assigned.get()
&& plane.formats.contains_key(&ARGB8888.drm) && plane.formats.contains_key(&ARGB8888.drm)
{ {
cursor_plane = Some(plane.clone()); if let Some(format) = plane.formats.get(&ARGB8888.drm) {
break; cursor_plane = Some(plane.clone());
cursor_modifiers = &format.modifiers[..];
break;
}
} }
} }
let mut cursor_buffers = None; let mut cursor_buffers = None;
if cursor_plane.is_some() { if cursor_plane.is_some() {
let res = self.create_scanout_buffers( let res = self.create_scanout_buffers(
&connector.dev, &connector.dev,
&ModifiedFormat { ARGB8888,
format: ARGB8888, cursor_modifiers,
modifier: INVALID_MODIFIER,
},
connector.dev.cursor_width as _, connector.dev.cursor_width as _,
connector.dev.cursor_height as _, connector.dev.cursor_height as _,
ctx, ctx,

View file

@ -21,7 +21,6 @@ use {
video::{ video::{
drm::{ConnectorType, Drm, DrmError, DrmVersion}, drm::{ConnectorType, Drm, DrmError, DrmVersion},
gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING}, gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING},
ModifiedFormat, INVALID_MODIFIER,
}, },
wire_xcon::{ wire_xcon::{
ChangeProperty, ChangeWindowAttributes, ConfigureNotify, CreateCursor, CreatePixmap, ChangeProperty, ChangeWindowAttributes, ConfigureNotify, CreateCursor, CreatePixmap,
@ -376,15 +375,11 @@ impl XBackend {
width: i32, width: i32,
height: i32, height: i32,
) -> Result<[XImage; 2], XBackendError> { ) -> Result<[XImage; 2], XBackendError> {
let format = ModifiedFormat {
format: XRGB8888,
modifier: INVALID_MODIFIER,
};
let mut images = [None, None]; let mut images = [None, None];
for image in &mut images { for image in &mut images {
let bo = self let bo = self
.gbm .gbm
.create_bo(width, height, &format, GBM_BO_USE_RENDERING)?; .create_bo(width, height, XRGB8888, &[], GBM_BO_USE_RENDERING)?;
let dma = bo.dmabuf(); let dma = bo.dmabuf();
assert!(dma.planes.len() == 1); assert!(dma.planes.len() == 1);
let plane = dma.planes.first().unwrap(); let plane = dma.planes.first().unwrap();

View file

@ -17,7 +17,6 @@ use {
video::{ video::{
dmabuf::DmaBuf, dmabuf::DmaBuf,
gbm::{GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING}, gbm::{GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
ModifiedFormat, INVALID_MODIFIER,
}, },
wire::{jay_screencast::*, JayScreencastId}, wire::{jay_screencast::*, JayScreencastId},
}, },
@ -199,17 +198,13 @@ impl JayScreencast {
let mode = output.global.mode.get(); let mode = output.global.mode.get();
let num = 3; let num = 3;
for _ in 0..num { for _ in 0..num {
let format = ModifiedFormat {
format: XRGB8888,
modifier: INVALID_MODIFIER,
};
let mut flags = GBM_BO_USE_RENDERING; let mut flags = GBM_BO_USE_RENDERING;
if self.linear.get() { if self.linear.get() {
flags |= GBM_BO_USE_LINEAR; flags |= GBM_BO_USE_LINEAR;
} }
let buffer = ctx let buffer = ctx
.gbm() .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()?; let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?;
buffers.push(ScreencastBuffer { buffers.push(ScreencastBuffer {
dmabuf: buffer.dmabuf().clone(), dmabuf: buffer.dmabuf().clone(),

View file

@ -15,7 +15,7 @@ use {
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap, asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
errorfmt::ErrorFmt, rc_eq::rc_eq, errorfmt::ErrorFmt, rc_eq::rc_eq,
}, },
video::{gbm::GBM_BO_USE_RENDERING, ModifiedFormat, INVALID_MODIFIER}, video::gbm::GBM_BO_USE_RENDERING,
wire::{ wire::{
wp_fractional_scale_v1::PreferredScale, zwlr_layer_surface_v1::Configure, wp_fractional_scale_v1::PreferredScale, zwlr_layer_surface_v1::Configure,
ZwpLinuxBufferParamsV1Id, ZwpLinuxBufferParamsV1Id,
@ -694,21 +694,18 @@ impl WindowData {
let width = (self.width.get() as f64 * self.scale.get().to_f64()).round() as i32; 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; let height = (self.height.get() as f64 * self.scale.get().to_f64()).round() as i32;
for _ in 0..NUM_BUFFERS { for _ in 0..NUM_BUFFERS {
let format = ModifiedFormat { let bo =
format: ARGB8888, match ctx
modifier: INVALID_MODIFIER, .ctx
}; .gbm()
let bo = match ctx .create_bo(width, height, ARGB8888, &[], GBM_BO_USE_RENDERING)
.ctx {
.gbm() Ok(b) => b,
.create_bo(width, height, &format, GBM_BO_USE_RENDERING) Err(e) => {
{ log::error!("Could not allocate dmabuf: {}", ErrorFmt(e));
Ok(b) => b, return;
Err(e) => { }
log::error!("Could not allocate dmabuf: {}", ErrorFmt(e)); };
return;
}
};
let img = match ctx.ctx.clone().dmabuf_img(bo.dmabuf()) { let img = match ctx.ctx.clone().dmabuf_img(bo.dmabuf()) {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {

View file

@ -7,7 +7,6 @@ use {
video::{ video::{
drm::DrmError, drm::DrmError,
gbm::{GbmBo, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING}, gbm::{GbmBo, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
ModifiedFormat, INVALID_MODIFIER,
}, },
}, },
std::{ops::Deref, rc::Rc}, std::{ops::Deref, rc::Rc},
@ -43,15 +42,12 @@ pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError>
if extents.is_empty() { if extents.is_empty() {
return Err(ScreenshooterError::EmptyDisplay); return Err(ScreenshooterError::EmptyDisplay);
} }
let format = ModifiedFormat {
format: XRGB8888,
modifier: INVALID_MODIFIER,
};
let gbm = ctx.gbm(); let gbm = ctx.gbm();
let bo = gbm.create_bo( let bo = gbm.create_bo(
extents.width(), extents.width(),
extents.height(), extents.height(),
&format, XRGB8888,
&[],
GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR,
)?; )?;
let fb = ctx.clone().dmabuf_fb(bo.dmabuf())?; let fb = ctx.clone().dmabuf_fb(bo.dmabuf())?;

View file

@ -1,5 +1,3 @@
use crate::format::Format;
pub mod dmabuf; pub mod dmabuf;
pub mod drm; pub mod drm;
pub mod gbm; pub mod gbm;
@ -9,9 +7,3 @@ pub type Modifier = u64;
pub const INVALID_MODIFIER: Modifier = 0x00ff_ffff_ffff_ffff; pub const INVALID_MODIFIER: Modifier = 0x00ff_ffff_ffff_ffff;
#[allow(dead_code)] #[allow(dead_code)]
pub const LINEAR_MODIFIER: Modifier = 0; pub const LINEAR_MODIFIER: Modifier = 0;
#[derive(Copy, Clone)]
pub struct ModifiedFormat {
pub format: &'static Format,
pub modifier: Modifier,
}

View file

@ -35,8 +35,11 @@ use crate::{
utils::{buf::Buf, errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt}, utils::{buf::Buf, errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt},
video::{ video::{
dmabuf::DmaBuf, dmabuf::DmaBuf,
drm::sys::{get_version, DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH}, drm::sys::{
INVALID_MODIFIER, 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::{ pub use sys::{
@ -106,6 +109,8 @@ pub enum DrmError {
InvalidRead, InvalidRead,
#[error("Could not determine the drm version")] #[error("Could not determine the drm version")]
Version(#[source] OsError), Version(#[source] OsError),
#[error("Format of IN_FORMATS property is invalid")]
InFormats,
} }
fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> { fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
@ -174,6 +179,11 @@ impl Drm {
} }
} }
pub struct InFormat {
pub format: u32,
pub modifiers: Vec<Modifier>,
}
pub struct DrmMaster { pub struct DrmMaster {
drm: Drm, drm: Drm,
u32_bufs: Stack<Vec<u32>>, u32_bufs: Stack<Vec<u32>>,
@ -373,6 +383,64 @@ impl DrmMaster {
} }
} }
pub fn get_in_formats(&self, property: u32) -> Result<Vec<InFormat>, DrmError> {
let blob = self.getblob_vec::<u8>(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::<u32>()));
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::<drm_format_modifier>()),
);
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::<u32, _>(&blob[formats_start..formats_end])
.unwrap()
.map(|f| InFormat {
format: f,
modifiers: vec![],
})
.collect();
let modifiers =
uapi::pod_iter::<drm_format_modifier, _>(&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)] #[allow(clippy::await_holding_refcell_ref)]
pub async fn event(&self) -> Result<Option<DrmEvent>, DrmError> { pub async fn event(&self) -> Result<Option<DrmEvent>, DrmError> {
if self.events.is_empty() { if self.events.is_empty() {

View file

@ -1132,3 +1132,27 @@ pub fn get_version(fd: c::c_int) -> Result<DrmVersion, OsError> {
desc: desc.into(), 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 {}

View file

@ -2,12 +2,12 @@
use { use {
crate::{ crate::{
format::formats, format::{formats, Format},
utils::oserror::OsError, utils::oserror::OsError,
video::{ video::{
dmabuf::{DmaBuf, DmaBufPlane, PlaneVec}, dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
drm::{Drm, DrmError}, drm::{Drm, DrmError},
ModifiedFormat, INVALID_MODIFIER, Modifier,
}, },
}, },
std::{ std::{
@ -27,7 +27,7 @@ pub enum GbmError {
#[error("Cloud not create a gbm device")] #[error("Cloud not create a gbm device")]
CreateDevice, CreateDevice,
#[error("Cloud not create a gbm buffer")] #[error("Cloud not create a gbm buffer")]
CreateBo, CreateBo(#[source] OsError),
#[error("gbm buffer has an unknown format")] #[error("gbm buffer has an unknown format")]
UnknownFormat, UnknownFormat,
#[error("Could not retrieve a drm-buf fd")] #[error("Could not retrieve a drm-buf fd")]
@ -199,26 +199,27 @@ impl GbmDevice {
&self, &self,
width: i32, width: i32,
height: i32, height: i32,
format: &ModifiedFormat, format: &Format,
modifiers: &[Modifier],
usage: u32, usage: u32,
) -> Result<GbmBo, GbmError> { ) -> Result<GbmBo, GbmError> {
unsafe { unsafe {
let (modifiers, n_modifiers) = if format.modifier == INVALID_MODIFIER { let (modifiers, n_modifiers) = if modifiers.is_empty() {
(ptr::null(), 0) (ptr::null(), 0)
} else { } else {
(&format.modifier as _, 1) (modifiers.as_ptr() as _, modifiers.len() as _)
}; };
let bo = gbm_bo_create_with_modifiers2( let bo = gbm_bo_create_with_modifiers2(
self.dev, self.dev,
width as _, width as _,
height as _, height as _,
format.format.drm, format.drm,
modifiers, modifiers,
n_modifiers, n_modifiers,
usage, usage,
); );
if bo.is_null() { if bo.is_null() {
return Err(GbmError::CreateBo); return Err(GbmError::CreateBo(OsError::default()));
} }
let bo = BoHolder { bo }; let bo = BoHolder { bo };
let dma = export_bo(bo.bo)?; let dma = export_bo(bo.bo)?;
@ -250,7 +251,7 @@ impl GbmDevice {
usage, usage,
); );
if bo.is_null() { if bo.is_null() {
return Err(GbmError::CreateBo); return Err(GbmError::CreateBo(OsError::default()));
} }
let bo = BoHolder { bo }; let bo = BoHolder { bo };
Ok(GbmBo { Ok(GbmBo {