gpu_buffer: create bindings to minigbm

These bindings are needed to allocate dmabufs that will be used for
accelerated rendering and zero-copy virtio-wayland support.

TEST=cargo test -p gpu_buffer
BUG=chromium:837073

Change-Id: I96d7bcdeaa1eda616a25fdcfedcbb734cd585ae7
Reviewed-on: https://chromium-review.googlesource.com/1029410
Commit-Ready: David Reveman <reveman@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
Zach Reizner 2018-04-25 17:49:56 -07:00 committed by chrome-bot
parent e0823392f4
commit 6f6854312d
5 changed files with 778 additions and 0 deletions

8
Cargo.lock generated
View file

@ -52,6 +52,7 @@ dependencies = [
"data_model 0.1.0",
"device_manager 0.1.0",
"devices 0.1.0",
"gpu_buffer 0.1.0",
"io_jail 0.1.0",
"kernel_cmdline 0.1.0",
"kernel_loader 0.1.0",
@ -133,6 +134,13 @@ name = "gcc"
version = "0.3.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "gpu_buffer"
version = "0.1.0"
dependencies = [
"data_model 0.1.0",
]
[[package]]
name = "io_jail"
version = "0.1.0"

View file

@ -16,6 +16,7 @@ plugin = ["plugin_proto", "crosvm_plugin", "protobuf"]
arch = { path = "arch" }
devices = { path = "devices" }
device_manager = { path = "device_manager" }
gpu_buffer = { path = "gpu_buffer", optional = true }
io_jail = { path = "io_jail" }
kvm = { path = "kvm" }
kvm_sys = { path = "kvm_sys" }

7
gpu_buffer/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "gpu_buffer"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
[dependencies]
data_model = { path = "../data_model" }

553
gpu_buffer/src/lib.rs Normal file
View file

@ -0,0 +1,553 @@
// 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(())
//! # }
//! ```
extern crate data_model;
mod raw;
use std::os::raw::c_void;
use std::fmt;
use std::cmp::min;
use std::fs::File;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::ptr::{copy_nonoverlapping, null_mut};
use std::rc::Rc;
use std::result::Result;
use data_model::VolatileSlice;
use raw::*;
const MAP_FAILED: *mut c_void = (-1isize as *mut _);
/// 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]
}
}
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, ()> {
// 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(())
} 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_format_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_num_planes(self.0) }
}
/// 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),
}
}
/// 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<(), ()> {
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,
GBM_BO_TRANSFER_READ,
&mut stride,
&mut map_data,
plane)
};
if mapping == MAP_FAILED {
return Err(());
}
let copy_size = (y as u64) * (stride as u64);
let res = if copy_size <= dst.size() {
// The two buffers can not be overlapping because we just made a new mapping in this
// scope.
unsafe {
copy_nonoverlapping(mapping as *mut u8, dst.as_ptr(), copy_size as usize);
}
Ok(())
} else {
Err(())
};
// 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.0, map_data);
}
res
}
/// Writes to the given subsection of the buffer from `src`.
pub fn write_from_slice(&self,
x: u32,
y: u32,
width: u32,
height: u32,
plane: usize,
src: &[u8])
-> Result<(), ()> {
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,
GBM_BO_TRANSFER_WRITE,
&mut stride,
&mut map_data,
plane)
};
if mapping == MAP_FAILED {
return Err(());
}
let copy_size = (height as u64) * (stride as u64);
let copy_sg_size = min(src.len() as u64, copy_size);
// The two buffers can not be overlapping because we just made a new mapping in this scope.
unsafe {
copy_nonoverlapping(src.as_ptr(), mapping as *mut u8, copy_sg_size as usize);
}
// 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.0, map_data);
}
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,
sgs: S)
-> Result<(), ()> {
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 mut mapping = unsafe {
gbm_bo_map(self.0,
x,
y,
width,
height,
GBM_BO_TRANSFER_WRITE,
&mut stride,
&mut map_data,
plane)
};
if mapping == MAP_FAILED {
return Err(());
}
let mut copy_size = (height as u64) * (stride as u64);
for sg in sgs {
let copy_sg_size = min(sg.size(), copy_size);
// The two buffers can not be overlapping because we just made a new mapping in this
// scope.
unsafe {
copy_nonoverlapping(sg.as_ptr(), mapping as *mut u8, copy_sg_size as usize);
}
mapping = mapping.wrapping_offset(copy_sg_size as isize);
copy_size -= copy_sg_size;
if copy_size == 0 {
break;
}
}
// 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.0, map_data);
}
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) }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fmt::Write;
use data_model::VolatileMemory;
#[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]
#[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_slice(0, 0, 1024, 1024, 0, dst.as_slice())
.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));
}
}

