blob: 16aae50fab613700ce7c03f9726bbf2284f09493 [file] [log] [blame]
// 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(())
//! # }
//! ```
mod drm_formats;
mod raw;
pub mod rendernode;
use std::cmp::min;
use std::fmt::{self, Display};
use std::fs::File;
use std::isize;
use std::os::raw::c_void;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::ptr::null_mut;
use std::rc::Rc;
use std::result::Result;
use data_model::{VolatileMemory, VolatileMemoryError, VolatileSlice};
use crate::drm_formats::*;
use crate::raw::*;
const MAP_FAILED: *mut c_void = (-1isize as *mut _);
#[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),
}
}
}
macro_rules! checked_arithmetic {
($x:ident $op:ident $y:ident $op_name:expr) => {
$x.$op($y).ok_or_else(|| Error::CheckedArithmetic {
field1: (stringify!($x), $x as usize),
field2: (stringify!($y), $y as usize),
op: $op_name,
})
};
($x:ident + $y:ident) => {
checked_arithmetic!($x checked_add $y "+")
};
($x:ident - $y:ident) => {
checked_arithmetic!($x checked_sub $y "-")
};
($x:ident * $y:ident) => {
checked_arithmetic!($x checked_mul $y "*")
};
}
macro_rules! checked_range {
($x:expr; <= $y:expr) => {
if $x <= $y {
Ok(())
} else {
Err(Error::InvalidPrecondition {
field1: (stringify!($x), $x as usize),
field2: (stringify!($y), $y as usize),
op: "<=",
})
}
};
($x:ident <= $y:ident) => {
check_range!($x; <= $y)
};
}
/// 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 })))
}
}
/// 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) }
}
/// Length in bytes of the stride or tiling.
pub fn stride_or_tiling(&self) -> u32 {
// This is always safe to call with a valid gbm_bo pointer.
unsafe { gbm_bo_get_stride_or_tiling(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) }
}
/// 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),
}
}
fn map(
&self,
x: u32,
y: u32,
width: u32,
height: u32,
plane: usize,
flags: u32,
) -> Result<BufferMapping, Error> {
checked_range!(checked_arithmetic!(x + width)?; <= self.width())?;
checked_range!(checked_arithmetic!(y + height)?; <= self.height())?;
checked_range!(plane; <= self.num_planes())?;
let bytes_per_pixel = self
.format()
.bytes_per_pixel(plane)
.ok_or(Error::UnknownFormat(self.format()))? as u32;
let mut stride = 0;
let mut map_data = null_mut();
// Safe because only a valid gbm_bo object is used and the return value is checked. Only
// pointers coerced from stack references are used for returned values, and we trust gbm to
// only write as many bytes as the size of the pointed to values.
let mapping = unsafe {
gbm_bo_map(
self.0,
x,
y,
width,
height,
flags,
&mut stride,
&mut map_data,
plane,
)
};
if mapping == MAP_FAILED {
return Err(Error::MapFailed);
}
// The size of returned slice is equal the size of a row in bytes multiplied by the height
// of the mapped region, subtracted by the offset into the first mapped row. The 'x' and
// 'y's in the below diagram of a 2D buffer are bytes in the mapping. The first 'y' is what
// the mapping points to in memory, and the '-'s are unmapped bytes of the buffer.
// |----------|
// |--stride--|
// |-----yyyyx| h
// |xxxxxyyyyx| e
// |xxxxxyyyyx| i
// |xxxxxyyyyx| g
// |xxxxxyyyyx| h
// |xxxxxyyyyx| t
// |----------|
let size = checked_arithmetic!(stride * height)?;
let x_offset_bytes = checked_arithmetic!(x * bytes_per_pixel)?;
let slice_size = checked_arithmetic!(size - x_offset_bytes)? as u64;
Ok(BufferMapping {
// Safe because the chunk of memory starting at mapping with size `slice_size` is valid
// and tied to the lifetime of `buffer_mapping`.
slice: unsafe { VolatileSlice::new(mapping as *mut u8, slice_size) },
stride,
map_data,
buffer: self,
})
}
/// Reads the given subsection of the buffer to `dst`.
pub fn read_to_volatile(
&self,
x: u32,
y: u32,
width: u32,
height: u32,
plane: usize,
dst: VolatileSlice,
) -> Result<(), Error> {
if width == 0 || height == 0 {
return Ok(());
}
let mapping = self.map(x, y, width, height, plane, GBM_BO_TRANSFER_READ)?;
if x == 0 && width == self.width() {
mapping.as_volatile_slice().copy_to_volatile_slice(dst);
} else {
// This path is more complicated because there are gaps in the data between lines.
let width = width as u64;
let stride = mapping.stride() as u64;
let bytes_per_pixel = match self.format().bytes_per_pixel(plane) {
Some(bpp) => bpp as u64,
None => return Err(Error::UnknownFormat(self.format())),
};
let line_copy_size = checked_arithmetic!(width * bytes_per_pixel)?;
let src = mapping.as_volatile_slice();
for yy in 0..(height as u64) {
let line_offset = checked_arithmetic!(yy * stride)?;
let src_line = src
.get_slice(line_offset, line_copy_size)
.map_err(Error::Memcopy)?;
let dst_line = dst
.get_slice(line_offset, line_copy_size)
.map_err(Error::Memcopy)?;
src_line.copy_to_volatile_slice(dst_line);
}
}
Ok(())
}
/// Writes to the given subsection of the buffer from `sgs`.
pub fn write_from_sg<'a, S: Iterator<Item = VolatileSlice<'a>>>(
&self,
x: u32,
y: u32,
width: u32,
height: u32,
plane: usize,
src_offset: usize,
mut sgs: S,
) -> Result<(), Error> {
if width == 0 || height == 0 {
return Ok(());
}
checked_range!(src_offset; <= isize::MAX as usize)?;
let mapping = self.map(x, y, width, height, plane, GBM_BO_TRANSFER_WRITE)?;
let mut dst_slice = mapping.as_volatile_slice();
let stride = mapping.stride() as u64;
let mut height = height as u64;
let mut src_offset = src_offset as u64;
if x == 0 && width == self.width() {
// This path is a simple copy from the scatter gather iterator to the buffer objection,
// with no gaps in the data.
let mut copy_size = checked_arithmetic!(stride * height)?;
for sg in sgs {
// Skip src_offset into this scatter gather item, or the entire thing if offset is
// larger.
let sg_size = match sg.size().checked_sub(src_offset) {
Some(sg_remaining_size) => sg_remaining_size,
None => {
src_offset -= sg.size();
continue;
}
};
let copy_sg_size = min(sg_size, copy_size);
let src_slice = sg
.get_slice(src_offset, copy_sg_size)
.map_err(Error::Memcopy)?;
src_slice.copy_to_volatile_slice(dst_slice);
src_offset = 0;
dst_slice = dst_slice.offset(copy_sg_size).map_err(Error::Memcopy)?;
copy_size -= copy_sg_size;
if copy_size == 0 {
break;
}
}
} else {
let width = width as u64;
// This path is more complicated because there are gaps in the data between lines.
let bytes_per_pixel = self.format().bytes_per_pixel(plane).unwrap_or(0) as u64;
let line_copy_size = checked_arithmetic!(width * bytes_per_pixel)?;
let line_end_skip = checked_arithmetic!(stride - line_copy_size)?;
let mut remaining_line_copy_size = line_copy_size;
let mut sg_opt = sgs.next();
while let Some(sg) = sg_opt {
// Skip src_offset into this scatter gather item, or the entire thing if offset is
// larger.
let sg_size = match sg.size().checked_sub(src_offset) {
None | Some(0) => {
src_offset -= sg.size();
sg_opt = sgs.next();
continue;
}
Some(sg_remaining_size) => sg_remaining_size,
};
let copy_sg_size = min(sg_size, remaining_line_copy_size);
let src_slice = sg
.get_slice(src_offset, copy_sg_size)
.map_err(Error::Memcopy)?;
src_slice.copy_to_volatile_slice(dst_slice);
src_offset += copy_sg_size;
dst_slice = dst_slice.offset(copy_sg_size).map_err(Error::Memcopy)?;
remaining_line_copy_size -= copy_sg_size;
if remaining_line_copy_size == 0 {
remaining_line_copy_size = line_copy_size;
height -= 1;
if height == 0 {
break;
}
src_offset += line_end_skip;
dst_slice = dst_slice.offset(line_end_skip).map_err(Error::Memcopy)?;
}
}
}
Ok(())
}
}
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) }
}
}
struct BufferMapping<'a> {
slice: VolatileSlice<'a>,
stride: u32,
map_data: *mut c_void,
buffer: &'a Buffer,
}
impl<'a> BufferMapping<'a> {
fn as_volatile_slice(&self) -> VolatileSlice {
self.slice
}
fn stride(&self) -> u32 {
self.stride
}
}
impl<'a> Drop for BufferMapping<'a> {
fn drop(&mut self) {
// safe because the gbm_bo is assumed to be valid and the map_data is the same one given by
// gbm_bo_map.
unsafe {
gbm_bo_unmap(self.buffer.0, self.map_data);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use data_model::VolatileMemory;
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");
}
#[test]
#[ignore] // no access to /dev/dri
fn buffer_transfer() {
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).use_linear(true),
)
.expect("failed to create buffer");
let mut dst: Vec<u8> = Vec::new();
dst.resize((bo.stride() * bo.height()) as usize, 0x4A);
let dst_len = dst.len() as u64;
bo.write_from_sg(
0,
0,
1024,
1024,
0,
0,
[dst.as_mut_slice().get_slice(0, dst_len).unwrap()]
.iter()
.cloned(),
)
.expect("failed to read bo");
bo.read_to_volatile(
0,
0,
1024,
1024,
0,
dst.as_mut_slice().get_slice(0, dst_len).unwrap(),
)
.expect("failed to read bo");
assert!(dst.iter().all(|&x| x == 0x4A));
}
}