OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 145

Waydroid GPU detection and hardware acceleration

Diagnose rendering issues through DRM nodes, Android graphics properties, and compositor interactions.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSWaydroid ↗
About 11 min readStart reading

Scope and prerequisites#

Commands and output are generic examples. Kernel, image, package, and service versions affect specific behavior; check the documentation for the versions you use as you read.

Before modifying containers, images, mounts, or firewalls, back up Android data and stop the relevant sessions; examples involving deletion or forced stops may lose data. Manage NixOS system and user-environment configuration through Nix. The general Linux sections using apt/pacman, writing directly to /etc, or installing with pip do not apply to a NixOS host. Rebuild build, test, and switch operations and post-startup functional verification are separate stages.

The key to Waydroid performance is its GPU detection chain: it determines whether rendering uses hardware acceleration (GBM gralloc + Mesa EGL) or CPU software rendering (SwiftShader). Understanding the full chain, from scanning DRI nodes to mapping the Vulkan HAL and writing configuration files, is essential to choosing correctly between the hardware and software paths.


GPU detection#

Waydroid’s GPU detection is implemented in tools/helpers/gpu.py, driven by the getDriNode() function. It does not depend on the running X11/Wayland environment; instead, it scans the Linux DRM subsystem directly to identify an available acceleration device before the container starts.

The DRM/DRI node model

The Linux kernel manages GPUs through the DRM (Direct Rendering Manager) subsystem. Each GPU exposes two types of character device:

Device node Purpose Example
/dev/dri/cardN modesetting — display-mode management (resolution, refresh rate, CRTC) /dev/dri/card0
/dev/dri/renderDN rendering — computation/rendering only (still subject to file permissions/ACLs, but does not require DRM master) /dev/dri/renderD128

The key distinction: cardN requires DRM master privileges, usually held by the compositor, while renderDN can be opened for rendering/computation only by processes with the appropriate file permissions or ACLs. As an LXC container, Waydroid needs only a render node to participate in OpenGL/Vulkan acceleration, but still requires driver support in the image and appropriate authorization. It generally does not handle the host’s display modesetting; the host compositor (Wayland) handles display output.

On multi-GPU systems, node numbers increase:

/dev/dri/
├── card0      ← iGPU modesetting
├── card1      ← dGPU modesetting
├── renderD128 ← iGPU 渲染 (对应 card0)
└── renderD129 ← dGPU 渲染 (对应 card1)

The getDriNode() detection algorithm

flowchart TD
    START(["getDriNode() 开始"]) --> CHECK_CFG{"/var/lib/waydroid/waydroid.cfg<br/>是否指定 drm_device?"}
    CHECK_CFG -->|"是"| USE_CFG["验证指定节点存在且驱动受支持<br/>仍过滤 nvidia"]
    CHECK_CFG -->|"否"| SCAN["扫描 /dev/dri/renderD*<br/>按编号排序"]
    SCAN --> FOR_EACH["遍历每个 renderD* 节点"]
    FOR_EACH --> READ_U["读取 /sys/class/drm/<br/>{dev}/device/uevent"]
    READ_U --> CHECK_NV{"内核驱动<br/>是 nvidia?"}
    CHECK_NV -->|"是 NVIDIA → 过滤"| FOR_EACH
    CHECK_NV -->|"否 → 有效"| GET_CARD["getCardFromRender()<br/>查找对应 card* 节点"]
    GET_CARD --> VALID["返回 (render_node, card_node)<br/>存入配置属性"]
    FOR_EACH -->|"遍历耗尽<br/>未找到有效节点"| NO_GPU["返回两个空字符串<br/>→ 后续处理软件路径"]
    
    style START fill:#0f3460,stroke:#16213e,color:#e0e0e0
    style VALID fill:#1b4332,stroke:#2d6a4f,color:#e0e0e0
    style NO_GPU fill:#5c1a1a,stroke:#8b0000,color:#e0e0e0
    style CHECK_NV fill:#533483,stroke:#3b2665,color:#e0e0e0