209
gpu_buffer/src/raw.rs Normal file
View file

@ -0,0 +1,209 @@
// 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.
// Generated with bindgen --whitelist-function='gbm_.*' --whitelist-type='gbm_.*' minigbm/gbm.h
// Hand-modified by zachr
#![allow(dead_code)]
use std::os::raw::{c_int, c_char, c_void};
/// \file gbm.h
/// \brief Generic Buffer Manager
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct gbm_device {
_unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct gbm_bo {
_unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct gbm_surface {
_unused: [u8; 0],
}
/// Abstraction representing the handle to a buffer allocated by the
/// manager
#[repr(C)]
#[derive(Copy, Clone)]
pub union gbm_bo_handle {
pub ptr: *mut c_void,
pub s32: i32,
pub u32: u32,
pub s64: i64,
pub u64: u64,
_bindgen_union_align: u64,
}
/// Buffer is going to be presented to the screen using an API such as KMS
pub const GBM_BO_USE_SCANOUT: gbm_bo_flags = 1;
/// Buffer is going to be used as cursor
pub const GBM_BO_USE_CURSOR: gbm_bo_flags = 2;
/// Deprecated
pub const GBM_BO_USE_CURSOR_64X64: gbm_bo_flags = 2;
/// Buffer is to be used for rendering - for example it is going to be used
/// as the storage for a color buffer
pub const GBM_BO_USE_RENDERING: gbm_bo_flags = 4;
/// Deprecated
pub const GBM_BO_USE_WRITE: gbm_bo_flags = 8;
/// Buffer is guaranteed to be laid out linearly in memory. That is, the
/// buffer is laid out as an array with 'height' blocks, each block with
/// length 'stride'. Each stride is in the same order as the rows of the
/// buffer. This is intended to be used with buffers that will be accessed
/// via dma-buf mmap().
pub const GBM_BO_USE_LINEAR: gbm_bo_flags = 16;
/// The buffer will be used as a texture that will be sampled from.
pub const GBM_BO_USE_TEXTURING: gbm_bo_flags = 32;
/// The buffer will be written to by a camera subsystem.
pub const GBM_BO_USE_CAMERA_WRITE: gbm_bo_flags = 64;
/// The buffer will be read from by a camera subsystem.
pub const GBM_BO_USE_CAMERA_READ: gbm_bo_flags = 128;
/// Buffer inaccessible to unprivileged users.
pub const GBM_BO_USE_PROTECTED: gbm_bo_flags = 256;
/// These flags specify the frequency of software access. These flags do not
/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
/// present a linear view.
pub const GBM_BO_USE_SW_READ_OFTEN: gbm_bo_flags = 512;
/// These flags specify the frequency of software access. These flags do not
/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
/// present a linear view.
pub const GBM_BO_USE_SW_READ_RARELY: gbm_bo_flags = 1024;
/// These flags specify the frequency of software access. These flags do not
/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
/// present a linear view.
pub const GBM_BO_USE_SW_WRITE_OFTEN: gbm_bo_flags = 2048;
/// These flags specify the frequency of software access. These flags do not
/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
/// present a linear view.
pub const GBM_BO_USE_SW_WRITE_RARELY: gbm_bo_flags = 4096;
/// Flags to indicate the intended use for the buffer - these are passed into
/// gbm_bo_create(). The caller must set the union of all the flags that are
/// appropriate
///
/// \sa Use gbm_device_is_format_supported() to check if the combination of format
/// and use flags are supported
#[allow(non_camel_case_types)]
pub type gbm_bo_flags = u32;
#[link(name = "gbm")]
extern "C" {
pub fn gbm_device_get_fd(gbm: *mut gbm_device) -> c_int;
pub fn gbm_device_get_backend_name(gbm: *mut gbm_device) -> *const c_char;
pub fn gbm_device_is_format_supported(gbm: *mut gbm_device, format: u32, usage: u32) -> c_int;
pub fn gbm_device_destroy(gbm: *mut gbm_device);
pub fn gbm_create_device(fd: c_int) -> *mut gbm_device;
pub fn gbm_bo_create(gbm: *mut gbm_device,
width: u32,
height: u32,
format: u32,
flags: u32)
-> *mut gbm_bo;
pub fn gbm_bo_create_with_modifiers(gbm: *mut gbm_device,
width: u32,
height: u32,
format: u32,
modifiers: *const u64,
count: u32)
-> *mut gbm_bo;
}
#[repr(C)]
#[derive(Debug, Copy, Clone )]
pub struct gbm_import_fd_data {
pub fd: c_int,
pub width: u32,
pub height: u32,
pub stride: u32,
pub format: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone )]
pub struct gbm_import_fd_planar_data {
pub fds: [c_int; 4usize],
pub width: u32,
pub height: u32,
pub format: u32,
pub strides: [u32; 4usize],
pub offsets: [u32; 4usize],
pub format_modifiers: [u64; 4usize],
}
extern "C" {
pub fn gbm_bo_import(gbm: *mut gbm_device,
type_: u32,
buffer: *mut c_void,
usage: u32)
-> *mut gbm_bo;
}
/// Buffer contents read back (or accessed directly) at transfer
/// create time.
pub const GBM_BO_TRANSFER_READ: gbm_bo_transfer_flags = 1;
/// Buffer contents will be written back at unmap time
/// (or modified as a result of being accessed directly).
pub const GBM_BO_TRANSFER_WRITE: gbm_bo_transfer_flags = 2;
/// Read/modify/write
pub const GBM_BO_TRANSFER_READ_WRITE: gbm_bo_transfer_flags = 3;
/// Flags to indicate the type of mapping for the buffer - these are
/// passed into gbm_bo_map(). The caller must set the union of all the
/// flags that are appropriate.
///
/// These flags are independent of the GBM_BO_USE_* creation flags. However,
/// mapping the buffer may require copying to/from a staging buffer.
///
/// See also: pipe_transfer_usage
#[allow(non_camel_case_types)]
pub type gbm_bo_transfer_flags = u32;
extern "C" {
pub fn gbm_bo_map(bo: *mut gbm_bo,
x: u32,
y: u32,
width: u32,
height: u32,
flags: u32,
stride: *mut u32,
map_data: *mut *mut c_void,
plane: usize)
-> *mut c_void;
pub fn gbm_bo_unmap(bo: *mut gbm_bo, map_data: *mut c_void);
pub fn gbm_bo_get_width(bo: *mut gbm_bo) -> u32;
pub fn gbm_bo_get_height(bo: *mut gbm_bo) -> u32;
pub fn gbm_bo_get_stride(bo: *mut gbm_bo) -> u32;
pub fn gbm_bo_get_stride_or_tiling(bo: *mut gbm_bo) -> u32;
pub fn gbm_bo_get_format(bo: *mut gbm_bo) -> u32;
pub fn gbm_bo_get_format_modifier(bo: *mut gbm_bo) -> u64;
pub fn gbm_bo_get_device(bo: *mut gbm_bo) -> *mut gbm_device;
pub fn gbm_bo_get_handle(bo: *mut gbm_bo) -> gbm_bo_handle;
pub fn gbm_bo_get_fd(bo: *mut gbm_bo) -> c_int;
pub fn gbm_bo_get_num_planes(bo: *mut gbm_bo) -> usize;
pub fn gbm_bo_get_plane_handle(bo: *mut gbm_bo, plane: usize) -> gbm_bo_handle;
pub fn gbm_bo_get_plane_fd(bo: *mut gbm_bo, plane: usize) -> c_int;
pub fn gbm_bo_get_plane_offset(bo: *mut gbm_bo, plane: usize) -> u32;
pub fn gbm_bo_get_plane_size(bo: *mut gbm_bo, plane: usize) -> u32;
pub fn gbm_bo_get_plane_stride(bo: *mut gbm_bo, plane: usize) -> u32;
pub fn gbm_bo_get_plane_format_modifier(bo: *mut gbm_bo, plane: usize) -> u64;
// Did not generate cleanly by bindgen. Redone manually by zachr.
pub fn gbm_bo_set_user_data(bo: *mut gbm_bo,
data: *mut c_void,
destroy_user_data: extern "C" fn(bo: *mut gbm_bo,
data: *mut c_void));
pub fn gbm_bo_get_user_data(bo: *mut gbm_bo) -> *mut c_void;
pub fn gbm_bo_destroy(bo: *mut gbm_bo);
pub fn gbm_surface_create(gbm: *mut gbm_device,
width: u32,
height: u32,
format: u32,
flags: u32)
-> *mut gbm_surface;
pub fn gbm_surface_lock_front_buffer(surface: *mut gbm_surface) -> *mut gbm_bo;
pub fn gbm_surface_release_buffer(surface: *mut gbm_surface, bo: *mut gbm_bo);
pub fn gbm_surface_has_free_buffers(surface: *mut gbm_surface) -> c_int;
pub fn gbm_surface_destroy(surface: *mut gbm_surface);
}