Using a GPU

No GPU is needed. When the machine has one, every session draws its desktop on it and encodes the H.264 video with the chip's own encoder, which leaves the CPU to the programs, and Chromium gets WebGL. If anything about the GPU fails, the session carries on in software.

What it changes

  • The desktop (sway, the shell, the applications' windows) is drawn on the GPU through a render node in /dev/dri. OpenGL and WebGL applications run on it; without a GPU they run on the CPU, and a WebGL page in the browser does not animate (see below).
  • The video is converted and encoded on the chip (VAAPI on Intel and AMD, NVENC on NVIDIA). On Intel the capture is also zero-copy: the compositor draws into GPU buffers and the GPU's video processor converts them, so the image never passes through the CPU.
  • Without a GPU OpenGL and Vulkan applications still open, drawn by Mesa's software drivers on the CPU (glxgears, vkcube and Zed opened on a server with no render node). The browser is the exception: Waydesk starts Chromium with --disable-gpu there, which saves memory but leaves WebGL out, so a WebGL page does not animate. --use-angle=swiftshader --enable-unsafe-swiftshader brings it back at about 8 fps for a full-screen page, using three of four cores. A server that has a render node while WAYDESK_GPU=off is a different case: vkcube failed there.
  • Nothing to configure in the usual case: the package gives the accounts of waydesk-users the render node with a udev rule, and WAYDESK_GPU=auto picks the first one.

Which GPUs

GPUstatewhat was done
Intel (HD Graphics 630)testedfull sessions, up to 21 active heavy users, VAAPI encoder, zero-copy capture
Intel (UHD Graphics 770)short testthe compositor on it with both renderers; its encoder in ffmpeg (446 fps at 1680x1050)
NVIDIA (RTX 4000 SFF Ada)tested, first versionsway on the card, NVENC encoder, looked at in the Windows App and in mstsc; needs NVIDIA's own driver 615 or newer
AMDnot triedshould work through VAAPI (mesa-va-drivers)

Set it up

Intel needs the media driver, which the package recommends: intel-media-va-driver-non-free (or intel-media-va-driver). AMD needs mesa-va-drivers. Then check that the machine has a render node and that sessions use it:

$ ls /dev/dri
by-path  card0  renderD128
$ waydesk-admin status

On a dedicated server from an installer that adds nomodeset to the kernel line or blacklists the graphics driver (Hetzner's installimage does the first unless USE_KERNEL_MODE_SETTING yes is set), /dev/dri does not appear until that is undone.

NVIDIA. Debian's own driver (550) lets sway start on the card and then gives it no buffers, so a session cannot be made there (it falls back to software, see below). NVIDIA's driver 615 does. These are the steps that worked on Debian 13:

$ curl -fsSLO https://developer.download.nvidia.com/compute/cuda/repos/debian13/x86_64/cuda-keyring_1.1-1_all.deb
$ sudo dpkg -i cuda-keyring_1.1-1_all.deb       # NVIDIA's apt repository and its signing key
$ sudo apt purge 'nvidia-*' 'libnvidia-*'        # Debian's 550 packages, if they are there
$ sudo apt update && sudo apt install nvidia-open
$ echo "options nvidia-drm modeset=1 fbdev=1" | sudo tee /etc/modprobe.d/nvidia-drm-modeset.conf
$ sudo update-initramfs -u && sudo reboot
$ nvidia-smi                                    # the card and the driver version

That adds a third-party apt repository and its key to the machine; read what you are trusting first. NVENC itself comes with the driver (libnvidia-encode); Waydesk loads it at run time, only when a session needs an encoder, so a machine without NVIDIA's driver is not affected.

See what runs

waydesk-admin status lists the GPUs and says, for every session, whether it renders on a GPU, which encoder it runs and which desktop it is. Four sessions on an Intel machine, one on each desktop, as the command printed them: all drawn on the card (renderD128) and encoded by VAAPI. The card has the role both (WAYDESK_GPU_ROLES=renderD128=both), WAYDESK_AI_ENDPOINT=127.0.0.1:11434 names a small model (qwen2.5:0.5b) in Ollama on the CPU, so the AI line and column show (no account had a request open). The Intel card cannot hold a model: the line says so. The System line says Omarchy because Omarchy's package replaces /etc/os-release. Not edited:

$ waydesk-admin status
Memory    62.6 GB in all, 54.6 GB available
Swap      none
Load      1.00 1.41 1.45 (8 cores)
System    Omarchy, kernel 7.2.11-arch1-1
GPU       Intel Kaby Lake-S GT2 [HD Graphics 630] (i915, renderD128): 2 % busy
            sessions and inference; 4 sessions: alice, bob, carol, dave
          new sessions use: auto
AI        127.0.0.1:11434: answering, no model loaded; 0 connections open
Desktop   sway for new sessions (WAYDESK_COMPOSITOR: sway, or hyprland, which needs a GPU)
Sessions  4 running; a new one would be accepted (needs 9.4 GB available)

ACCOUNT            DESKTOP   SESSION     MEMORY    SWAP    CPU    LIMIT RENDER     ENCODER                      AI   PROGRAMS (resident)
alice              sway      running     230 MB       -     4%  31.3 GB renderD128 vaapi@renderD128 q18 2fps    -    sway 273 MB, waydesk-server 172 MB, foot 32 MB
bob                plasma    running     660 MB       -     3%  31.3 GB renderD128 vaapi@renderD128             -    Xwayland 506 MB, plasmashell 473 MB, kwin_wayland 409 MB
carol              cosmic    running     920 MB       -     3%  31.3 GB renderD128 vaapi@renderD128             -    Xwayland 508 MB, cosmic-app-libr 313 MB, cosmic-launcher 313 MB
dave               hyprland  running     866 MB       -     6%  31.3 GB renderD128 vaapi@renderD128             -    quickshell 567 MB, Hyprland 363 MB, Xwayland 253 MB

An NVIDIA machine (the values of two readings a minute apart, put together; the encoder engine is shown separately, and the Intel card of the same machine is idle):

$ waydesk-admin status
GPU       Intel AlderLake-S GT1 (i915, renderD128): 0 % busy
          NVIDIA RTX 4000 SFF Ada Generation (nvidia, renderD129): 8 % busy, encoder 3 %, 786 MB of 20 GB, 1560 MHz, 26 W
          new sessions use: /dev/dri/renderD129

ACCOUNT            SESSION     MEMORY    SWAP    CPU    LIMIT RENDER ENCODER            PROGRAMS (resident)
lt0                running     657 MB       -     48%  31.3 GB gpu    nvenc q40 20fps    chromium 1121 MB, qs 356 MB, waydesk-server 231 MB

The NVIDIA reading is an older capture (before the DESKTOP column and the per-card lines): the current status also prints, under each card, its role and the sessions on it, and, for a server with roles and a local model, an Inference line, an AI line and an AI column with the connections each account has open (see Settings and the recipe for a shared model). The same layout, with the values of an example (the card kept for the model, two sessions on the Intel card, one of them using the model), as the format the command prints now:

$ waydesk-admin status
GPU       Intel AlderLake-S GT1 (i915, renderD128): 12 % busy
            sessions only; 2 sessions: lt1, lt2
          NVIDIA RTX 4000 SFF Ada Generation (nvidia, renderD129): 98 % busy, 7.1 GB of 20 GB
            inference only (new sessions do not pick it); no sessions; for a model: CUDA_VISIBLE_DEVICES=GPU-135bf55a-cdf3-d072-c499-bdc16464ecca
          new sessions use: auto
Inference CUDA_VISIBLE_DEVICES=GPU-135bf55a-cdf3-d072-c499-bdc16464ecca (renderD129)
AI        http://127.0.0.1:11434: llama3.1:8b-instruct-q4_K_M 6.7 GB in video memory; 1 connection open

ACCOUNT            SESSION     MEMORY    SWAP    CPU    LIMIT RENDER     ENCODER                      AI  DESKTOP    PROGRAMS (resident)
lt1                running     860 MB       -     29%  31.3 GB renderD128 vaapi@renderD128 q40 20fps  1   sway       chromium 1066 MB, qs 329 MB
lt2                running     736 MB       -     10%  31.3 GB renderD128 vaapi@renderD128 q40 20fps  -   sway       code 1109 MB, qs 399 MB

How busy a card is comes from NVIDIA's management library for NVIDIA, from gpu_busy_percent for AMD, and, on Intel, from the share of time the card is out of its idle state (not the split between its render and video engines). --watch keeps it on the screen.

Settings

All of these go in /etc/default/waydesk and take effect for sessions that start afterwards (end the session or log out first).

settingmeaning
WAYDESK_GPU=offsoftware rendering, as without a GPU. Another value: the render node to use, for example /dev/dri/renderD129
WAYDESK_GPU_ROLES=renderD128=render,renderD129=inferencewhat each card is for, by render node: render (sessions only), inference (a local model only: auto never picks it and no other card's session encodes on it; an account pinned there by hand still can) or both. Cards not listed are render. Set it with sudo waydesk-admin gpu roles renderD129=inference (only the named cards change; gpu roles alone shows the cards and their roles; gpu roles reset removes the setting). waydesk-admin status then prints the CUDA_VISIBLE_DEVICES to give the model service. A role starts nothing and moves nothing: you point the model service at its card yourself, and inference on an Intel or AMD card only keeps sessions off it
WAYDESK_ENCODER=softwarekeep the GPU for drawing but encode on the CPU. auto (the default: VAAPI, then NVENC), vaapi and nvenc are the others
WAYDESK_NVENC_DEVICE=1the NVIDIA card (CUDA number) that encodes; by default the card the session draws on, else the first
WAYDESK_DMABUF=0go back to copying the image through shared memory instead of the zero-copy capture
WAYDESK_VA_MIN_QP, WAYDESK_VA_MAX_QPthe two ends of the quantiser (default 18 and 40): the quality wanted, and what a slow link may cost

The GPU encoders run at a constant quantiser and move it as the link allows, one step at a time. A step is only a number in the next frame's header: no keyframe and no new parameter sets, which matters because a client can show a washed-out image when it meets several in a row (a first version did, before this release).

When it fails

  • A session that cannot be made on the GPU is made again in software, once, and the log says so (the session could not be made on the GPU (...): software rendering). Before this, Debian's NVIDIA driver left the account without a desktop.
  • An encoder that cannot start falls back to OpenH264 on the CPU: VAAPI encoder not available (...): OpenH264 on the CPU. The session keeps working; the worker then takes about 12 % of the machine instead of 3 % for one full-screen WebGL page.
  • GeForce cards limit simultaneous NVENC sessions (8 on recent drivers); past the limit the new session encodes with OpenH264. Workstation and datacenter cards have no such cap. Not measured here.
  • Both fallbacks show in status: the RENDER column says cpu, the ENCODER column openh264.

What it measured

On a Core i7-7700 with its integrated HD 630, 64 GB, users added one at a time, each active (browsing, typing in a terminal, Calc on 300,000 rows, VS Code, GIMP, a video), with the RDP clients on another machine:

active heavy usersCPU, GPU onCPU, GPU offcapture and encoding, GPU on / off
1047 %45 %4.2 % / 8.7 %
1549 %60 %4.6 % / 11.7 %
2050 %69 %4.8 % / 15.4 %
24refused at 22 (memory)94 %– / 21.1 %
  • The GPU flattens the CPU, but on this machine it did not add users: 21 fit with it and 24 without, because an integrated GPU takes system memory (2.24 GB a user against 1.95 GB) and memory ended both runs.
  • Full-screen WebGL (the heaviest image) holds 60 fps for two users on the HD 630, 47 for three, 20 for six and 13 for ten: the card is the limit and the CPU stays under 40 %. With the GPU off the page does not animate.
  • NVIDIA: 61 fps on the same page, 4 to 9 ms per encoded frame, the card's encoder engine 4 % busy.

The method, every table and the scripts are in docs/MEASUREMENTS.md; the notes behind the NVIDIA work in docs/GPU-PLAN.md. Not measured: AMD, several users on one NVIDIA card, a card with its own memory under many users.

This page is a file in the repository: corrections are welcome.