Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Smithay/gbm.rs/llms.txt
Use this file to discover all available pages before exploring further.
BufferObject<T> represents a GPU-allocated buffer managed by libgbm. It is the central handle type in gbm.rs, wrapping a gbm_bo pointer and keeping the parent gbm_device alive for as long as the buffer exists. The type parameter T lets you attach arbitrary typed data to the buffer object, which libgbm stores and returns on demand. Buffer objects are created through Device::create_buffer_object and are automatically destroyed when the last reference is dropped.
Struct Definition
pub struct BufferObject<T: 'static>
The internal ffi pointer is dropped before the _device pointer, ensuring the device always outlives any buffer it owns.
Trait Implementations
| Trait | Notes |
|---|
Debug | Prints pointer address, dimensions, format, modifier, and plane offsets. |
AsRaw<gbm_bo> | Returns a raw *const gbm_bo for FFI interop. |
Send | Safe to send across threads; the underlying libgbm types are thread-safe. |
Sync | Safe to share references across threads. |
drm::buffer::Buffer (drm-support) | Exposes size, format, pitch, and GEM handle for DRM framebuffer operations. |
drm::buffer::PlanarBuffer (drm-support) | Extends Buffer with per-plane pitches, handles, offsets, and modifier. |
These methods query the dimensions, pixel format, and memory layout reported by the GBM backend at allocation time.
width
pub fn width(&self) -> u32
Returns the width of the buffer in pixels.
let w = bo.width();
println!("buffer width: {w}px");
height
pub fn height(&self) -> u32
Returns the height of the buffer in pixels.
let h = bo.height();
println!("buffer height: {h}px");
stride
pub fn stride(&self) -> u32
Returns the stride (row pitch) of plane 0 in bytes. The stride may be larger than width * bytes_per_pixel due to hardware alignment requirements.
let stride = bo.stride();
println!("plane 0 stride: {stride} bytes/row");
stride_for_plane
pub fn stride_for_plane(&self, plane: i32) -> u32
Returns the stride in bytes for the given plane index. Useful for multi-planar formats such as NV12 or YUV420.
Zero-based plane index. Must be less than plane_count().
// Inspect strides for a two-plane NV12 buffer
let y_stride = bo.stride_for_plane(0);
let uv_stride = bo.stride_for_plane(1);
println!("Y stride: {y_stride}, UV stride: {uv_stride}");
pub fn format(&self) -> Format
Returns the pixel format as a drm_fourcc::DrmFourcc value (re-exported as Format). Panics if libgbm returns an unrecognised format code.
use gbm::Format;
let fmt = bo.format();
assert_eq!(fmt, Format::Argb8888);
bpp
Returns the bits per pixel for the buffer’s format.
let bits = bo.bpp();
println!("bits per pixel: {bits}");
offset
pub fn offset(&self, plane: i32) -> u32
Returns the byte offset of the given plane within the buffer’s memory allocation.
let plane0_offset = bo.offset(0);
println!("plane 0 starts at byte {plane0_offset}");
plane_count
pub fn plane_count(&self) -> u32
Returns the number of planes in the buffer. Packed formats like ARGB8888 have one plane; YUV formats may have two or three.
println!("planes: {}", bo.plane_count());
modifier
pub fn modifier(&self) -> Modifier
Returns the tiling or compression modifier as a drm_fourcc::DrmModifier value (re-exported as Modifier). Modifier::Invalid indicates that no explicit modifier was requested.
use gbm::Modifier;
let m = bo.modifier();
println!("modifier: {m:?}");
if m == Modifier::Linear {
println!("buffer is stored in linear layout");
}
Full geometry example
use gbm::{BufferObjectFlags, Device, Format};
let bo = device
.create_buffer_object::<()>(1920, 1080, Format::Xrgb8888, BufferObjectFlags::RENDERING)
.unwrap();
println!(
"{}x{} px, fmt={:?}, modifier={:?}, stride={} B, planes={}",
bo.width(),
bo.height(),
bo.format(),
bo.modifier(),
bo.stride(),
bo.plane_count(),
);
File Descriptors and Handles
These methods expose the OS-level handles needed to share a buffer with other subsystems such as EGL, Vulkan, or the kernel DRM driver.
pub fn fd(&self) -> Result<OwnedFd, InvalidFdError>
Creates a new DMA-BUF (PRIME) file descriptor for the buffer. Each call allocates a fresh file descriptor; the caller is responsible for closing it (the returned OwnedFd closes it on drop). Returns [InvalidFdError] if libgbm cannot export the buffer.
use std::os::unix::io::IntoRawFd;
let dmabuf_fd = bo.fd().expect("DMA-BUF export failed");
// Pass the raw fd to EGL or V4L2, then it will be closed when dmabuf_fd drops.
println!("DMA-BUF fd: {}", dmabuf_fd.as_raw_fd());
fd_for_plane
pub fn fd_for_plane(&self, plane: i32) -> Result<OwnedFd, InvalidFdError>
Creates a new DMA-BUF file descriptor for a specific plane. Like fd(), each call produces a new owned descriptor that must be closed by the caller.
// Export plane fds for a multi-planar buffer (e.g. NV12)
let fd0 = bo.fd_for_plane(0).unwrap();
let fd1 = bo.fd_for_plane(1).unwrap();
handle
pub fn handle(&self) -> BufferObjectHandle
Returns the GEM handle for the buffer as a gbm_bo_handle union. The exact field to access (u32_, s32, etc.) depends on the platform and driver.
let h = bo.handle();
// Access the unsigned 32-bit field on most platforms
println!("GEM handle: {}", unsafe { h.u32_ });
handle_for_plane
pub fn handle_for_plane(&self, plane: i32) -> BufferObjectHandle
Returns the GEM handle for a specific plane of a multi-planar buffer.
let h0 = bo.handle_for_plane(0);
let h1 = bo.handle_for_plane(1);
device_fd
pub fn device_fd(&self) -> BorrowedFd
Returns a borrowed file descriptor for the GBM device that owns this buffer. The lifetime of the returned BorrowedFd is tied to &self.
let dev_fd = bo.device_fd();
println!("GBM device fd: {}", dev_fd.as_raw_fd());
CPU Access
GBM provides two mechanisms for the CPU to read or write a buffer’s pixel data: closure-based memory mappings and a direct write helper.
map
pub fn map<'a, F, S>(
&'a self,
x: u32,
y: u32,
width: u32,
height: u32,
f: F,
) -> IoResult<S>
where
F: FnOnce(&MappedBufferObject<'a, T>) -> S,
Maps a rectangular region of the buffer for read-only CPU access. The region is described by its top-left corner (x, y) and dimensions (width, height). The closure f receives a MappedBufferObject reference; the mapping is released automatically when the closure returns. Returns an Err if the kernel mapping call fails.
Left edge of the region to map, in pixels.
Top edge of the region to map, in pixels.
Width of the region to map, in pixels.
Height of the region to map, in pixels.
f
FnOnce(&MappedBufferObject<T>) -> S
required
Closure that receives the read-only mapped view. Runs synchronously before the mapping is released.
// Read the first pixel of a 32 bpp ARGB buffer
let pixel = bo
.map(0, 0, 1, 1, |mbo| {
let bytes = mbo.buffer();
u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
})
.expect("map failed");
println!("top-left pixel (ARGB): {pixel:#010x}");
map_mut
pub fn map_mut<'a, F, S>(
&'a mut self,
x: u32,
y: u32,
width: u32,
height: u32,
f: F,
) -> IoResult<S>
where
F: FnOnce(&mut MappedBufferObject<'a, T>) -> S,
Maps a rectangular region for read-write CPU access. The closure receives a mutable MappedBufferObject; changes written to buffer_mut() are flushed back to the GPU buffer when the mapping is released on closure return.
Left edge of the region to map, in pixels.
Top edge of the region to map, in pixels.
Width of the region to map, in pixels.
Height of the region to map, in pixels.
f
FnOnce(&mut MappedBufferObject<T>) -> S
required
Closure that receives the read-write mapped view.
// Clear the entire buffer to opaque black (ARGB8888)
let (w, h) = (bo.width(), bo.height());
bo.map_mut(0, 0, w, h, |mbo| {
for chunk in mbo.buffer_mut().chunks_exact_mut(4) {
chunk.copy_from_slice(&[0x00, 0x00, 0x00, 0xFF]); // B G R A
}
})
.expect("map_mut failed");
write
pub fn write(&mut self, buffer: &[u8]) -> IoResult<()>
Copies the contents of buffer directly into the GBM buffer object. The buffer object must have been created with BufferObjectFlags::WRITE. The caller is responsible for ensuring buffer contains valid pixel data matching the buffer’s width, height, stride, and format.
Raw pixel bytes to write. Must represent valid data for the buffer’s format and dimensions.
use gbm::{BufferObjectFlags, Format};
let mut bo = device
.create_buffer_object::<()>(64, 64, Format::Argb8888, BufferObjectFlags::WRITE)
.unwrap();
// Fill with a flat colour (BGRA byte order for ARGB8888)
let pixel_data: Vec<u8> = (0..64 * 64)
.flat_map(|_| [0x40u8, 0x80, 0xC0, 0xFF]) // teal
.collect();
bo.write(&pixel_data).expect("write failed");
Userdata
BufferObject<T> can hold a single value of type T (the type parameter). The data is heap-allocated and libgbm is given a destructor callback so the value is dropped when the buffer object is destroyed — even if Rust code never calls take_userdata.
set_userdata
pub fn set_userdata(&mut self, userdata: T) -> Option<T>
Attaches userdata to the buffer object. If data was already set, the previous value is returned as Some(T). If no data was previously attached, returns None.
Value to associate with this buffer object.
struct Meta { label: String }
let old = bo.set_userdata(Meta { label: "framebuffer-0".into() });
assert!(old.is_none());
userdata
pub fn userdata(&self) -> Option<&T>
Returns a shared reference to the attached userdata, or None if no userdata has been set.
if let Some(meta) = bo.userdata() {
println!("buffer label: {}", meta.label);
}
userdata_mut
pub fn userdata_mut(&mut self) -> Option<&mut T>
Returns an exclusive reference to the attached userdata, or None if no userdata has been set.
if let Some(meta) = bo.userdata_mut() {
meta.label.push_str(" (dirty)");
}
take_userdata
pub fn take_userdata(&mut self) -> Option<T>
Removes and returns the attached userdata. After this call userdata() will return None. The libgbm destructor callback is cleared so that the value is not double-freed.
if let Some(meta) = bo.take_userdata() {
println!("reclaimed: {}", meta.label);
}
assert!(bo.userdata().is_none());
clear_userdata
pub fn clear_userdata(&mut self)
Drops the attached userdata in place without returning it. Equivalent to calling take_userdata() and discarding the result.
bo.set_userdata(42u32);
bo.clear_userdata();
assert!(bo.userdata().is_none());
Full userdata example
use gbm::{BufferObjectFlags, Device, Format};
#[derive(Debug)]
struct FbMeta {
sequence: u64,
rendered: bool,
}
let mut bo = device
.create_buffer_object::<FbMeta>(
1920,
1080,
Format::Xrgb8888,
BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
)
.unwrap();
bo.set_userdata(FbMeta { sequence: 0, rendered: false });
// Later, after rendering:
if let Some(meta) = bo.userdata_mut() {
meta.sequence += 1;
meta.rendered = true;
}
println!("{:?}", bo.userdata());