Skip to main content

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> is the fundamental unit of GPU memory in gbm.rs. Each instance represents a contiguous region of GPU-accessible memory — a framebuffer backing store, a cursor plane image, an import target for a shared DMA-BUF, or any other driver-managed pixel buffer. The type is generic over T, which lets you attach arbitrary Rust values to a buffer as userdata, tying domain-specific state (like a cached framebuffer handle) directly to the object that needs it.

The Generic Parameter T

The T in BufferObject<T> represents user-controlled payload stored inside the buffer object. libgbm provides gbm_bo_set_user_data / gbm_bo_get_user_data for exactly this purpose. gbm.rs boxes the value, stores the pointer through the C API, and registers a C destructor that reconstructs and drops the Box<T> when the buffer object is freed — even if that happens from within libgbm. When you do not need userdata, use BufferObject<()>.

Creation

All creation methods live on Device<T>. They accept at minimum a width, height, pixel format, and usage flags.

create_buffer_object

The basic allocation path. Lets the driver choose the buffer layout (tiling, compression) freely.
use gbm::{Device, Format, BufferObjectFlags};
use std::fs::File;

let file = File::options().read(true).write(true).open("/dev/dri/card0")?;
let gbm = Device::new(file)?;

let bo = gbm.create_buffer_object::<()>(
    1920,                    // width in pixels
    1080,                    // height in pixels
    Format::Xrgb8888,        // pixel format
    BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
)?;

create_buffer_object_with_modifiers

Passes an explicit list of DRM format modifiers. The driver picks the first modifier from the list that it supports. Use this when you have negotiated modifiers with EGL or another API and need the buffer layout to match.
use gbm::Modifier;

let modifiers = [Modifier::Linear];
let bo = gbm.create_buffer_object_with_modifiers::<()>(
    1920,
    1080,
    Format::Xrgb8888,
    modifiers.iter().copied(),
)?;

create_buffer_object_with_modifiers2

Combines explicit modifiers with usage flags. This is the most expressive creation method and maps directly to gbm_bo_create_with_modifiers2.
let bo = gbm.create_buffer_object_with_modifiers2::<()>(
    1920,
    1080,
    Format::Xrgb8888,
    modifiers.iter().copied(),
    BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
)?;

BufferObjectFlags

Flags communicate the intended use of the buffer to the driver. They are a bitflags type so you can combine them with |.
FlagMeaning
SCANOUTBuffer will be presented to the screen via KMS/DRM. Driver may apply display-compatible tiling.
CURSORBuffer will be used as a hardware cursor plane.
RENDERINGBuffer will be used as a render target (e.g. OpenGL color attachment).
WRITEBuffer supports direct CPU writes via BufferObject::write().
LINEARBuffer must be allocated with a linear (non-tiled) layout.
PROTECTEDBuffer contents are protected from CPU access (secure video path).
CURSOR_64X64 is deprecated — use CURSOR instead.

Inspecting a Buffer Object

Once allocated, a BufferObject exposes a comprehensive set of read-only accessors:
println!("Size:        {}×{}", bo.width(), bo.height());
println!("Stride:      {} bytes/row", bo.stride());
println!("Format:      {:?}", bo.format());
println!("Bits/pixel:  {}", bo.bpp());
println!("Planes:      {}", bo.plane_count());
println!("Modifier:    {:?}", bo.modifier());

// Per-plane information (multi-plane formats like NV12)
for plane in 0..bo.plane_count() as i32 {
    println!(
        "  Plane {}: stride={} offset={}",
        plane,
        bo.stride_for_plane(plane),
        bo.offset(plane),
    );
}

Full Accessor Reference

MethodReturnsDescription
width()u32Buffer width in pixels
height()u32Buffer height in pixels
stride()u32Row stride in bytes for plane 0
stride_for_plane(plane)u32Row stride for a given plane
format()FormatDRM FourCC pixel format
bpp()u32Bits per pixel
offset(plane)u32Byte offset of a plane from the buffer start
plane_count()u32Number of planes in this buffer
modifier()ModifierDRM format modifier describing tiling layout

DMA-BUF and Platform Handles

fd() and fd_for_plane()

