Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Smithay/drm-rs/llms.txt

Use this file to discover all available pages before exploring further.

DRM dumb buffers are the simplest form of GPU-visible memory available in the Linux DRM subsystem. They are allocated by the kernel, backed by anonymous memory, and directly accessible from userspace via mmap. No GPU-specific knowledge is required — any driver that supports the DumbBuffer capability can create them. Dumb buffers are ideal for:
  • Software rendering (writing pixels directly from the CPU)
  • Boot screens and splash images
  • Simple framebuffer outputs on embedded or virtual hardware
  • Learning and prototyping with DRM before moving to hardware-accelerated paths
For hardware-accelerated rendering you will want GPU-backed buffers allocated via GBM, which can be imported into drm-rs as PRIME framebuffers.

Checking driver support

Before allocating a dumb buffer, verify that the driver supports the feature:
let cap = card
    .get_driver_capability(drm::DriverCapability::DumbBuffer)
    .expect("Could not query DumbBuffer capability");

assert!(cap != 0, "Driver does not support dumb buffers");
The get_driver_capability method returns a u64; a non-zero value means the feature is supported.

Creating a dumb buffer

create_dumb_buffer takes the desired (width, height) in pixels, the pixel format as a DrmFourcc value, and the bits-per-pixel count:
use drm::buffer::DrmFourcc;

let mut db = card
    .create_dumb_buffer(
        (1920, 1080),       // width × height in pixels
        DrmFourcc::Xrgb8888,
        32,                 // bits per pixel
    )
    .expect("Could not create dumb buffer");

let (w, h) = db.size();
println!("Size:   {}x{}", w, h);           // 1920x1080
println!("Pitch:  {} bytes/row", db.pitch());
println!("Total:  {} bytes", db.pitch() as usize * h as usize);
The pitch (stride) is the number of bytes per pixel row including any padding the driver adds for alignment. Always use db.pitch() to compute row offsets — do not assume pitch == width * (bpp / 8).

Mapping the buffer and writing pixel data

map_dumb_buffer maps the buffer into the current process’s address space and returns a DumbMapping that holds the mmap region for the lifetime of the object:
{
    let mut map = card
        .map_dumb_buffer(&mut db)
        .expect("Could not map dumb buffer");

    // Fill the entire buffer with mid-grey (0x80 in every byte channel).
    for b in map.as_mut() {
        *b = 0x80;
    }
} // DumbMapping is dropped here — the buffer is automatically unmapped.
DumbMapping implements AsRef<[u8]> and AsMut<[u8]> for read/write slice access, as well as Deref<Target = [u8]> and DerefMut, so you can use it anywhere a byte slice is expected. Writing a specific pixel at coordinates (x, y) in XRGB8888 format:
let mut map = card
    .map_dumb_buffer(&mut db)
    .expect("Could not map dumb buffer");

let (width, _height) = db.size();
let pitch = db.pitch() as usize;
let bytes = map.as_mut();

let x: usize = 100;
let y: usize = 200;
let offset = y * pitch + x * 4; // 4 bytes per pixel for 32-bpp

bytes[offset]     = 0x00; // Blue
bytes[offset + 1] = 0x80; // Green
bytes[offset + 2] = 0xFF; // Red
bytes[offset + 3] = 0x00; // Padding (X channel — ignored)
The memory mapping is only valid while the DumbMapping object is alive. Do not store raw pointers into the mapped region or let the mapping escape the scope in which it is used. Accessing the memory after DumbMapping is dropped is undefined behaviour.

Creating a framebuffer from a dumb buffer

A DumbBuffer alone cannot be displayed. You must wrap it in a framebuffer object that the DRM kernel subsystem can attach to a CRTC or plane:
// For DrmFourcc::Xrgb8888: depth = 24 (colour bits), bpp = 32 (total bits including padding).
let fb = card
    .add_framebuffer(&db, 24, 32)
    .expect("Could not create framebuffer");

println!("Framebuffer handle: {:?}", fb);
The depth and bpp parameters to add_framebuffer are separate concepts:
  • bpp (bits per pixel) is the total storage width of one pixel, including unused bits. For Xrgb8888 this is 32.
  • depth is the number of bits actually used for colour information. For Xrgb8888, the X channel carries no colour, so depth is 24.
Always pass depth=24, bpp=32 for Xrgb8888. Using mismatched values will cause the kernel to reject the framebuffer with EINVAL.

Passing the framebuffer to modesetting

With the framebuffer handle in hand, you can pass it to set_crtc (legacy) or include it in an AtomicModeReq (atomic):
// Legacy path:
card.set_crtc(crtc.handle(), Some(fb), (0, 0), &[con.handle()], Some(mode))
    .expect("Could not set CRTC");

// Atomic path — add FB_ID to the plane's property set:
req.add_property(
    plane,
    plane_props["FB_ID"].handle(),
    drm::control::property::Value::Framebuffer(Some(fb)),
);
See Legacy Modesetting and Atomic Modesetting for complete examples.

Cleaning up

Destroy the framebuffer object first, then the dumb buffer:
card.destroy_framebuffer(fb)
    .expect("Could not destroy framebuffer");

card.destroy_dumb_buffer(db)
    .expect("Could not destroy dumb buffer");
Destroying the framebuffer first ensures the kernel is no longer scanning out from the buffer before the underlying memory is freed. Dropping the Card without explicitly destroying these objects will leak them in the kernel until the file descriptor is closed (at which point the kernel reclaims them automatically).

DumbBuffer API reference

DumbBuffer implements the drm::buffer::Buffer trait and exposes:
MethodReturn typeDescription
size()(u32, u32)Width and height of the buffer in pixels
pitch()u32Bytes per row, including alignment padding
format()DrmFourccThe pixel format specified at creation time
handle()buffer::HandleRaw GEM handle used by kernel ioctls
To compute the total buffer byte length, use db.pitch() as usize * db.size().1 as usize. DumbMapping API:
Trait / MethodDescription
AsRef<[u8]>Immutable byte-slice view of the mapped region
AsMut<[u8]>Mutable byte-slice view of the mapped region
Deref<Target=[u8]>Auto-deref to &[u8]
DerefMutAuto-deref to &mut [u8]
DropCalls munmap on the region automatically

Build docs developers (and LLMs) love