Backends and rendering
The backend flag chooses how scoot presents what it drew; the renderer flag chooses what draws it. They are independent axes — any renderer runs on any backend — but only some combinations buy anything. (Deciding which package to install is the install page’s one-command check; this page is what happens underneath.)
The three backends
Section titled “The three backends”| Backend | Presents via | Outputs | Scale |
|---|---|---|---|
--headless |
nowhere; scootctl screenshot reads the framebuffer |
N virtual outputs (--outputs, 1–8) |
from the config |
--nested |
a window in the host compositor | one window; follows the host’s size live | the host owns it (a set scale is ignored with a warning) |
--tty |
real DRM/KMS hardware on a console | every connected monitor, by connector name | from the config, per output |
Under --nested the session follows the host window’s size for life:
resize it and the desktop inside resizes with it; if a size cannot be
allocated scoot logs it, stays where it was, and the host letterboxes
the difference. Under --tty monitors plug and unplug live (below).
Which renderer draws the frames
Section titled “Which renderer draws the frames”--renderer pixman|gles (config: [renderer] backend) picks what
composites each frame. The default is pixman, the CPU renderer,
and that is not changing — running with no GPU at all is a hard
requirement, not a fallback tier. gles is opt-in:
- What it buys. Correctness parity with pixman on a second
renderer, and the groundwork for scanning a GPU buffer out directly
under
--tty. Every pixel-readback test passes byte-identically under either renderer. - What it does not buy (yet). No speed, except on the scanout
path below: the frame is composited into an offscreen buffer and
read back to main memory exactly as pixman’s is, so
glesadds a GPU round trip without removing any CPU copy — on a machine whose “GPU” is a software rasteriser it is several times slower than pixman. - Under
--tty,glesscans out from the GPU — in agpu-scanoutbuild. The frame is scanned out directly instead of being read back and memcpy’d into a dumb buffer. Without that feature--ttywarns and keeps pixman, because the read-back shape would be strictly worse than the CPU. With the feature, a device that cannot drive the scanout tier warns and falls back the same way — CPU renderer and dumb buffers — instead of refusing to start: on--ttyscoot is the session, so a refusal would be a lockout.--headlessis read-back in every build. - Under
--nested, agpu-scanoutbuild hands each frame to the host as a dma-buf — no CPU copy on scoot’s side, nothing for the host to upload — but only when it is safe (the host offers dma-buf feedback naming the same DRM device, a common format, and a startup test buffer allocates, renders and copies). Anything else keeps the read-back, and the startup log says which and why, once. - Hardware first. The EGL device is chosen by preferring a real
device over a software one and taking the first that yields a
working renderer. The chosen device is logged at startup (
the GLES renderer is up device=/dev/dri/renderD128 software=false) — trust that line over the flag name. - A wrong
--renderer glesis a startup error, not a silent downgrade — when EGL itself is missing or broken. If no EGL device can drive it scoot says so and names each failure rather than quietly compositing with the other renderer. There is no automatic fallback on that path: drop the flag (stay on pixman) or fix the cause. (The deliberate exception is--ttyGPU scanout above: there scoot warns and keeps the CPU renderer instead of refusing to start.)
Measured where it pays (Apple M2 under Asahi Linux): 4–5x less compositor CPU under damage than the default tier, ~0.2 W less power, 7–16 MB more RSS, no idle difference. A fullscreen window covering its output goes primary-direct (zero-copy), decided per frame; GPU clients get the driver’s whole import set (tiled and compressed layouts, multi-plane YUV included), where the CPU tier offers linear RGB only.
| Field | Type | Default | Reload | Meaning |
|---|---|---|---|---|
[renderer] backend |
"pixman" / "gles" |
"pixman" |
restart only | Which renderer composites each frame. A name this build knows but cannot build (gles with no working EGL) is a startup error; --renderer wins over the file either way. |
Which DRM device --tty drives
Section titled “Which DRM device --tty drives”Normally: whichever one works. scoot asks for the seat’s primary GPU,
and if that device cannot drive a display it tries every other DRM
device on the seat in turn. The log line worth grepping is drm: driving this device, with the device path on it (a rejected device
gets a drm: device unusable warning naming it and what it said).
If the automatic search picks wrong, name the device — the one that owns the connectors, never a render-only node:
scoot --tty --gpu /dev/dri/card0 -- foot[tty]gpu = "/dev/dri/card0"--gpu replaces the search entirely — exactly that device, no
fallback — so a wrong path is a clean startup error, not a silent
fallback. It means nothing outside --tty (ignored with a warning).
Prefer a stable /dev/dri/by-path/... alias over a cardN number.
| Field | Type | Default | Reload | Meaning |
|---|---|---|---|---|
[tty] gpu |
string (device path) | unset (automatic search) | restart only | Which DRM device --tty drives, when the search is wrong. Set means exactly that device: a wrong or empty path is a startup error naming the key. |
Symptom: every device refused, “seat takes one client at a time”. Another compositor already holds the seat — no choice of device gets around a busy seat. To see what the seat has:
ls /dev/dri/card*.
Hotplug, VT switching, captures
Section titled “Hotplug, VT switching, captures”Hotplug. --tty watches udev and re-runs the device choice
whenever the display moves: a plugged monitor gets an output of its
own, placed right of the others, without moving the session off
already-lit screens; pulling one adopts its workspaces elsewhere (and
a matching monitor’s return moves them back). With nothing connected
at all scoot holds the last frame and keeps running. One tier for the
whole session: the first monitor decides it — if the first falls back
to dumb buffers, every monitor uses dumb buffers, and a later monitor
that cannot join the GPU tier stays dark rather than mixing tiers.
VT switching. --tty binds Ctrl+Alt+F1…F12, layered on after
the config loads and always winning over a colliding file bind (with a
warning naming what they displaced) — on real hardware that is the one
recovery path, so it cannot silently lose to a typo. They keep working
under fullscreen grabs and the session lock, and a reload cannot strip
them.
Captures and the pointer
Section titled “Captures and the pointer”Screenshots and screen capture read the
same composited frame the outputs show — which is why scootctl screenshot works on every backend, including --headless with no
display at all. See Screenshots.