The algorithm runs in five steps:

Step 1: check for a user override

Waydroid lets you specify a GPU device manually in /var/lib/waydroid/waydroid.cfg, under [waydroid]:

[waydroid]
drm_device = /dev/dri/renderD129

This is particularly useful on multi-GPU systems, such as laptops with an Intel iGPU + AMD dGPU. If automatic detection chooses the wrong GPU, specifying one manually bypasses the automatic scan order, but node-existence checks and driver filtering still apply.

Step 2: scan render nodes

Scan /dev/dri/renderD* in alphabetical order. Note that alphabetical order places renderD128 before renderD129, but does not guarantee that 128 corresponds to card0; verify the mapping through sysfs.

Step 3: read the kernel driver name

cat /sys/class/drm/renderD128/device/uevent
# 输出示例:
# DRIVER=amdgpu
# PCI_SLOT_NAME=0000:03:00.0

The driver name is read through sysfs rather than lspci, because sysfs provides a direct mapping from device nodes to kernel drivers without parsing the PCI topology.

Step 4: filter out NVIDIA

UNSUPPORTED_GPU_DRIVERS = ["nvidia"]

Modern NVIDIA drivers support GBM, but that does not guarantee compatibility between their userspace components and the Mesa/gralloc/EGL integration in Waydroid’s Android image. This upstream version still filters by the nvidia driver name. Nouveau appears in the mapping table; its performance and compatibility depend on the GPU generation, kernel, Mesa, and image, so it should not be declared universally unusable.

Step 5: obtain the corresponding card node

Look it up with getCardFromRender():

/sys/class/drm/{dev}/device/drm/card*

Determine which card node is associated with the render node. Although Waydroid does not use the card node directly for rendering, it needs their mapping to generate the complete LXC device-binding configuration.

The fallback path when no GPU is available

If getDriNode() returns None because there are no DRI nodes or all have been filtered out, make_base_props() automatically switches to software rendering:

ro.hardware.gralloc=default
ro.hardware.egl=swiftshader

This path is most common in VM environments, on NVIDIA platforms, and on servers without GPUs.


Vulkan driver mapping#

After a DRI node is detected, getVulkanDriver() maps the kernel driver name to an Android Vulkan HAL implementation. The mapping logic is called in make_base_props(), and the generated properties are injected into waydroid_base.prop in the Android container.

The complete mapping table

Host kernel driver Hardware vendor Android Vulkan HAL Notes
i915 Intel intel Intel Gen 9+ (Skylake and newer)
i915 (gen < 9) Intel intel_hasvk Intel Gen < 9 (Broadwell and older) uses the legacy Vulkan driver
xe Intel intel The next-generation Xe kernel driver (Lunar Lake, etc.)
amdgpu AMD radeon AMD Radeon RX/Pro series
radeon AMD Not listed in the mapping checked for this article The original notes treated automatic support for the older driver as guaranteed; actual validation is required
panfrost ARM Mali panfrost Open-source driver for Mali Midgard/Bifrost GPUs
msm / msm_dpu Qualcomm freedreno Qualcomm Adreno GPU (Snapdragon)
vc4 Broadcom broadcom Broadcom VideoCore IV/V
nouveau NVIDIA nouveau Open-source NVIDIA driver (limited performance; generally not recommended)

Determining Intel GPU generations Intel is the only GPU vendor requiring an additional generation check. Waydroid reads /sys/kernel/debug/dri/0/i915_capabilities to obtain the GPU generation. When generation < 9, it uses intel_hasvk instead of intel. This is a separate Vulkan driver Mesa provides for older Intel GPUs and is incompatible with the newer intel Vulkan driver (ANV).

The make_base_props() configuration logic

