Graphics Debugging & Troubleshooting¶
Real-world debugging for when the display stack breaks. This article covers systematic diagnosis of the most common failures: black screen after boot, compositor crashes on hotplug, tearing despite VSync, GPU hangs and resets. Then the complete debugging toolkit: Mesa debug environment variables, DRM kernel debugging, Wayland protocol tracing, the "works on X11 but not Wayland" checklist, and strace patterns for graphics ioctls. Every section includes the actual commands and output you would see when diagnosing the problem.
1. "Black Screen After Boot"¶
The most common graphics failure. Systematic diagnosis from the bottom of the stack upward.
Step 1: Do you have a console?¶
# If the screen is completely black, try switching to a text VT
Ctrl+Alt+F2 # switch to VT2
# If this works: the kernel and display driver are fine, the compositor failed
# If this does not work: the problem is in the kernel display driver
Step 2: Kernel console¶
# Add kernel parameters to force a text console (edit bootloader):
# GRUB: edit at boot, append to linux line:
nomodeset # disable KMS entirely (uses BIOS framebuffer)
# OR:
drm.modeset=1 fbcon=map:0 # force framebuffer console on first output
# If nomodeset gives you a console: the DRM driver is the problem
# If even nomodeset fails: hardware issue, cable, or BIOS/UEFI config
Step 3: DRM debug logging¶
# Enable comprehensive DRM/KMS debug logging (kernel parameter):
drm.debug=0x1ff
# This enables ALL DRM debug categories:
# 0x001 = DRM_UT_CORE (core DRM)
# 0x002 = DRM_UT_DRIVER (driver-specific)
# 0x004 = DRM_UT_KMS (modesetting)
# 0x008 = DRM_UT_PRIME (PRIME/dma-buf)
# 0x010 = DRM_UT_ATOMIC (atomic modesetting)
# 0x020 = DRM_UT_VBL (vblank handling)
# 0x040 = DRM_UT_STATE (state management)
# 0x080 = DRM_UT_LEASE (DRM leases)
# 0x100 = DRM_UT_DP (DisplayPort)
# Runtime (without reboot):
echo 0x1ff > /sys/module/drm/parameters/debug
# Read the debug output:
dmesg | grep -i '\[drm\]'
journalctl -k | grep -i drm
Step 4: Check connector and EDID¶
# List all DRM connectors and their status
cat /sys/class/drm/card0-*/status
# card0-HDMI-A-1/status: connected
# card0-DP-1/status: disconnected
# Read EDID (monitor identification)
cat /sys/class/drm/card0-HDMI-A-1/edid | edid-decode
# If EDID is empty or corrupt: the monitor is not communicating properly
# Or use drm_info (comprehensive DRM information)
drm_info
# Shows: connectors, CRTCs, planes, formats, modifiers, EDID
Step 5: Manual modesetting test¶
# modetest: set a mode manually, bypassing the compositor entirely
# This proves whether the kernel DRM driver can drive the display
# List available modes
modetest -M i915 -c # -M <driver>, -c = connectors
# Output:
# Connectors:
# 32 HDMI-A-1 (connected)
# Modes:
# 1920x1080@60.00 ← use this
# Set a test pattern
modetest -M i915 -s 32:1920x1080@60.00
# If this shows a test pattern: kernel driver works, problem is userspace
# If this fails: kernel driver bug or hardware issue
Step 6: Check initramfs¶
# If the display works with nomodeset but not with KMS:
# The DRM driver might not be in the initramfs
# List modules in initramfs
lsinitramfs /boot/initramfs-$(uname -r).img | grep -E 'drm|i915|amdgpu|nouveau'
# If your GPU driver is missing, add it:
# Debian/Ubuntu:
echo "i915" >> /etc/initramfs-tools/modules
update-initramfs -u
# Dracut (Fedora/RHEL):
echo 'add_drivers+=" i915 "' > /etc/dracut.conf.d/gpu.conf
dracut --force
Decision tree¶
graph TD
Start["Black screen"] --> VT{"Ctrl+Alt+F2<br/>works?"}
VT -->|Yes| Comp["Compositor problem<br/>(check Sway logs)"]
VT -->|No| NoModeset{"nomodeset<br/>gives console?"}
NoModeset -->|Yes| DRMBug["DRM driver issue<br/>(check dmesg, drm.debug)"]
NoModeset -->|No| HW["Hardware/cable/BIOS issue"]
DRMBug --> Modetest{"modetest<br/>shows pattern?"}
Modetest -->|Yes| UserSpace["Userspace issue<br/>(EGL, Mesa config)"]
Modetest -->|No| KernelBug["Kernel driver bug<br/>(check firmware, EDID)"]
2. "Compositor Crashes on Output Hotplug"¶
Plugging/unplugging a monitor (or a VNC client connecting/disconnecting to a virtual output) triggers DRM connector probing. This is a common crash vector.
The problem¶
When a connector state changes:
- Kernel sends a DRM hotplug uevent.
- Compositor re-probes all connectors (
drmModeGetConnector()). - If a connector was removed: its CRTC, planes, and framebuffers become invalid.
- If the compositor still holds references to these objects: crash (use-after-free or invalid ioctl).
Diagnosis¶
# Monitor DRM hotplug events
udevadm monitor --subsystem-match=drm
# KERNEL[12345.678] change /devices/pci.../drm/card0 (drm)
# ACTION=change, HOTPLUG=1
# Check connector state after hotplug
cat /sys/class/drm/card0-*/status
# In Sway debug log:
sway -d 2>&1 | grep -E 'hotplug|output|connector'
# [DEBUG] output_manager: output HDMI-A-1 disconnected
# [ERROR] wlr_output_commit: invalid CRTC ← crash incoming
EDID parsing failures¶
Some monitors send corrupt or non-standard EDID data:
# Read raw EDID
cat /sys/class/drm/card0-HDMI-A-1/edid | xxd | head -20
# Parse with edid-decode (shows warnings for non-standard data)
cat /sys/class/drm/card0-HDMI-A-1/edid | edid-decode 2>&1
# WARN: EDID block 0 checksum is invalid
# WARN: Non-standard detailed timing descriptor
# Force a specific mode if EDID is unreliable:
# Kernel parameter:
video=HDMI-A-1:1920x1080@60e # 'e' = force enable
# Or in Sway config:
output HDMI-A-1 mode 1920x1080@60Hz
Fallback modes¶
When EDID fails, the kernel falls back to a built-in mode list:
# The kernel's fallback modes (drivers/gpu/drm/drm_edid.c):
# 1024x768@60Hz (the "safe" mode)
# 800x600@60Hz
# 640x480@60Hz
# Check which mode is actually active
cat /sys/class/drm/card0-HDMI-A-1/modes
# If only low resolutions appear: EDID is the problem
3. "Tearing Despite VSync"¶
Tearing means the display shows parts of two different frames simultaneously. Even with VSync "enabled", tearing can occur.
Understanding the presentation modes¶
graph LR
subgraph "Double Buffering + VSync (FIFO)"
FB1["Front Buffer<br/>(displayed)"] --> Swap["Swap at<br/>vblank"]
FB2["Back Buffer<br/>(rendering)"] --> Swap
end
| Mode | Behavior | Tearing? | Latency |
|---|---|---|---|
| Immediate (no sync) | Swap buffers instantly | Yes | Lowest |
| FIFO (VSync on) | Wait for vblank to swap | No | +1 frame (16.67ms) |
| FIFO relaxed | FIFO, but if you miss vblank, swap immediately | Sometimes | Variable |
| Mailbox (triple buffering) | Latest frame replaces previous in mailbox, swap at vblank | No | Lowest without tearing |
Why tearing still happens with VSync¶
Cause 1: Legacy page flip (not atomic)
The old drmModeSetCrtc() / drmModePageFlip() API can tear if the
flip is not synchronized with vblank:
# Check if Sway is using atomic modesetting
sway -d 2>&1 | grep -i atomic
# [DEBUG] wlr_drm: using atomic modesetting
# If you see "using legacy modesetting" → upgrade wlroots or check DRM driver
Cause 2: Direct scanout format/modifier mismatch
When Sway attempts direct scanout (client buffer → display, no compositing), it may choose a buffer with the wrong format or modifier:
# Check if direct scanout is happening
sway -d 2>&1 | grep -i scanout
# [DEBUG] output: direct scanout for surface 0xNNNN
# [WARN] output: direct scanout failed, falling back to compositing
Cause 3: Multi-plane tearing (hardware bug)
Some GPU hardware has tearing between overlapping planes (primary + cursor, primary + overlay). The atomic commit should be atomic across planes, but some drivers have bugs:
# Disable hardware cursor (forces cursor into compositing pass)
# Sway config:
seat * hide_cursor when-typing enable
# Or: WLR_NO_HARDWARE_CURSORS=1 sway
Cause 4: VRR (Variable Refresh Rate) misconfiguration
With VRR (FreeSync/G-Sync), the display refresh rate adapts to the application. If VRR is partially enabled, you can get tearing:
# Check VRR status
cat /sys/class/drm/card0-HDMI-A-1/vrr_capable
# 0 = not supported, 1 = supported
# Enable/disable in Sway:
output HDMI-A-1 adaptive_sync on # enable VRR
output HDMI-A-1 adaptive_sync off # disable VRR
4. "GPU Hang / Reset"¶
A GPU hang means the GPU stops processing commands. The kernel driver detects this (usually via a watchdog timer) and attempts a reset.
Reading dmesg for GPU hangs¶
# Intel i915
dmesg | grep -E 'GPU HANG|i915.*reset|Resetting'
# [ 123.456] i915 0000:00:02.0: GPU HANG: ecode 9:1:0x00000000,
# in chromium [1234], reason: Hang on render ring, action: reset
# [ 123.789] i915 0000:00:02.0: Resetting chip for Hang on render ring
# AMD amdgpu
dmesg | grep -E 'amdgpu.*reset|gpu_recover|job timeout'
# [ 456.789] amdgpu 0000:03:00.0: amdgpu: GPU reset begin!
# [ 456.800] amdgpu: ring gfx timeout, but soft recovered
# [ 457.123] amdgpu 0000:03:00.0: amdgpu: GPU reset succeeded
GPU hang analysis¶
# Intel: capture GPU error state (devcoredump)
ls /sys/class/devcoredump/devcd*/
cat /sys/class/devcoredump/devcd0/data > gpu-hang.bin
# Decode with intel_error_decode (from intel-gpu-tools)
intel_error_decode < gpu-hang.bin
# Shows: batch buffer contents, register state, which shader was running
# AMD: devcoredump analysis
cat /sys/class/devcoredump/devcd0/data | umr --dump-ib
# Shows: the GPU instruction buffer that was running when the hang occurred
Common GPU hang causes¶
| Cause | Symptoms | Fix |
|---|---|---|
| Shader infinite loop | Consistent hang on specific content | WebGL content issue; --disable-webgl in Chromium |
| Memory corruption | Random hangs, different contexts | Update GPU firmware, Mesa, kernel |
| Power management | Hang after idle period | i915.enable_rc6=0 (Intel) or amdgpu.dpm=0 (AMD) |
| Overheating | Hang under load, mcelog thermal events |
Check cooling, sensors output |
| Firmware bug | Hang at specific GPU frequency | Update linux-firmware package |
# Update GPU firmware (most GPU hangs are firmware bugs)
apt update && apt install -y linux-firmware
# Reboot for firmware to take effect
# Check current firmware version
dmesg | grep -i firmware
# [ 2.345] i915: loaded firmware i915/tgl_guc_70.1.1.bin
5. Mesa Debugging Toolkit¶
Mesa is the userspace graphics driver stack. It provides debug knobs for every level.
Environment variables¶
# General Mesa debug
MESA_DEBUG=1 sway # enable Mesa debug output
MESA_DEBUG=flush sway # flush after every GL call (slow but catches errors)
LIBGL_DEBUG=verbose sway # verbose GL context creation
# Force software rendering (bypass GPU entirely)
LIBGL_ALWAYS_SOFTWARE=1 sway
# Uses llvmpipe (CPU-based GL). If this works and hardware GL crashes:
# the problem is in the GPU driver.
# EGL debugging
EGL_LOG_LEVEL=debug sway
# Shows: EGL context creation, config selection, display initialization
# Vulkan debugging
VK_LOADER_DEBUG=all sway # Vulkan loader debug
MESA_VK_ABORT_ON_DEVICE_LOSS=1 sway # abort() on GPU hang (for debugging)
Shader debugging¶
# Dump compiled shaders to disk
MESA_SHADER_DUMP_PATH=/tmp/shaders sway
# Creates .glsl / .spirv files for every compiled shader
# Read back shader stats
MESA_SHADER_READ_PATH=/tmp/shaders sway
# Useful for testing shader modifications without recompiling Mesa
# Intel-specific: dump shader assembly
INTEL_DEBUG=bat,vs,fs sway
# bat = batch buffers, vs = vertex shaders, fs = fragment shaders
apitrace: record and replay GL calls¶
# Record all GL calls made by Sway
apitrace trace --api=egl sway
# Replay the trace (on any machine with compatible GL)
apitrace replay sway.trace
# Dump the trace as text (every GL call with parameters)
apitrace dump sway.trace | head -100
# 1 eglGetPlatformDisplay(platform = EGL_PLATFORM_GBM_KHR, ...)
# 2 eglInitialize(dpy = 0x1, major = &3, minor = &0)
# 3 eglChooseConfig(...)
# Find the GL call that causes a hang
apitrace replay --headless --loop sway.trace
# Binary search: apitrace trim to narrow down the problem call
RenderDoc (Vulkan/GL frame analysis)¶
# Capture a frame for analysis
renderdoccmd capture sway
# Opens a frame capture that shows:
# - Every draw call
# - Texture contents at each stage
# - Shader source code
# - Pipeline state (blend, depth, stencil)
# - GPU timing per draw call
6. DRM Kernel Debugging¶
drm.debug kernel parameter¶
The DRM subsystem has extensive debug logging controlled by a bitmask:
# Enable at boot (GRUB kernel parameter):
drm.debug=0x1ff
# Enable at runtime:
echo 0x1ff > /sys/module/drm/parameters/debug
# WARNING: this produces ENORMOUS log output. Use targeted bits:
# Just KMS/modesetting:
echo 0x04 > /sys/module/drm/parameters/debug
# Just atomic commits:
echo 0x14 > /sys/module/drm/parameters/debug
# Just dma-buf/PRIME:
echo 0x08 > /sys/module/drm/parameters/debug
drm_info: comprehensive DRM state¶
# Install: apt install drm-info (or build from source)
drm_info
# Output shows:
# - Device: /dev/dri/card0 (i915)
# - Connectors: HDMI-A-1 (connected), DP-1 (disconnected)
# - CRTCs: CRTC 0 (active, 1920x1080@60.00)
# - Planes: primary (formats: XRGB8888, ARGB8888, NV12...)
# - Modifiers: I915_FORMAT_MOD_Y_TILED, LINEAR
modetest: manual modesetting¶
# modetest is from libdrm-tests / drm-utils
# List everything
modetest -M i915
# Set a specific mode with a color pattern
modetest -M i915 -s 32:1920x1080@60.00 -P 31@51:1920x1080+0+0
# -s <connector_id>:<mode>
# -P <plane_id>@<crtc_id>:<WxH+X+Y>
# Test atomic modesetting
modetest -M i915 -a -s 32:1920x1080@60.00
# -a = use atomic API
Tracing DRM ioctls with strace¶
# Trace all DRM ioctls made by Sway
strace -e trace=ioctl -f -p $(pidof sway) 2>&1 | grep -i drm
# ioctl(7, DRM_IOCTL_MODE_ATOMIC, ...) ← atomic commit
# ioctl(7, DRM_IOCTL_MODE_GETCONNECTOR, ...) ← connector probe
# ioctl(7, DRM_IOCTL_MODE_ADDFB2, ...) ← add framebuffer
# ioctl(7, DRM_IOCTL_GEM_CLOSE, ...) ← free buffer
# Measure ioctl latency
strace -e trace=ioctl -T -f -p $(pidof sway) 2>&1 | grep DRM_IOCTL_MODE_ATOMIC
# ioctl(7, DRM_IOCTL_MODE_ATOMIC, ...) = 0 <0.000234>
# ^^^^^^^^ 234 microseconds
7. Wayland Protocol Debugging¶
WAYLAND_DEBUG=1¶
The most important Wayland debugging tool. Logs every protocol message between client and compositor:
# Debug a specific client
WAYLAND_DEBUG=1 chromium --ozone-platform=wayland 2>&1 | head -50
# [1234567.890] -> wl_compositor@3.create_surface(new id wl_surface@8)
# [1234567.891] -> xdg_wm_base@5.get_xdg_surface(new id xdg_surface@9, wl_surface@8)
# [1234567.892] -> xdg_surface@9.get_toplevel(new id xdg_toplevel@10)
# [1234567.893] -> xdg_toplevel@10.set_title("Chromium")
# [1234567.894] -> wl_surface@8.commit()
# [1234567.895] wl_display@1.error(xdg_surface@9, 4, "xdg_surface not configured")
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Common error: commit before configure
# Debug the compositor side (all clients)
WAYLAND_DEBUG=server sway 2>&1 | grep -E 'error|destroy|commit'
Protocol message format¶
[timestamp] -> object@id.method(args) # client → compositor (request)
[timestamp] object@id.event(args) # compositor → client (event)
Common protocol errors¶
| Error message | Cause | Fix |
|---|---|---|
xdg_surface not configured |
Client committed before handling the initial configure event | Wait for xdg_surface.configure before committing |
wl_buffer not released |
Client destroyed a buffer still in use by compositor | Wait for wl_buffer.release before destroying |
invalid object |
Client sent a request on a destroyed object | Object lifecycle bug |
no buffer attached |
Client committed a surface with no buffer | Attach a buffer (wl_shm or dma-buf) before commit |
wlhax: Wayland protocol inspector¶
# wlhax interposes between client and compositor
wlhax -- chromium --ozone-platform=wayland
# Shows protocol messages with pretty-printed arguments
# Filter for specific interfaces
wlhax --filter='wl_surface,xdg_*' -- chromium --ozone-platform=wayland
libinput debug-events¶
For input debugging (keyboard, mouse, touch):
# Show all input events as received by libinput
sudo libinput debug-events
# event4 POINTER_MOTION +3.456s 12.34/ 5.67
# event4 POINTER_BUTTON +3.789s BTN_LEFT pressed
# event6 KEYBOARD_KEY +4.012s KEY_A pressed
# Show device capabilities
sudo libinput debug-events --show-keycodes
# Useful for diagnosing: "key not working", "mouse inverted", "touchpad gestures broken"
8. The "It Works on X11 but Not Wayland" Checklist¶
When an application works on X11 but fails on Wayland, check these in order:
1. XWayland fallback¶
# Check if the app is running on XWayland or native Wayland
xprop -root | grep -i wayland # runs on X11 (shows nothing meaningful)
# Better: check the app's environment
cat /proc/$(pidof myapp)/environ | tr '\0' '\n' | grep DISPLAY
# DISPLAY=:0 ← XWayland
# WAYLAND_DISPLAY=wayland-1 ← native Wayland
# Both present → app might use either
# Force XWayland (workaround)
GDK_BACKEND=x11 myapp
QT_QPA_PLATFORM=xcb myapp
# Force native Wayland
GDK_BACKEND=wayland myapp
QT_QPA_PLATFORM=wayland myapp
ELECTRON_OZONE_PLATFORM_HINT=wayland myapp
2. Missing Wayland protocols¶
# Check which protocols the compositor supports
wayland-info
# Outputs all interfaces and versions. Look for:
# - xdg_wm_base (v6) ← window management
# - zwp_linux_dmabuf_v1 (v5) ← zero-copy buffers
# - wp_viewporter (v1) ← buffer scaling
# - xdg_activation_v1 (v1) ← window activation
# - wp_fractional_scale_v1 ← fractional scaling
# If a protocol is missing and the app needs it:
# the app will fall back to a less efficient path or fail
3. Client-Side Decorations (CSD) vs Server-Side (SSD)¶
# Sway uses SSD by default for xdg_toplevel
# If an app draws its own title bar AND the compositor draws one:
# → double decorations
# Check if the app supports xdg-decoration protocol
WAYLAND_DEBUG=1 myapp 2>&1 | grep decoration
# If no decoration negotiation: app assumes CSD
# Sway config to force CSD or SSD:
for_window [app_id="chromium"] border none # no SSD
for_window [app_id="firefox"] border normal # SSD with title bar
4. Clipboard and drag-and-drop¶
# X11 clipboard (CLIPBOARD, PRIMARY) ↔ Wayland clipboard
# XWayland bridges this automatically
# Native Wayland apps use wl_data_device
# Debug clipboard issues
wl-copy "test" # copy to Wayland clipboard
wl-paste # paste from Wayland clipboard
xclip -o # paste from X11 clipboard (via XWayland bridge)
5. Screen capture / recording¶
The biggest X11 → Wayland difference. X11 allows any client to capture any
window (XGetImage). Wayland does not. Solutions:
# Wayland screen capture protocols:
# - ext-image-copy-capture-v1 (new, preferred, Sway 1.10+)
# - wlr-screencopy (wlroots-specific, widely supported)
# - xdg-desktop-portal + PipeWire (GNOME/KDE)
# Check if PipeWire screen sharing works
pw-cli ls
# Look for video source nodes
# For OBS:
# Needs xdg-desktop-portal-wlr (for Sway) or portal-gnome
6. Input method (IME)¶
# Wayland IME protocol: zwp_text_input_v3
# Supported by: Sway (wlroots), GNOME, KDE
# Check if IME works
WAYLAND_DEBUG=1 myapp 2>&1 | grep text_input
# If no text_input messages: app does not support Wayland IME
# Workaround: run via XWayland
9. strace Patterns for Graphics¶
Tracing DRM ioctls¶
# All DRM ioctls with timing
strace -e trace=ioctl -T -f -p $(pidof sway) 2>&1 | \
grep -E 'DRM_IOCTL_(MODE_ATOMIC|MODE_ADDFB|PRIME|GEM)'
# Decoded ioctl names (strace knows DRM ioctls since strace 5.x)
strace -e trace=%ioctl -y -f -p $(pidof sway) 2>&1 | grep drm
# ioctl(7</dev/dri/card0>, DRM_IOCTL_MODE_ATOMIC, ...) = 0 <0.000234>
# ^ -y flag shows the file path
Spotting failed GBM allocations¶
# Trace GBM buffer allocations (via ltrace)
ltrace -e 'gbm_bo_create+gbm_bo_create_with_modifiers' -p $(pidof sway)
# gbm_bo_create(0x55a, 1920, 1080, 875713112, 5) = 0x55b ← success
# gbm_bo_create(0x55a, 3840, 2160, 875713112, 5) = 0 ← FAILED
# The format argument (875713112 = 0x34325258 = "XR24" = XRGB8888)
# If gbm_bo_create returns NULL: the GPU driver does not support
# this format/modifier combination at this resolution
Finding the blocked ioctl¶
When Sway or a client hangs, find which syscall it is stuck on:
# Show the current syscall for each thread
cat /proc/$(pidof sway)/task/*/syscall
# If you see:
# 16 0x... 0x... 0x... ... ← syscall 16 = ioctl
# Then strace -p to see which ioctl:
strace -p $(pidof sway) -e trace=ioctl
# ioctl(7, DRM_IOCTL_MODE_ATOMIC, ...) ← stuck here = waiting for vblank or GPU
# Or check the kernel wait state
cat /proc/$(pidof sway)/task/*/wchan
# drm_atomic_helper_wait_for_flip_done ← waiting for page flip
# schedule ← sleeping (epoll_wait probably)
Tracing mmap for GPU buffer mapping¶
# Trace all mmap/munmap calls (GPU buffer creation/destruction)
strace -e trace=mmap,munmap,mmap2 -f -p $(pidof sway) 2>&1 | \
grep -v 'PROT_READ.*MAP_PRIVATE' # filter out library loads
# mmap(NULL, 8294400, PROT_READ|PROT_WRITE, MAP_SHARED, 8, 0) = 0x7f...
# ^^^^^^^^ = 1920*1080*4 bytes = one XRGB8888 framebuffer
# ^^^^^^^^^^^ fd 8 = a dma-buf
10. Debugging Checklist by Symptom¶
| Symptom | First check | Second check | Third check |
|---|---|---|---|
| Black screen | Ctrl+Alt+F2 (VT switch) |
dmesg \| grep drm |
modetest manual mode |
| Crash on hotplug | sway -d log near crash |
udevadm monitor --subsystem=drm |
EDID: edid-decode |
| Tearing | sway -d \| grep atomic |
Check VRR: cat vrr_capable |
Disable hardware cursor |
| GPU hang | dmesg \| grep -i hang |
devcoredump in /sys/class/devcoredump/ |
Update linux-firmware |
| Slow rendering | intel_gpu_top / radeontop |
GALLIUM_HUD=fps sway |
perf record -p PID |
| Wrong colors | Check format: drm_info \| grep format |
MESA_DEBUG=1 |
Force XRGB8888 |
| No Vulkan | vulkaninfo |
ls /usr/share/vulkan/icd.d/ |
Set VK_ICD_FILENAMES |
| Client crash | WAYLAND_DEBUG=1 myapp |
Look for protocol errors | coredumpctl debug |
| Missing output | sway -d \| grep output |
cat /sys/class/drm/card*/status |
Check cable/adapter |
| Flickering | sway -d \| grep damage |
Atomic commit errors in dmesg | Try WLR_NO_HARDWARE_CURSORS=1 |
What's new (2025--2026)
- DRM panic handler (kernel 6.12): displays a QR code on kernel panic, linking to a bug report. Written in Rust by Jocelyn Falempe (Red Hat).
- ext-image-copy-capture-v1: the new standard Wayland screen capture
protocol, replacing the wlroots-specific
wlr-screencopy. Sway 1.10 supports it. - Mesa 24.3+: improved error messages for buffer allocation failures,
making
MESA_DEBUG=1much more useful. - intel_gpu_top now shows per-engine utilization for Xe2 (Lunar Lake) GPUs.
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.
- EDID (Extended Display Identification Data)
- Data structure sent by monitors over DDC (I2C), describing supported resolutions, timings, and capabilities.
- modetest
- Tool from libdrm-tests for manual DRM modesetting. Sets modes, creates framebuffers, displays test patterns.
- drm_info
- Tool displaying comprehensive DRM device information: connectors, CRTCs, planes, formats, modifiers.
- devcoredump
- Kernel mechanism for GPU drivers to dump hardware state after a hang. Available via
/sys/class/devcoredump/. - apitrace
- Tool for recording and replaying OpenGL/EGL API calls. Essential for reproducing GPU bugs.
- GALLIUM_HUD
- Mesa environment variable enabling real-time GPU metrics overlay (FPS, draw calls, VRAM usage).
- XWayland
- X11 compatibility layer running inside a Wayland compositor. Provides X11 services for legacy apps.
- WAYLAND_DEBUG
- Environment variable (set to
1orserver) enabling Wayland protocol message logging. - VRR (Variable Refresh Rate)
- Display technology (FreeSync/G-Sync) where the monitor adapts its refresh rate to the application's frame rate.
- nomodeset
- Kernel parameter disabling KMS. Falls back to BIOS framebuffer. Used for diagnosis when KMS fails.
- Direct scanout
- Optimization: the compositor sends a client's buffer directly to the display without a compositing pass.
- wl-copy / wl-paste
- Command-line tools for Wayland clipboard access. Part of the
wl-clipboardpackage. - PipeWire
- Multimedia framework handling both audio and video streams. Used for Wayland screen capture via xdg-desktop-portal.