Table of Contents

GPU Rendering Artifacts on ThinkPad X220 with Hyprland — Diagnosis & Fix

Hardware & Software Context

Machine Lenovo ThinkPad X220 (4291SWP)
GPU Intel HD Graphics 3000 — Sandy Bridge, Gen 6
Kernel driver i915
Mesa driver crocus (Gallium3D for Intel Gen 4–8)
Wayland compositor Hyprland (uses Aquamarine as DRM backend)
Display Internal LVDS-1, 1366×768

Sandy Bridge (2010–2011) is the oldest Intel architecture still supported by Mesa's Gallium3D stack. It cannot use Vulkan (requires Gen 7+), cannot use the iris driver (requires Gen 8+), and only supports OpenGL ES 3.0 — not 3.2. Any rendering stack must go through crocus.


Symptoms


How to Investigate This Class of Issue

The following steps, taken in order, efficiently isolate the cause of system-wide GPU rendering corruption on a Hyprland/Wayland system.

Step 1 — Gather system context

omarchy debug --no-sudo --print

Note the GPU, Mesa version, Hyprland version, and kernel version. On a Sandy Bridge machine: confirm the driver is crocus and the DRI path is /usr/lib/dri/crocus_dri.so.

Step 2 — Check recent package updates

grep -E "(mesa|hyprland|xwayland|vulkan|electron|code)" /var/log/pacman.log | tail -60

The most relevant packages for Wayland compositor rendering are: mesa, hyprland, xorg-xwayland, and libdrm. Note which versions were installed and when symptoms began.

Step 3 — Read the live Hyprland log