Export the buffer as a DMA-BUF (PRIME) file descriptor. Each call creates a new OwnedFd; the caller is responsible for closing it. Returns Err(InvalidFdError) if the driver does not support PRIME export.
let dma_buf_fd: OwnedFd = bo.fd()?;

// Multi-plane: export each plane separately
let plane0_fd = bo.fd_for_plane(0)?;
let plane1_fd = bo.fd_for_plane(1)?;

handle() and handle_for_plane()

Return the platform-specific BufferObjectHandle (a union over u32, u64, pointer, and signed types). The interpretation depends on the DRM driver. drm-rs consumes this value when adding a framebuffer.
let handle = bo.handle();
// Access as u32 (common for DRM GEM handles)
let gem_handle: u32 = unsafe { handle.u32_ };

CPU Mapping

GBM exposes two mapping functions that grant the CPU direct access to the GPU buffer’s pixel data. Both accept a rectangular region (x, y, width, height) and operate through a callback, ensuring the map is always unmapped when the closure returns.

map() — Read-Only Access

let result = bo.map(0, 0, bo.width(), bo.height(), |mapped| {
    println!("Map stride: {}", mapped.stride());
    println!("First byte: {}", mapped.buffer()[0]);
    // Return any value from the closure
    mapped.buffer().len()
})?;

println!("Mapped {} bytes", result);

map_mut() — Read-Write Access

bo.map_mut(0, 0, bo.width(), bo.height(), |mapped| {
    // Fill the entire buffer with a solid colour (XRGB8888 blue)
    for pixel in mapped.buffer_mut().chunks_mut(4) {
        pixel[0] = 0xFF; // B
        pixel[1] = 0x00; // G
        pixel[2] = 0x00; // R
        pixel[3] = 0xFF; // X
    }
})?;
MappedBufferObject<'a, T> implements Deref<Target = BufferObject<T>> so all inspection methods remain accessible inside the closure.

Direct CPU Write

write(buffer) copies raw bytes directly into the GPU buffer without going through the CPU map API. It requires the buffer to have been created with BufferObjectFlags::WRITE.
// Create a WRITE-flagged buffer
let mut bo = gbm.create_buffer_object::<()>(
    64, 64,
    Format::Argb8888,
    BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
)?;

// Fill with a solid colour
let pixel_data: Vec<u8> = (0..64 * 64 * 4)
    .map(|i| if i % 4 == 3 { 0xFF } else { 0x80 })
    .collect();

bo.write(&pixel_data)?;
write() copies the raw byte slice without format conversion. The data must match the buffer’s format, width, height, and stride exactly.

Userdata

BufferObject<T> can carry a single value of type T that travels with the buffer through its lifetime. This is useful for caching derived state like a DRM framebuffer handle alongside the underlying buffer object.
use gbm::{Device, Format, BufferObjectFlags};

struct FramebufferId(u32);

// Create a buffer and immediately attach a framebuffer handle
let mut bo = gbm.create_buffer_object::<FramebufferId>(
    1920, 1080, Format::Xrgb8888, BufferObjectFlags::SCANOUT,
)?;

// Returns the old value, if any
let old = bo.set_userdata(FramebufferId(42));
assert!(old.is_none());

// Immutable borrow
if let Some(fb) = bo.userdata() {
    println!("Framebuffer id: {}", fb.0);
}

// Mutable borrow
if let Some(fb) = bo.userdata_mut() {
    fb.0 += 1;
}

// Take ownership — userdata is removed from the buffer
let fb: Option<FramebufferId> = bo.take_userdata();

// Discard without returning the value
bo.set_userdata(FramebufferId(99));
bo.clear_userdata();

Userdata API Summary

MethodDescription
set_userdata(value)Store a value; returns the previous value if one was set.
userdata()Borrow the stored value, or None.
userdata_mut()Mutably borrow the stored value, or None.
take_userdata()Remove and return the stored value, or None.
clear_userdata()Remove the stored value and drop it in place.
When the drm-support feature is enabled, BufferObject<T> implements both drm::buffer::Buffer and drm::buffer::PlanarBuffer. You can pass a &BufferObject directly to drm::control::Device::add_framebuffer and add_planar_framebuffer without any manual handle extraction.

Build docs developers (and LLMs) love