blob: dbae7047c27e39da28f76326e33a3f0c84ea7659 [file]
// 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<Error>> {
//! 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(())
//! # }
//! ```
pub 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(base::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<usize> {
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<u32> for Format {
fn from(u: u32) -> Format {
Format(u)
}
}
impl From<Format> 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<DeviceInner>);
impl Device {
/// Returns a new `Device` using the given `fd` opened from a device in `/dev/dri/`.
pub fn new(fd: File) -> Result<Device, ()> {
// 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<Buffer, Error> {
// 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<File, i32> {
// 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");
}
}