Kernel Graphics: DRM/KMS, GBM, EGL & dma-buf¶
The Linux graphics stack runs from kernel DRM/KMS (modesetting and buffer management) through GBM (buffer allocation) and EGL (GPU context) to the compositor. This article covers the full display pipeline, atomic modesetting, dma-buf zero-copy sharing, and the lifecycle of a frame from render to vblank.
Display Pipeline¶
graph LR
FB["Framebuffer<br/>(pixel data)"] --> PLANE["Plane<br/>(scaling, format)"]
PLANE --> CRTC["CRTC<br/>(scanout engine)"]
CRTC --> ENC["Encoder<br/>(signal conversion)"]
ENC --> CONN["Connector<br/>(HDMI, DP, virtual)"]
| Object | Role |
|---|---|
| Connector | Physical port (HDMI-A-1, DP-1). Reports EDID, connection status. |
| Encoder | Converts CRTC pixel stream to connector signal (TMDS, LVDS). |
| CRTC | Scanout engine -- reads planes, drives timing (mode lines). |
| Plane | Image layer. Primary (main fb), cursor, overlay (video HW scaling). |
| Framebuffer | Handle wrapping a GEM/dma-buf with format + dimensions + pitches. |
Atomic Modesetting¶
Legacy KMS set properties one-at-a-time (flickering, tearing). Atomic
modesetting commits all changes in a single drmModeAtomicCommit():
drmModeAtomicReq *req = drmModeAtomicAlloc();
/* Set plane framebuffer and position */
drmModeAtomicAddProperty(req, plane_id, prop_fb_id, fb_id);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_id, crtc_id);
drmModeAtomicAddProperty(req, plane_id, prop_src_w, width << 16);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_w, width);
/* TEST_ONLY first to validate without applying */
int ret = drmModeAtomicCommit(fd, req,
DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
if (ret == 0) {
/* Commit for real, non-blocking with page-flip event */
drmModeAtomicCommit(fd, req,
DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK, userdata);
}
drmModeAtomicFree(req);
Key flags: TEST_ONLY (validate), NONBLOCK (don't stall), ALLOW_MODESET
(permit mode changes that may blank the display briefly).
GBM Buffer Allocation¶
GBM (Generic Buffer Manager) allocates GPU buffers backed by GEM objects.
gbm_surface is legacy
The gbm_surface / gbm_surface_lock_front_buffer API is now considered
legacy. KWin ported away from it in 2023, and wlroots uses explicit
gbm_bo allocation + direct drmModeAddFB2. New compositors should
allocate gbm_bo objects directly rather than going through
gbm_surface.
struct gbm_device *gbm = gbm_create_device(drm_fd);
struct gbm_bo *bo = gbm_bo_create(gbm,
1920, 1080, GBM_FORMAT_XRGB8888,
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
/* Get dma-buf fd for zero-copy sharing */
int dmabuf_fd = gbm_bo_get_fd(bo);
| Usage flag | Meaning |
|---|---|
GBM_BO_USE_SCANOUT |
Buffer can be scanned out by CRTC |
GBM_BO_USE_RENDERING |
Buffer can be GL render target |
GBM_BO_USE_LINEAR |
Force linear layout (needed for screencopy/VNC) |
EGL Platform Setup¶
EGL binds OpenGL ES to a native display (GBM, Wayland, or X11):
/* For a compositor rendering on DRM/GBM */
EGLDisplay egl_display = eglGetPlatformDisplay(
EGL_PLATFORM_GBM_KHR, gbm_device, NULL);
eglInitialize(egl_display, &major, &minor);
EGLConfig config;
eglChooseConfig(egl_display, attribs, &config, 1, &n);
EGLContext ctx = eglCreateContext(egl_display, config,
EGL_NO_CONTEXT, context_attribs);
/* Create EGLImage from dma-buf for texturing client buffers */
EGLImage img = eglCreateImage(egl_display, EGL_NO_CONTEXT,
EGL_LINUX_DMA_BUF_EXT, NULL, img_attribs);
wlroots does all of this internally. The compositor author uses
wlr_renderer and never touches EGL directly.
dma-buf Zero-Copy Sharing¶
graph LR
CLIENT["Client (Chromium)"] -- "dma-buf fd via<br/>wl_drm / linux-dmabuf" --> COMP["Compositor (Sway)"]
COMP -- "dma-buf fd via<br/>wlr-screencopy" --> VNC["WayVNC"]
CLIENT -- "same GPU<br/>memory" --> GPU["GPU Buffer<br/>(GEM object)"]
COMP -- "EGLImage import" --> GPU
VNC -- "mmap or GL read" --> GPU
A dma-buf is a file descriptor referencing a kernel buffer. Any process with the fd can map or import it -- zero copies between client, compositor, and VNC capture.
Key properties: fds are passed via SCM_RIGHTS over Unix sockets. Metadata
(format, modifier, stride) travels alongside in the Wayland protocol.
If you know Unix pipes
dma-buf fds are like pipes but for GPU memory. Same fd passing
(SCM_RIGHTS), but the kernel guarantees DMA-capable physical pages and
zero-copy access across devices.
If you build OCI images
Building a custom Linux distro is like writing a Dockerfile for the whole machine. Base image = debootstrap. Layers = overlayfs. Registry = OSTree repo.
Direct Scanout Optimization¶
When a single client covers the entire output with a compatible buffer:
- Compositor detects 1:1 coverage, no transforms, supported format/modifier.
- Atomic commit with the client's buffer directly on the primary plane.
- No compositing pass -- GPU renders directly to display.
- Saves one full-screen copy per frame (critical at 4K 60Hz).
wlroots wlr_scene implements this automatically via
wlr_scene_output_commit().
Frame Lifecycle¶
sequenceDiagram
participant App as Client
participant Comp as Compositor
participant KMS as DRM/KMS
App->>Comp: wl_surface.commit(dma-buf)
Comp->>Comp: Scene graph damage check
Comp->>Comp: Render (or direct scanout)
Comp->>KMS: drmModeAtomicCommit(NONBLOCK)
KMS->>KMS: Wait for vblank
KMS->>Comp: Page-flip event
Comp->>App: wl_callback.done(timestamp)
Note over App: App renders next frame
- Client commits a buffer (dma-buf or shm).
- Compositor checks damage, composites if needed.
- Atomic commit queues the framebuffer for next vblank.
- Kernel flips at vblank, fires page-flip event.
- Compositor sends
frame callbackto client -- client may now render again. - Old buffer released back to client.
This ensures no tearing (vblank sync) and no over-rendering (frame callbacks throttle the client).
Expert insight
"Our DRM testing is seriously lacking." -- Linus Torvalds, 6.8 release cycle, on the need for better CI coverage of kernel graphics drivers.
What's new (2025–2026)
- Rust DRM bindings:
rvkms(virtual KMS driver in Rust) merged upstream. Nova (NVIDIA) and Asahi (Apple) GPU drivers written in Rust are in active development. - Dave Airlie (DRM maintainer): "about a year away" from requiring Rust for new DRM drivers.
- DRM panic handler merged in 6.12 -- displays QR codes on kernel panic for easy bug reporting. Written in Rust by Jocelyn Falempe (Red Hat).
Glossary
- DRM (Direct Rendering Manager)
- Kernel subsystem managing GPU hardware: modesetting, buffer management, command submission.
- KMS (Kernel Mode Setting)
- DRM subsystem controlling display outputs: modes, CRTCs, planes, connectors.
- CRTC
- CRT Controller -- scanout engine that reads pixel data from planes and drives display timing.
- GBM (Generic Buffer Manager)
- Userspace library allocating GPU buffers. Mesa's abstraction over driver-specific GEM ioctls.
- GEM (Graphics Execution Manager)
- Kernel buffer object API. Each driver implements GEM (i915, amdgpu, etc.).
- dma-buf
- Kernel framework for sharing buffers between devices/processes via file descriptors.
- EGLImage
- EGL handle wrapping a dma-buf for use as a GL texture or renderbuffer.
- Atomic commit
- Single ioctl updating all display properties at once, avoiding intermediate states.
- Page flip
- Swapping the displayed framebuffer at vblank. Kernel signals completion via DRM event.