flowchart LR
    DRI["getDriNode()<br/>返回结果"]
    DRI -->|"有 DRI 节点"| HW["硬件加速路径"]
    DRI -->|"None"| SW["软件渲染路径"]
    
    HW --> VK["getVulkanDriver()<br/>内核驱动 → Vulkan HAL"]
    HW --> GRALLOC["ro.hardware.gralloc=gbm<br/>GBM Gralloc"]
    HW --> EGL["ro.hardware.egl=mesa<br/>Mesa EGL"]
    VK --> PROP["写入 /var/lib/waydroid/<br/>waydroid_base.prop"]
    GRALLOC --> PROP
    EGL --> PROP
    
    SW --> SW_GR["ro.hardware.gralloc=default<br/>默认 Gralloc"]
    SW --> SW_EGL["ro.hardware.egl=swiftshader<br/>SwiftShader EGL"]
    SW --> SW_VK["无 Vulkan HAL<br/>CPU 模拟 Vulkan"]
    
    style HW fill:#1b4332,stroke:#2d6a4f,color:#e0e0e0
    style SW fill:#5c1a1a,stroke:#8b0000,color:#e0e0e0

make_base_props() also uses find_hidl() to check whether the android.hardware.graphics.allocator@4.0 HIDL service is available. If it is and a DRI node exists, hardware-accelerated configuration takes highest priority.

Mounting device nodes in the container

The LXC configuration generator, generate_nodes_lxc_config(), bind-mounts the following host device nodes into the Android container:

