// Copyright 2018 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! A crate for creating [DRM](https://en.wikipedia.org/wiki/Direct_Rendering_Manager) managed //! buffer objects. Such objects are useful for exporting as DMABUFs/prime FDs, texturing, render //! targets, memory mapping, and scanout. //! //! # Examples //! //! ```rust //! # use std::error::Error; //! # use std::fs::File; //! # use std::result::Result; //! # use gpu_buffer::*; //! # fn test() -> Result<(), Box> { //! let drm_card = File::open("/dev/dri/card0")?; //! let device = Device::new(drm_card).map_err(|_| "failed to create device")?; //! let bo = device //! .create_buffer(1024, //! 512, //! Format::new(b'X', b'R', b'2', b'4'), //! Flags::empty().use_scanout(true)) //! .map_err(|_| "failed to create buffer")?; //! assert_eq!(bo.width(), 1024); //! assert_eq!(bo.height(), 512); //! assert_eq!(bo.format(), Format::new(b'X', b'R', b'2', b'4')); //! assert_eq!(bo.num_planes(), 1); //! # Ok(()) //! # } //! ``` mod drm_formats; mod raw; pub mod rendernode; use std::ffi::CStr; use std::fmt::{self, Display}; use std::fs::File; use std::os::raw::c_char; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::rc::Rc; use std::result::Result; use data_model::VolatileMemoryError; use crate::drm_formats::*; use crate::raw::*; #[derive(Debug)] pub enum Error { GbmFailed, ExportFailed(sys_util::Error), MapFailed, UnknownFormat(Format), CheckedArithmetic { field1: (&'static str, usize), field2: (&'static str, usize), op: &'static str, }, InvalidPrecondition { field1: (&'static str, usize), field2: (&'static str, usize), op: &'static str, }, Memcopy(VolatileMemoryError), } impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Error::*; match self { GbmFailed => write!(f, "internal GBM failure"), ExportFailed(e) => write!(f, "export failed: {}", e), MapFailed => write!(f, "map failed"), CheckedArithmetic { field1: (label1, value1), field2: (label2, value2), op, } => write!( f, "arithmetic failed: {}({}) {} {}({})", label1, value1, op, label2, value2 ), InvalidPrecondition { field1: (label1, value1), field2: (label2, value2), op, } => write!( f, "invalid precondition: {}({}) {} {}({})", label1, value1, op, label2, value2 ), UnknownFormat(format) => write!(f, "unknown format {:?}", format), Memcopy(e) => write!(f, "error copying memory: {}", e), } } } /// A [fourcc](https://en.wikipedia.org/wiki/FourCC) format identifier. #[derive(Copy, Clone, Eq, PartialEq)] pub struct Format(u32); impl Format { /// Constructs a format identifer using a fourcc byte sequence. /// /// # Examples /// /// ```rust /// use gpu_buffer::Format; /// /// let format = Format::new(b'X', b'R', b'2', b'4'); /// println!("format: {:?}", format); /// ``` #[inline(always)] pub fn new(a: u8, b: u8, c: u8, d: u8) -> Format { Format(a as u32 | (b as u32) << 8 | (c as u32) << 16 | (d as u32) << 24) } /// Returns the fourcc code as a sequence of bytes. #[inline(always)] pub fn to_bytes(&self) -> [u8; 4] { let f = self.0; [f as u8, (f >> 8) as u8, (f >> 16) as u8, (f >> 24) as u8] } /// Returns the number of bytes per pixel for the given plane, suitable for making copies /// to/from the plane. pub fn bytes_per_pixel(&self, plane: usize) -> Option { let b = self.to_bytes(); // NV12 and NV21 have 2 planes with 1 byte per pixel. if (b == DRM_FORMAT_NV12 || b == DRM_FORMAT_NV21) && plane < 2 { return Some(1); } // YVU420 has 3 planes, all with the same 1 byte per pixel. if b == DRM_FORMAT_YVU420 && plane < 3 { return Some(1); } if plane != 0 { return None; } let bpp = match self.to_bytes() { DRM_FORMAT_BGR233 => 1, DRM_FORMAT_C8 => 1, DRM_FORMAT_R8 => 1, DRM_FORMAT_RGB332 => 1, DRM_FORMAT_ABGR1555 => 2, DRM_FORMAT_ABGR4444 => 2, DRM_FORMAT_ARGB1555 => 2, DRM_FORMAT_ARGB4444 => 2, DRM_FORMAT_BGR565 => 2, DRM_FORMAT_BGRA4444 => 2, DRM_FORMAT_BGRA5551 => 2, DRM_FORMAT_BGRX4444 => 2, DRM_FORMAT_BGRX5551 => 2, DRM_FORMAT_GR88 => 2, DRM_FORMAT_RG88 => 2, DRM_FORMAT_RGB565 => 2, DRM_FORMAT_RGBA4444 => 2, DRM_FORMAT_RGBA5551 => 2, DRM_FORMAT_RGBX4444 => 2, DRM_FORMAT_RGBX5551 => 2, DRM_FORMAT_UYVY => 2, DRM_FORMAT_VYUY => 2, DRM_FORMAT_XBGR1555 => 2, DRM_FORMAT_XBGR4444 => 2, DRM_FORMAT_XRGB1555 => 2, DRM_FORMAT_XRGB4444 => 2, DRM_FORMAT_YUYV => 2, DRM_FORMAT_YVYU => 2, DRM_FORMAT_BGR888 => 3, DRM_FORMAT_RGB888 => 3, DRM_FORMAT_ABGR2101010 => 4, DRM_FORMAT_ABGR8888 => 4, DRM_FORMAT_ARGB2101010 => 4, DRM_FORMAT_ARGB8888 => 4, DRM_FORMAT_AYUV => 4, DRM_FORMAT_BGRA1010102 => 4, DRM_FORMAT_BGRA8888 => 4, DRM_FORMAT_BGRX1010102 => 4, DRM_FORMAT_BGRX8888 => 4, DRM_FORMAT_RGBA1010102 => 4, DRM_FORMAT_RGBA8888 => 4, DRM_FORMAT_RGBX1010102 => 4, DRM_FORMAT_RGBX8888 => 4, DRM_FORMAT_XBGR2101010 => 4, DRM_FORMAT_XBGR8888 => 4, DRM_FORMAT_XRGB2101010 => 4, DRM_FORMAT_XRGB8888 => 4, _ => return None, }; Some(bpp) } } impl From for Format { fn from(u: u32) -> Format { Format(u) } } impl From for u32 { fn from(f: Format) -> u32 { f.0 } } impl fmt::Debug for Format { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let b = self.to_bytes(); if b.iter().all(u8::is_ascii_graphic) { write!( f, "fourcc({}{}{}{})", b[0] as char, b[1] as char, b[2] as char, b[3] as char ) } else { write!( f, "fourcc(0x{:02x}{:02x}{:02x}{:02x})", b[0], b[1], b[2], b[3] ) } } } /// Usage flags for constructing a buffer object. #[derive(Copy, Clone, Eq, PartialEq)] pub struct Flags(u32); impl Flags { /// Returns empty set of flags. #[inline(always)] pub fn empty() -> Flags { Flags(0) } /// Returns the given set of raw `GBM_BO` flags wrapped in a `Flags` struct. #[inline(always)] pub fn new(raw: u32) -> Flags { Flags(raw) } /// Sets the scanout flag's presence #[inline(always)] pub fn use_scanout(self, e: bool) -> Flags { if e { Flags(self.0 | GBM_BO_USE_SCANOUT) } else { Flags(self.0 & !GBM_BO_USE_SCANOUT) } } /// Sets the cursor flag's presence #[inline(always)] pub fn use_cursor(self, e: bool) -> Flags { if e { Flags(self.0 | GBM_BO_USE_CURSOR) } else { Flags(self.0 & !GBM_BO_USE_CURSOR) } } /// Sets the cursor 64x64 flag's presence #[inline(always)] pub fn use_cursor64(self, e: bool) -> Flags { if e { Flags(self.0 | GBM_BO_USE_CURSOR_64X64) } else { Flags(self.0 & !GBM_BO_USE_CURSOR_64X64) } } /// Sets the rendering flag's presence #[inline(always)] pub fn use_rendering(self, e: bool) -> Flags { if e { Flags(self.0 | GBM_BO_USE_RENDERING) } else { Flags(self.0 & !GBM_BO_USE_RENDERING) } } /// Sets the linear flag's presence #[inline(always)] pub fn use_linear(self, e: bool) -> Flags { if e { Flags(self.0 | GBM_BO_USE_LINEAR) } else { Flags(self.0 & !GBM_BO_USE_LINEAR) } } /// Sets the texturing flag's presence #[inline(always)] pub fn use_texturing(self, e: bool) -> Flags { if e { Flags(self.0 | GBM_BO_USE_TEXTURING) } else { Flags(self.0 & !GBM_BO_USE_TEXTURING) } } } struct DeviceInner { _fd: File, gbm: *mut gbm_device, } impl Drop for DeviceInner { fn drop(self: &mut DeviceInner) { // Safe because DeviceInner is only constructed with a valid gbm_device. unsafe { gbm_device_destroy(self.gbm); } } } /// A device capable of allocating `Buffer`. #[derive(Clone)] pub struct Device(Rc); impl Device { /// Returns a new `Device` using the given `fd` opened from a device in `/dev/dri/`. pub fn new(fd: File) -> Result { // gbm_create_device is safe to call with a valid fd, and we check that a valid one is // returned. The FD is not of the appropriate kind (i.e. not a DRM device), // gbm_create_device should reject it. let gbm = unsafe { gbm_create_device(fd.as_raw_fd()) }; if gbm.is_null() { Err(()) } else { Ok(Device(Rc::new(DeviceInner { _fd: fd, gbm }))) } } /// Copies and returns name of GBM backend. pub fn get_backend_name(&self) -> String { let backend_name: *const c_char = unsafe { gbm_device_get_backend_name(self.0.gbm) }; let c_str: &CStr = unsafe { CStr::from_ptr(backend_name) }; let str_slice: &str = c_str.to_str().unwrap_or(""); str_slice.to_owned() } /// Creates a new buffer with the given metadata. pub fn create_buffer( &self, width: u32, height: u32, format: Format, usage: Flags, ) -> Result { // This is safe because only a valid gbm_device is used and the return value is checked. let bo = unsafe { gbm_bo_create(self.0.gbm, width, height, format.0, usage.0) }; if bo.is_null() { Err(Error::GbmFailed) } else { Ok(Buffer(bo, self.clone())) } } } /// An allocation from a `Device`. pub struct Buffer(*mut gbm_bo, Device); impl Buffer { /// The device pub fn device(&self) -> &Device { &self.1 } /// Width in pixels. pub fn width(&self) -> u32 { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_get_width(self.0) } } /// Height in pixels. pub fn height(&self) -> u32 { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_get_height(self.0) } } /// Length in bytes of one row of the buffer. pub fn stride(&self) -> u32 { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_get_stride(self.0) } } /// `Format` of the buffer. pub fn format(&self) -> Format { // This is always safe to call with a valid gbm_bo pointer. unsafe { Format(gbm_bo_get_format(self.0)) } } /// Format modifier flags for the buffer. pub fn format_modifier(&self) -> u64 { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_get_modifier(self.0) } } /// Number of planes present in this buffer. pub fn num_planes(&self) -> usize { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_get_plane_count(self.0) as usize } } /// Handle as u64 for the given plane. pub fn plane_handle(&self, plane: usize) -> u64 { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_get_handle_for_plane(self.0, plane).u64 } } /// Offset in bytes for the given plane. pub fn plane_offset(&self, plane: usize) -> u32 { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_get_offset(self.0, plane) } } /// Length in bytes of one row for the given plane. pub fn plane_stride(&self, plane: usize) -> u32 { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_get_stride_for_plane(self.0, plane) } } /// Size of a plane, in bytes. pub fn plane_size(&self, plane: usize) -> u32 { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_get_plane_size(self.0, plane) } } /// Exports a new dmabuf/prime file descriptor for the given plane. pub fn export_plane_fd(&self, plane: usize) -> Result { // This is always safe to call with a valid gbm_bo pointer. match unsafe { gbm_bo_get_plane_fd(self.0, plane) } { fd if fd >= 0 => Ok(unsafe { File::from_raw_fd(fd) }), ret => Err(ret), } } } impl Drop for Buffer { fn drop(&mut self) { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_destroy(self.0) } } } impl AsRawFd for Buffer { fn as_raw_fd(&self) -> RawFd { // This is always safe to call with a valid gbm_bo pointer. unsafe { gbm_bo_get_fd(self.0) } } } #[cfg(test)] mod tests { use super::*; use std::fmt::Write; #[test] fn format_debug() { let f = Format::new(b'X', b'R', b'2', b'4'); let mut buf = String::new(); write!(&mut buf, "{:?}", f).unwrap(); assert_eq!(buf, "fourcc(XR24)"); let f = Format::new(0, 1, 2, 16); let mut buf = String::new(); write!(&mut buf, "{:?}", f).unwrap(); assert_eq!(buf, "fourcc(0x00010210)"); } #[test] fn format_bytes_per_pixel() { let f = Format::new(b'X', b'R', b'2', b'4'); assert_eq!(f.bytes_per_pixel(0), Some(4)); assert_eq!(f.bytes_per_pixel(1), None); let f = Format::new(b'N', b'V', b'1', b'2'); assert_eq!(f.bytes_per_pixel(0), Some(1)); assert_eq!(f.bytes_per_pixel(1), Some(1)); assert_eq!(f.bytes_per_pixel(2), None); let f = Format::new(b'R', b'8', b' ', b' '); assert_eq!(f.bytes_per_pixel(0), Some(1)); assert_eq!(f.bytes_per_pixel(1), None); let f = Format::new(b'B', b'G', b'2', b'4'); assert_eq!(f.bytes_per_pixel(0), Some(3)); assert_eq!(f.bytes_per_pixel(1), None); let f = Format::new(b'G', b'R', b'8', b'8'); assert_eq!(f.bytes_per_pixel(0), Some(2)); assert_eq!(f.bytes_per_pixel(1), None); let f = Format::new(b'Z', b'A', b'C', b'H'); assert_eq!(f.bytes_per_pixel(0), None); } #[test] #[ignore] // no access to /dev/dri fn open_device() { let drm_card = File::open("/dev/dri/card0").expect("failed to open card"); Device::new(drm_card).expect("failed to create device with card"); } #[test] #[ignore] // no access to /dev/dri fn create_buffer() { let drm_card = File::open("/dev/dri/card0").expect("failed to open card"); let device = Device::new(drm_card).expect("failed to create device with card"); let bo = device .create_buffer( 1024, 512, Format::new(b'X', b'R', b'2', b'4'), Flags::empty().use_scanout(true), ) .expect("failed to create buffer"); assert_eq!(bo.width(), 1024); assert_eq!(bo.height(), 512); assert_eq!(bo.format(), Format::new(b'X', b'R', b'2', b'4')); assert_eq!(bo.num_planes(), 1); } #[test] #[ignore] // no access to /dev/dri fn export_buffer() { let drm_card = File::open("/dev/dri/card0").expect("failed to open card"); let device = Device::new(drm_card).expect("failed to create device with card"); let bo = device .create_buffer( 1024, 1024, Format::new(b'X', b'R', b'2', b'4'), Flags::empty().use_scanout(true), ) .expect("failed to create buffer"); bo.export_plane_fd(0).expect("failed to export plane"); } }