use std::os::fd::OwnedFd;
use std::sync::Arc;
use smallvec::SmallVec;
use crate::SurfaceFormatKind;
#[derive(Debug, Clone)]
pub struct DmaBufPlane {
pub fd: Arc<OwnedFd>,
pub offset: u64,
pub stride: u32,
}
impl PartialEq for DmaBufPlane {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.fd, &other.fd)
&& self.offset == other.offset
&& self.stride == other.stride
}
}
impl Eq for DmaBufPlane {}
impl DmaBufPlane {
pub fn new(fd: OwnedFd, offset: u64, stride: u32) -> Self {
Self {
fd: Arc::new(fd),
offset,
stride,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ChromaReconstruction {
#[default]
Bilinear,
LumaGuided,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum YuvMatrix {
#[default]
Bt601,
Bt709,
Bt2020,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum YuvRange {
#[default]
Full,
Limited,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TransferFunction {
#[default]
Srgb,
Linear,
Bt709,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct YuvColorSpace {
pub matrix: YuvMatrix,
pub range: YuvRange,
}
impl YuvColorSpace {
pub const BT601_LIMITED: Self = Self {
matrix: YuvMatrix::Bt601,
range: YuvRange::Limited,
};
pub const BT709_LIMITED: Self = Self {
matrix: YuvMatrix::Bt709,
range: YuvRange::Limited,
};
}
#[derive(Debug, Clone)]
pub struct DmaBufHandle {
pub width: u32,
pub height: u32,
pub format: SurfaceFormatKind,
pub modifier: u64,
pub planes: SmallVec<[DmaBufPlane; 2]>,
pub acquire_fence: Option<Arc<OwnedFd>>,
pub release: Option<Arc<OwnedFd>>,
pub chroma: ChromaReconstruction,
pub color_space: YuvColorSpace,
pub transfer: TransferFunction,
}
impl PartialEq for DmaBufHandle {
fn eq(&self, other: &Self) -> bool {
self.width == other.width
&& self.height == other.height
&& self.format == other.format
&& self.modifier == other.modifier
&& self.planes == other.planes
&& same_descriptor(&self.acquire_fence, &other.acquire_fence)
&& same_descriptor(&self.release, &other.release)
&& self.chroma == other.chroma
&& self.color_space == other.color_space
&& self.transfer == other.transfer
}
}
fn same_descriptor(a: &Option<Arc<OwnedFd>>, b: &Option<Arc<OwnedFd>>) -> bool {
match (a, b) {
(Some(a), Some(b)) => Arc::ptr_eq(a, b),
(None, None) => true,
_ => false,
}
}
impl Eq for DmaBufHandle {}
impl DmaBufHandle {
pub const LINEAR: u64 = 0;
pub fn new(
width: u32,
height: u32,
format: SurfaceFormatKind,
modifier: u64,
planes: impl IntoIterator<Item = DmaBufPlane>,
acquire_fence: Option<OwnedFd>,
) -> Self {
Self {
width,
height,
format,
modifier,
planes: planes.into_iter().collect(),
acquire_fence: acquire_fence.map(Arc::new),
release: None,
chroma: ChromaReconstruction::default(),
color_space: YuvColorSpace::default(),
transfer: if format.is_yuv() {
TransferFunction::Bt709
} else {
TransferFunction::Srgb
},
}
}
pub fn with_chroma(mut self, chroma: ChromaReconstruction) -> Self {
self.chroma = chroma;
self
}
pub fn with_color_space(mut self, color_space: YuvColorSpace) -> Self {
self.color_space = color_space;
self
}
pub fn with_transfer(mut self, transfer: TransferFunction) -> Self {
self.transfer = transfer;
self
}
pub fn with_release(mut self, release: OwnedFd) -> Self {
self.release = Some(Arc::new(release));
self
}
pub fn signal_release(&self) {
#[cfg(target_os = "linux")]
{
use std::io::Write as _;
if let Some(release) = &self.release
&& let Ok(duplicate) = release.try_clone()
{
let _ = std::fs::File::from(duplicate).write_all(&1u64.to_ne_bytes());
}
}
}
pub fn plane_count(&self) -> usize {
self.planes.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{DmaBufCapabilities, DmaBufFormatCapability};
fn descriptor() -> OwnedFd {
std::fs::File::open("/dev/null").expect("/dev/null").into()
}
#[test]
fn linear_is_the_zero_modifier() {
assert_eq!(DmaBufHandle::LINEAR, 0);
}
#[test]
fn new_collects_the_planes_it_is_given() {
let handle = DmaBufHandle::new(
4,
2,
SurfaceFormatKind::nv12(),
DmaBufHandle::LINEAR,
[
DmaBufPlane::new(descriptor(), 0, 4),
DmaBufPlane::new(descriptor(), 8, 4),
],
None,
);
assert_eq!(handle.plane_count(), 2);
assert_eq!((handle.width, handle.height), (4, 2));
assert_eq!(handle.format, SurfaceFormatKind::nv12());
assert!(handle.acquire_fence.is_none());
}
#[test]
fn new_wraps_the_acquire_fence() {
let handle = DmaBufHandle::new(
1,
1,
SurfaceFormatKind::rgba8(),
DmaBufHandle::LINEAR,
[DmaBufPlane::new(descriptor(), 0, 4)],
Some(descriptor()),
);
assert!(handle.acquire_fence.is_some());
}
#[test]
fn a_clone_shares_the_descriptor_rather_than_duplicating_it() {
let plane = DmaBufPlane::new(descriptor(), 0, 4);
let cloned = plane.clone();
assert!(Arc::ptr_eq(&plane.fd, &cloned.fd));
}
#[test]
fn equality_is_the_descriptor_and_the_layout() {
let fd = Arc::new(descriptor());
let plane = DmaBufPlane {
fd: fd.clone(),
offset: 0,
stride: 4,
};
let same = DmaBufPlane {
fd: fd.clone(),
offset: 0,
stride: 4,
};
let elsewhere = DmaBufPlane {
fd,
offset: 4,
stride: 4,
};
assert_eq!(plane, same);
assert_ne!(plane, elsewhere);
}
#[test]
fn a_handle_becomes_a_surface_source() {
let handle = DmaBufHandle::new(
1,
1,
SurfaceFormatKind::rgba8(),
DmaBufHandle::LINEAR,
[DmaBufPlane::new(descriptor(), 0, 4)],
None,
);
let source: crate::SurfaceSource = handle.clone().into();
assert_eq!(source, crate::SurfaceSource::DmaBuf(handle));
}
#[test]
fn a_handle_defaults_to_bt601_full_range() {
let handle = DmaBufHandle::new(
2,
2,
SurfaceFormatKind::nv12(),
DmaBufHandle::LINEAR,
[
DmaBufPlane::new(descriptor(), 0, 2),
DmaBufPlane::new(descriptor(), 4, 2),
],
None,
);
assert_eq!(
handle.color_space,
YuvColorSpace {
matrix: YuvMatrix::Bt601,
range: YuvRange::Full,
},
);
}
#[test]
fn the_colour_space_is_part_of_identity() {
let handle = DmaBufHandle::new(
2,
2,
SurfaceFormatKind::nv12(),
DmaBufHandle::LINEAR,
[
DmaBufPlane::new(descriptor(), 0, 2),
DmaBufPlane::new(descriptor(), 4, 2),
],
None,
);
let declared = handle
.clone()
.with_color_space(YuvColorSpace::BT709_LIMITED);
assert_eq!(declared.color_space, YuvColorSpace::BT709_LIMITED);
assert_ne!(handle, declared);
}
#[test]
fn the_transfer_defaults_to_the_formats_curve() {
let rgb = DmaBufHandle::new(
1,
1,
SurfaceFormatKind::bgra8(),
DmaBufHandle::LINEAR,
[DmaBufPlane::new(descriptor(), 0, 4)],
None,
);
assert_eq!(rgb.transfer, TransferFunction::Srgb);
let yuv = DmaBufHandle::new(
2,
2,
SurfaceFormatKind::nv12(),
DmaBufHandle::LINEAR,
[
DmaBufPlane::new(descriptor(), 0, 2),
DmaBufPlane::new(descriptor(), 4, 2),
],
None,
);
assert_eq!(yuv.transfer, TransferFunction::Bt709);
}
#[test]
fn the_transfer_is_part_of_identity() {
let handle = DmaBufHandle::new(
1,
1,
SurfaceFormatKind::bgra8(),
DmaBufHandle::LINEAR,
[DmaBufPlane::new(descriptor(), 0, 4)],
None,
);
let linear = handle.clone().with_transfer(TransferFunction::Linear);
assert_eq!(linear.transfer, TransferFunction::Linear);
assert_ne!(handle, linear);
}
#[test]
fn a_handle_carries_a_release_descriptor_when_asked() {
let handle = DmaBufHandle::new(
1,
1,
SurfaceFormatKind::rgba8(),
DmaBufHandle::LINEAR,
[DmaBufPlane::new(descriptor(), 0, 4)],
None,
);
assert!(handle.release.is_none());
let released = handle.clone().with_release(descriptor());
assert!(released.release.is_some());
assert_ne!(handle, released);
}
#[test]
fn a_capability_report_answers_by_format_and_modifier() {
let caps = DmaBufCapabilities::new(vec![
DmaBufFormatCapability {
format: SurfaceFormatKind::nv12(),
modifiers: vec![DmaBufHandle::LINEAR, 0x0100_0000_0000_0002],
},
DmaBufFormatCapability {
format: SurfaceFormatKind::bgra8(),
modifiers: vec![DmaBufHandle::LINEAR],
},
]);
assert!(caps.supports(&SurfaceFormatKind::nv12(), DmaBufHandle::LINEAR));
assert!(caps.supports(&SurfaceFormatKind::nv12(), 0x0100_0000_0000_0002));
assert!(!caps.supports(&SurfaceFormatKind::nv12(), 0x10));
assert!(caps.supports(&SurfaceFormatKind::bgra8(), DmaBufHandle::LINEAR));
assert!(!caps.supports(&SurfaceFormatKind::i420(), DmaBufHandle::LINEAR));
assert_eq!(
caps.modifiers(&SurfaceFormatKind::bgra8()),
Some(&[DmaBufHandle::LINEAR][..])
);
assert_eq!(caps.modifiers(&SurfaceFormatKind::rgba8()), None);
}
}