cat /run/user/$(id -u)/hypr/*/hyprland.log

Look for: - EGL context creation errorseglCreateContext errored out with EGL_BAD_MATCH: Sandy Bridge only supports GLES 3.0; any code path requesting GLES 3.2 will fail. A failed context without a successful fallback means a renderer is degraded or absent. - GBM buffer formatformat XR24 with modifier 0x...: the modifier field shows whether buffers are LINEAR (modifier 0x0) or tiled (e.g. X_TILED, modifier 0x100000000000001). Tiled formats on Sandy Bridge can corrupt popup surfaces. - DRM commit errorsdrm: Cannot commit when a page-flip is awaiting: frame timing failures; indicates Hyprland is submitting frames faster than the display can flip. - Renderer selected — confirms which GPU and GLES version are active.

Step 4 — Check Aquamarine environment variable support

Hyprland's DRM backend (Aquamarine) respects specific environment variables. Extract the ones it knows about:

strings /usr/lib/libaquamarine.so* | grep -E "^AQ_" | sort -u

Key variables: AQ_NO_MODIFIERS, AQ_NO_ATOMIC, AQ_FORCE_LINEAR_BLIT, AQ_DRM_DEVICES.

Step 5 — Inspect the Mesa driver architecture

# Check if crocus is a standalone library or a symlink to a unified loader
file /usr/lib/dri/crocus_dri.so
 
# Check for a shared libgallium — if present, ALL Gallium drivers live here
ls -lh /usr/lib/libgallium*.so

In Mesa 26.0.x, crocus_dri.so was a standalone self-contained shared library. In Mesa 26.1.x, all Gallium3D drivers were consolidated into libgallium-<version>.so, with crocus_dri.so becoming a thin stub (crocus_dri.so → libdril_dri.so, ~95 KB loader). This architectural change is a common regression vector for older hardware.

Step 6 — Check i915 kernel module parameters

cat /etc/modprobe.d/i915.conf
ls /sys/module/i915/parameters/

Sandy Bridge is sensitive to several i915 features. The following should typically be disabled for stability:

Parameter Recommended Reason
enable_psr 0 Panel Self Refresh causes display corruption on LVDS
enable_fbc 0 Framebuffer Compression has known Sandy Bridge bugs
enable_dc 0 Display power-saving can interfere with DRM commits

Step 7 — Check Mesa shader cache

du -sh ~/.cache/mesa_shader_cache

After a Mesa version upgrade, stale cached shader binaries from the previous version can cause rendering failures. Clear the cache after any Mesa upgrade:

rm -rf ~/.cache/mesa_shader_cache

Step 8 — Confirm the fix candidate by checking cached packages

ls /var/cache/pacman/pkg/mesa-*.zst

If an older working Mesa version is in the package cache, it can be reinstated quickly without downloading. This is the fastest route to confirming a Mesa regression: downgrade, observe whether artifacts disappear, and if so, pin the working version.


Root Causes Found (This System, Mesa 26.1.1 / Hyprland 0.55.2)

Root Cause 1 — Mesa 26.1.1 crocus regression (primary, system-wide)

Mesa 26.1.0 introduced a major architectural refactor: all Gallium3D drivers were merged from individual shared libraries into a single libgallium-<version>.so. The crocus_dri.so driver became a ~95 KB stub loader (libdril_dri.so) that dispatches into the 52 MB monolithic library.

Mesa 26.0.6:  crocus_dri.so  — standalone, ~15 MB, self-contained
Mesa 26.1.1:  crocus_dri.so → libdril_dri.so (95 KB) → libgallium-26.1.1.so (52 MB)

The refactoring introduced a regression in the crocus EGL/GLES rendering path for Sandy Bridge (Gen 6). Because Hyprland's Aquamarine compositor backend uses crocus for all compositor rendering via EGL (every window's contents are textured and blended by crocus), the regression corrupts every composited surface — producing system-wide artifacts.

This is confirmed by: (a) artifacts appearing immediately after the Mesa 26.0.6 → 26.1.1 upgrade, (b) artifacts disappearing after downgrading back to 26.0.6.

Root Cause 2 — GBM buffer modifiers not disabled (popup artifacts)

Hyprland's Aquamarine backend, by default, allocates GBM display buffers using hardware-optimal tiled formats (e.g. X_TILED, modifier 0x100000000000001) for better GPU scan-out performance. On Sandy Bridge, the crocus driver can corrupt new popup surfaces (dropdowns, tooltips) when they first appear in a tiled buffer — the compositor reads back the buffer before the GPU has flushed the tile swizzle.

The fix is to force linear buffer allocation via the Aquamarine environment variable AQ_NO_MODIFIERS=1.

Verification: after setting this variable, the Hyprland log should show modifier 0x0 : LINEAR for all GBM buffer allocations.

Root Cause 3 — VS Code / Electron GPU rendering on Wayland

Omarchy sets ELECTRON_OZONE_PLATFORM_HINT=wayland globally, causing VS Code (Electron) to run natively on Wayland and use the GPU for font rendering. Combined with the crocus regression and Sandy Bridge's GLES 3.0 limitation, Electron's GPU text rendering pipeline produces garbled glyphs.

This is application-scoped and does not require fixing the compositor — it is resolved independently with --disable-gpu in VS Code's launch flags.


Fix Applied

1. Downgrade Mesa 26.1.1 → 26.0.6

The old package was still in the pacman cache:

sudo pacman -U \
  /var/cache/pacman/pkg/mesa-1:26.0.6-1-x86_64.pkg.tar.zst \
  /var/cache/pacman/pkg/vulkan-mesa-implicit-layers-1:26.0.6-1-x86_64.pkg.tar.zst

2. Pin Mesa in pacman.conf

Add to /etc/pacman.conf under [options]:

IgnorePkg = mesa vulkan-mesa-implicit-layers

This prevents omarchy update / pacman -Syu from re-upgrading Mesa until a release that fixes the crocus regression is confirmed working.

3. Force linear GBM buffer allocation

In ~/.config/hypr/hyprland.conf:

# Force Aquamarine to allocate GBM buffers in linear format.
# Sandy Bridge (crocus) corrupts popup surfaces with tiled modifiers.
env = AQ_NO_MODIFIERS,1
 
# Force linear blit path in Aquamarine's renderer (belt-and-suspenders).
env = AQ_FORCE_LINEAR_BLIT,1

4. Disable hardware cursor

In ~/.config/hypr/looknfeel.conf:

cursor {
    no_hardware_cursors = true
}

Sandy Bridge's i915 hardware cursor has known glitches with the crocus Wayland stack.

5. Disable VS Code GPU acceleration

Create ~/.config/code-flags.conf:

--disable-gpu

6. Kernel module parameters for i915

In /etc/modprobe.d/i915.conf:

options i915 enable_psr=0
options i915 enable_fbc=0
options i915 enable_dc=0

Requires a reboot to take effect (or modprobe -r i915 && modprobe i915, which is only possible when Hyprland is not running).

7. Clear Mesa shader cache

rm -rf ~/.cache/mesa_shader_cache

Run this after any Mesa version change.

8. Restart Hyprland session

A full session restart (log out → log back in) is required after a Mesa downgrade. Running processes retain the old Mesa shared library (libgallium-26.1.1.so) in memory; only a fresh Hyprland process loads the downgraded version.


What to Monitor Going Forward


Quick Diagnostic Checklist for the Same Issue

If you are on a ThinkPad X220 (or any Sandy Bridge machine) with Hyprland and see system-wide rendering artifacts after a system update, run through this in order:

  1. pacman -Q mesa — what Mesa version is installed?
  2. cat /run/user/$(id -u)/hypr/*/hyprland.log | grep -E "ERR|modifier|GLES" — any EGL failures or XTILED buffers? 3. file /usr/lib/dri/crocus_dri.so — is it a real library or a symlink to libdril_dri.so? 4. ls -lh /usr/lib/libgallium*.so — does a monolithic libgallium exist? 5. ls /var/cache/pacman/pkg/mesa-*.zst — is an older Mesa version cached for rollback? 6. Set AQ_NO_MODIFIERS=1 in hyprland.conf and reload — do popup artifacts improve? 7. If all else fails, downgrade Mesa and pin it.