| — | — |
| 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.
The following steps, taken in order, efficiently isolate the cause of system-wide GPU rendering corruption on a Hyprland/Wayland system.
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.
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.
cat /run/user/$(id -u)/hypr/*/hyprland.log
Look for:
- EGL context creation errors — eglCreateContext 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 format — format 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 errors — drm: 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.
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.
# 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.
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 |
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
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.
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.
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.
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.
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
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.
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
In ~/.config/hypr/looknfeel.conf:
cursor { no_hardware_cursors = true }
Sandy Bridge's i915 hardware cursor has known glitches with the crocus Wayland stack.
Create ~/.config/code-flags.conf:
--disable-gpu
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).
rm -rf ~/.cache/mesa_shader_cache
Run this after any Mesa version change.
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.
IgnorePkg line from /etc/pacman.conf and upgrade.crocus or Gen6.AQ_NO_ATOMIC=1 flag in Aquamarine was tested and found to cause additional errors on Sandy Bridge (Aquamarine's legacy DRM mode reports missing CRTC/encoder IDs for all disconnected ports). It should not be set on this hardware.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:
pacman -Q mesa — what Mesa version is installed?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.