Category Device node Purpose
Primary GPU /dev/dri/renderD* OpenGL/Vulkan rendering
Qualcomm /dev/kgsl-3d0 Qualcomm Adreno GPU
ARM Mali /dev/mali0 ARM Mali GPU
PowerVR /dev/pvr_sync PowerVR synchronization device
WSLg /dev/dxg WSLg DirectX device
Framebuffer /dev/fb*, /dev/graphics/fb* Framebuffer device
Camera/hardware encoding /dev/video* V4L2 devices
DMA /dev/dma_heap/* DMA-BUF heap allocator
Input /dev/uhid, /dev/input/* Userspace HID and input devices
Sensors /sys/class/leds/vibrator Vibration motor

Only the render node selected by getDriNode() is mounted. Other render nodes on a multi-GPU system do not enter the container.


Switching between hardware acceleration and software rendering#

GBM + Mesa EGL (the hardware-accelerated path)

This is Waydroid’s preferred path. It is enabled automatically when a DRI node is available:

ro.hardware.gralloc = gbm
ro.hardware.egl = mesa

GBM (Generic Buffer Management) is a userspace buffer-management API provided by Mesa and others that works with kernel DRM drivers. Waydroid’s GBM gralloc allocates Android graphics buffers through Mesa’s GBM backend. These buffers are allocated on the host GPU and can be used directly by the host compositor: zero-copy.

The process:

  1. An Android app requests a graphics buffer
  2. GBM gralloc allocates a BO (Buffer Object) on the host GPU through Mesa GBM
  3. Android SurfaceFlinger passes the BO as a dma-buf
  4. The Waydroid Session Manager converts the dma-buf to a Wayland wl_buffer
  5. The compositor renders directly, with no CPU copy

Performance checks: compare the actual renderer, extensions, versions, and application workload. dma-buf can reduce copying, but cross-GPU operation and format/modifier mismatches can still cause copies or failures. It does not guarantee native-equivalent performance or a fixed API version.

SwiftShader (the software-rendering fallback)

ro.hardware.gralloc = default
ro.hardware.egl = swiftshader

SwiftShader is Google’s high-performance CPU renderer; specific API support depends on the build inside the container and its Android integration. It is not traditional slow software rendering: it uses JIT compilation and SIMD optimizations, but its performance gap relative to hardware acceleration must be measured for the workload.

When to use software rendering:

  • An optional fallback when the chosen NVIDIA path is incompatible with the image
  • A virtual machine without GPU passthrough (such as QEMU without virtio-gpu 3D)
  • A server without a GPU
  • A temporary fallback when hardware acceleration causes graphical problems

Switching the configuration manually

Edit /var/lib/waydroid/waydroid.cfg and add the following to [properties]:

[properties]
ro.hardware.gralloc=default
ro.hardware.egl=swiftshader

Apply the configuration and restart the container:

sudo waydroid upgrade --offline
sudo systemctl restart waydroid-container

waydroid upgrade overwrites part of base.prop waydroid upgrade --offline does not use the network; it only regenerates waydroid_base.prop and synchronizes the LXC configuration. If you manually edited waydroid_base.prop, the upgrade will overwrite those changes. Put custom properties in waydroid.cfg, under [properties], rather than editing base.prop directly.

Restore hardware acceleration:

# 从 waydroid.cfg 中移除 ro.hardware.gralloc 和 ro.hardware.egl
# 然后重新执行:
sudo waydroid upgrade --offline
# upgrade 会重新运行 getDriNode(),自动恢复 GBM + Mesa
sudo systemctl restart waydroid-container

Verify the current rendering path

# 在 Waydroid 内部检查当前使用的 EGL 实现
sudo waydroid shell dumpsys SurfaceFlinger | grep -i "GLES\|EGL\|render"

# 或者直接查看生成的属性
cat /var/lib/waydroid/waydroid_base.prop | grep -E "ro.hardware.gralloc|ro.hardware.egl"

Output of gbm + mesa indicates hardware acceleration; default + swiftshader indicates software rendering.


NVIDIA and hybrid-GPU compatibility options#

The underlying problem with NVIDIA’s proprietary driver

Modern NVIDIA drivers support GBM. Waydroid’s limitation is integration across the entire Android graphics stack and driver; the explanation that “NVIDIA does not support GBM” is no longer valid. Waydroid’s hardware-acceleration stack is built entirely on GBM (GBM gralloc → Mesa EGL → dma-buf → Wayland), so getDriNode() has a hard-coded filter for NVIDIA’s proprietary driver.

This leaves NVIDIA users with three options:

Option Performance Complexity Use case
Pure software rendering (SwiftShader) Low Low Lightweight apps (WeChat, reading)
Nouveau open-source driver Medium Medium NVIDIA GTX 9/10 series + Mesa
NVK (Nouveau Vulkan) Medium High Turing+ GPUs + the latest kernel

NVIDIA and Wayland compositor alignment Using the same GPU often reduces cross-device dma-buf compatibility problems, but it is not a universal strict requirement. Android-rendered buffers are exported to the compositor as dma-bufs; cross-device sharing depends on drivers, formats, and modifiers, and may fail, require a copy, or work normally. This means that even if the compositor runs on an NVIDIA GPU, you can first test a supported integrated GPU or software rendering and assess the compositor’s actual behavior.

Software-rendering workflow

Force Waydroid to use SwiftShader and disable GBM:

1. Configure waydroid.cfg

[properties]
ro.hardware.gralloc=default
ro.hardware.egl=swiftshader

2. Service configuration and persistence boundaries

# 未在所核对版本确认 WAYDROID_DISABLE_GBM 是受支持接口;使用下方 properties 方案

Only the structural example of a systemd drop-in is retained below. There are no verified graphics environment variables, so there is no need to create an empty file solely for this:

sudo mkdir -p /etc/systemd/system/waydroid-container.service.d
sudo tee /etc/systemd/system/waydroid-container.service.d/nvidia-override.conf <<'EOF'
[Service]
# 不加入未核实环境变量;systemd drop-in 结构仅作示意
EOF
sudo systemctl daemon-reload

3. Apply and restart

sudo waydroid upgrade --offline
sudo systemctl restart waydroid-container

Note for NixOS users User-session variables and system-service environments have different configuration scopes on NixOS; system services do not automatically inherit environment.sessionVariables. Manage graphics properties through the actual Waydroid configuration. In configuration.nix:

# NixOS 中配置实际服务环境需使用 systemd.services 对应 serviceConfig.Environment

Manage the graphics backend through [properties] in waydroid.cfg#

Hybrid graphics: Intel iGPU + NVIDIA dGPU

This is the most common laptop configuration. The strategy is to make Waydroid use only the Intel iGPU:

Option A: waydroid-choose-gpu.sh

The community project Quackdoc/waydroid-scripts provides waydroid-choose-gpu.sh for interactively selecting the GPU Waydroid uses:

git clone https://github.com/Quackdoc/waydroid-scripts.git
cd waydroid-scripts
chmod +x waydroid-choose-gpu.sh
sudo ./waydroid-choose-gpu.sh

The script:

  1. Lists all GPUs on the system by scanning with lspci + /dev/dri
  2. Prompts the user to select the target GPU
  3. Modifies the LXC container’s config_nodes to bind only the selected render node
  4. Restarts the container to apply the configuration

Option B: specify drm_device manually

Specify the Intel GPU’s render node directly in /var/lib/waydroid/waydroid.cfg:

[waydroid]
drm_device = /dev/dri/renderD128

First confirm which render node corresponds to Intel:

for d in /dev/dri/renderD*; do
    dev=$(basename "$d")
    driver=$(cat "/sys/class/drm/$dev/device/uevent" 2>/dev/null | grep DRIVER | cut -d= -f2)
    echo "$d → $driver"
done

AMD + AMD hybrid graphics

AMD APU + AMD dGPU systems, such as Ryzen + RX 6800M, are in a much better position than NVIDIA systems because both GPUs support GBM. However, there are known issues:

  • GitHub Issue #1454: on Ryzen 680M (APU) + RX 6800M (dGPU) systems, automatic detection may select the wrong GPU, causing Waydroid to enter a startup loop
  • Both GPUs also share the amdgpu kernel driver, and getDriNode()’s "return the first valid node" strategy may bind the container to a different GPU from the compositor

Solution: use drm_device to force selection of the same GPU as the compositor. AMD’s open-source Mesa drivers provide full GBM support for both GPUs; once the correct GPU is selected, functionality matches Intel’s.

Intel Arc discrete GPUs

Intel Arc (Alchemist/Battlemage) uses the i915 or xe kernel driver and is theoretically supported directly. However, the community currently reports unstable behavior: some Arc users experience graphical flickering or Vulkan initialization failures in Waydroid. The latest Mesa (24.1+) and a Linux 6.9+ kernel are recommended for the best support.

Debugging tools

# 检查 Waydroid 实际使用的 GPU
sudo waydroid shell ls -la /dev/dri/ # input/devices 不能证明实际渲染 GPU

# 检查容器内的 DRM 设备
sudo waydroid shell ls -la /dev/dri/

# 查看 EGL/Vulkan 运行时状态
waydroid logcat | grep -E "gralloc|egl|vulkan|GPU"

Resources#


Verification sources#

Waydroid official installation instructions.

Waydroid official command documentation.

Waydroid upstream source.

GPU selection and Vulkan mapping.

Waydroid software-rendering FAQ.

References & further reading

  1. Waydroid Official Installation Instructions ↗
  2. Waydroid Official Command Documentation ↗
  3. Waydroid Upstream Source ↗
  4. GPU Selection and Vulkan Mapping ↗
  5. Waydroid Software-rendering FAQ ↗
  6. NVIDIA Official GBM Documentation ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

Waydroid

  1. 01Waydroid architecture: LXC, Binder, and session bridging
  2. 02Waydroid installation and basic setup
  3. 03Waydroid integration on NixOS
  4. 04Waydroid GPU detection and hardware acceleration
  5. 05Waydroid bridges, NAT, and security boundaries
  6. 06Waydroid properties, multi-window mode, and ARM translation
  7. 07Waydroid troubleshooting and FAQ
  8. 08Waydroid tips: windows, translation, and shared directories
CONTINUE READING / NEXT ARTICLEWaydroid bridges, NAT, and security boundaries

Troubleshoot container networking step by step through veth, bridges, DHCP, DNS, and firewalls.