You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On a single-GPU system whose DRM device exposes no render node (/dev/dri/card1 with
no renderD*), CDRMBackend::initMgpu() fails on every output commit and is never
cached as failed. Each failure emits five log lines, and the renderer it is trying to
build is one that shouldBlit() guarantees will never be used, because primary is
null on a single-GPU setup.
Over a ~21.5 h session this wrote 425,020,662 bytes (406 MiB) into $XDG_RUNTIME_DIR/hypr/<sig>/hyprland.log, which lives on a 1.6 GB tmpfs — 26% of it
consumed by one repeating error.
Related work
This is the same code path and the same log flood as #294 (DisplayLink/evdi), which
is still open. #311 reported the evdi hotplug failure from the same failing call,
and PR #310 fixed it by marking evdi backends as not needing a renderer — that is
the AQ_BACKEND_GPU_DRIVER_EVDI guard now sitting at DRM.cpp:898.
That fix is keyed on the driver name, so it covers evdi only. Any other KMS driver
with no render node hits the identical loop, and qxl is one of them. Filing separately
rather than commenting on #294 because #294 is specifically about DisplayLink and is
plausibly resolved for its reporter by #310, whereas the general case is not.
Environment
aquamarine
0.14.0 (Arch aquamarine 0.14.0-2)
Hyprland
0.56.2
mesa
26.2.1
libdrm
2.4.134
kernel
7.1.9-arch1-2
GPU
00:1e.0 Red Hat, Inc. QXL paravirtual graphic card [1b36:0100], driver qxl
DRM nodes
/dev/dri/card1 only — no renderD*
Host
QEMU/KVM guest (Q35 + ICH9), no GPU on the host either
qxl is a display-only KMS driver: it provides a card node and no render node, and
Mesa has no EGL device backed by it. Rendering falls back to llvmpipe, and the desktop
otherwise works fine — screenshots, screen capture and normal compositing all succeed.
Symptom
This exact five-line block repeats indefinitely:
ERR from aquamarine ]: [EGL] Command eglQueryDeviceStringEXT errored out with EGL_BAD_PARAMETER (0x12300): eglQueryDeviceStringEXT
ERR from aquamarine ]: [EGL] Command eglQueryDeviceStringEXT errored out with EGL_BAD_PARAMETER (0x12300): eglQueryDeviceStringEXT
ERR from aquamarine ]: CDRMRenderer(drm): Can't create renderer, no matching devices found
ERR from aquamarine ]: drm: initMgpu: no renderer
ERR from aquamarine ]: drm: Failed to update renderer state for Virtual-1 on applyCommit
DEBUG from aquamarine ]: Supported EGL client extensions: (16) EGL_EXT_device_base EGL_EXT_device_enumeration ...
Counting a 2 MB tail sample: 2090 iterations, i.e. ~1 KB of log per commit. The first
occurrence is at byte offset 10671 — it starts at compositor startup and never stops.
It is bursty rather than continuous: idle produces nothing, but every output state
change produces a burst (a re-attach of a screen-capture client produced 4.8 MB in a
few seconds).
Analysis
Renderer.cpp:236eglDeviceFromDRMFD() enumerates EGL devices and looks for one whose EGL_DRM_DEVICE_FILE_EXT matches the DRM device. With no render node, initMgpu()
passes the card fd (DRM.cpp:669, gpu->renderNodeFd >= 0 ? gpu->renderNodeFd : gpu->fd),
and the only EGL device present is the software one, which has no DRM device file — so eglQueryDeviceStringEXT returns EGL_BAD_PARAMETER and no device matches
(Renderer.cpp:588).
The failure is never remembered. DRM.cpp:683:
boolAquamarine::CDRMBackend::updateSecondaryRendererState() {
if (!backend->ready)
returntrue;
if (!primary) {
if (rendererState.renderer && rendererState.allocator)
returntrue;
returninitMgpu(); // <-- no negative caching
}
rendererState.renderer stays null, so every call retries from scratch. Since updateSecondaryRendererState() is called from applyCommit (DRM.cpp:1878) and on
connect (DRM.cpp:1817), this runs per commit for the lifetime of the session.
On a single-GPU system primary is null: in CDRMBackend::attempt() the first GPU is
registered while newPrimary is still unset and only then becomes newPrimary for
subsequent GPUs. So shouldBlit() is permanently false and the mgpu renderer is never
consulted — yet updateSecondaryRendererState() insists on constructing it on every
commit.
The guard already exists, but is keyed on driver name
initMgpu() opens with exactly the right escape hatch (DRM.cpp:652):
if (!rendererRequired)
returntrue;
and rendererRequired is cleared in exactly one place in the file (DRM.cpp:901):
if (driver == AQ_BACKEND_GPU_DRIVER_EVDI) {
// DisplayLink/evdi exposes KMS without a usable EGL renderer.
primary = {};
rendererRequired = false;
}
The comment describes this situation precisely — KMS without a usable EGL renderer —
but the condition tests the driver name rather than the condition, so qxl (and any
other render-node-less KMS driver) does not benefit.
There is no environment variable that disables this path; the full set in 0.14.0 is AQ_DRM_DEVICES, AQ_NO_MODIFIERS, AQ_NO_ATOMIC, AQ_MGPU_NO_EXPLICIT, AQ_FORCE_LINEAR_BLIT, AQ_LIBINPUT_NO_PLUGINS, AQ_TRACE, and none touches renderer
creation.
Suggested fix
Either of these would resolve it; the first is the smaller change:
Cache the failure. Add a rendererInitFailed flag set by initMgpu() on failure
and checked alongside rendererRequired, so the attempt happens once and is logged
once. A single startup error is genuinely useful; the same error per commit is not.
Generalise the evdi guard from Fix DisplayLink/evdi monitor hotplug #310. Clear rendererRequired whenever the GPU
has no render node and no matching EGL device, rather than special-casing one driver
name. This matches the intent already stated in the comment at DRM.cpp:899, and
would cover qxl, vmwgfx, and any future display-only KMS driver without needing a
new name in the enum each time.
Either way it may also be worth skipping the mgpu renderer entirely when shouldBlit() can never become true, since that is the only consumer.
Downstream note
On this machine the symptom that led me here was wayvnc 0.10.1 logging Output is now off. Pausing frame capture and never resuming, which left the VNC
server accepting TCP connections while sending no frames. That correlates in time with
these bursts, but I have not traced a causal path through aquamarine and am not
claiming one — I mention it only as the context in which the log flood was found. The
log flood itself is reproducible and self-evident.
Reproduction
Run Hyprland in a QEMU/KVM guest configured with a qxl video device (Unraid's
"VM console video driver: QXL", or -device qxl-vga). Confirm /dev/dri contains a card* node and no renderD*, then watch $XDG_RUNTIME_DIR/hypr/<sig>/hyprland.log
grow while moving windows or toggling outputs.
Summary
On a single-GPU system whose DRM device exposes no render node (
/dev/dri/card1withno
renderD*),CDRMBackend::initMgpu()fails on every output commit and is nevercached as failed. Each failure emits five log lines, and the renderer it is trying to
build is one that
shouldBlit()guarantees will never be used, becauseprimaryisnull on a single-GPU setup.
Over a ~21.5 h session this wrote 425,020,662 bytes (406 MiB) into
$XDG_RUNTIME_DIR/hypr/<sig>/hyprland.log, which lives on a 1.6 GB tmpfs — 26% of itconsumed by one repeating error.
Related work
This is the same code path and the same log flood as #294 (DisplayLink/evdi), which
is still open. #311 reported the evdi hotplug failure from the same failing call,
and PR #310 fixed it by marking evdi backends as not needing a renderer — that is
the
AQ_BACKEND_GPU_DRIVER_EVDIguard now sitting atDRM.cpp:898.That fix is keyed on the driver name, so it covers evdi only. Any other KMS driver
with no render node hits the identical loop, and
qxlis one of them. Filing separatelyrather than commenting on #294 because #294 is specifically about DisplayLink and is
plausibly resolved for its reporter by #310, whereas the general case is not.
Environment
aquamarine 0.14.0-2)00:1e.0 Red Hat, Inc. QXL paravirtual graphic card [1b36:0100], driverqxl/dev/dri/card1only — norenderD*qxlis a display-only KMS driver: it provides a card node and no render node, andMesa has no EGL device backed by it. Rendering falls back to llvmpipe, and the desktop
otherwise works fine — screenshots, screen capture and normal compositing all succeed.
Symptom
This exact five-line block repeats indefinitely:
Counting a 2 MB tail sample: 2090 iterations, i.e. ~1 KB of log per commit. The first
occurrence is at byte offset 10671 — it starts at compositor startup and never stops.
It is bursty rather than continuous: idle produces nothing, but every output state
change produces a burst (a re-attach of a screen-capture client produced 4.8 MB in a
few seconds).
Analysis
Renderer.cpp:236eglDeviceFromDRMFD()enumerates EGL devices and looks for one whoseEGL_DRM_DEVICE_FILE_EXTmatches the DRM device. With no render node,initMgpu()passes the card fd (
DRM.cpp:669,gpu->renderNodeFd >= 0 ? gpu->renderNodeFd : gpu->fd),and the only EGL device present is the software one, which has no DRM device file — so
eglQueryDeviceStringEXTreturnsEGL_BAD_PARAMETERand no device matches(
Renderer.cpp:588).The failure is never remembered.
DRM.cpp:683:rendererState.rendererstays null, so every call retries from scratch. SinceupdateSecondaryRendererState()is called fromapplyCommit(DRM.cpp:1878) and onconnect (
DRM.cpp:1817), this runs per commit for the lifetime of the session.The renderer is never used here
DRM.cpp:647:On a single-GPU system
primaryis null: inCDRMBackend::attempt()the first GPU isregistered while
newPrimaryis still unset and only then becomesnewPrimaryforsubsequent GPUs. So
shouldBlit()is permanently false and the mgpu renderer is neverconsulted — yet
updateSecondaryRendererState()insists on constructing it on everycommit.
The guard already exists, but is keyed on driver name
initMgpu()opens with exactly the right escape hatch (DRM.cpp:652):and
rendererRequiredis cleared in exactly one place in the file (DRM.cpp:901):The comment describes this situation precisely — KMS without a usable EGL renderer —
but the condition tests the driver name rather than the condition, so
qxl(and anyother render-node-less KMS driver) does not benefit.
There is no environment variable that disables this path; the full set in 0.14.0 is
AQ_DRM_DEVICES,AQ_NO_MODIFIERS,AQ_NO_ATOMIC,AQ_MGPU_NO_EXPLICIT,AQ_FORCE_LINEAR_BLIT,AQ_LIBINPUT_NO_PLUGINS,AQ_TRACE, and none touches renderercreation.
Suggested fix
Either of these would resolve it; the first is the smaller change:
Cache the failure. Add a
rendererInitFailedflag set byinitMgpu()on failureand checked alongside
rendererRequired, so the attempt happens once and is loggedonce. A single startup error is genuinely useful; the same error per commit is not.
Generalise the evdi guard from Fix DisplayLink/evdi monitor hotplug #310. Clear
rendererRequiredwhenever the GPUhas no render node and no matching EGL device, rather than special-casing one driver
name. This matches the intent already stated in the comment at
DRM.cpp:899, andwould cover qxl, vmwgfx, and any future display-only KMS driver without needing a
new name in the enum each time.
Either way it may also be worth skipping the mgpu renderer entirely when
shouldBlit()can never become true, since that is the only consumer.Downstream note
On this machine the symptom that led me here was
wayvnc0.10.1 loggingOutput is now off. Pausing frame captureand never resuming, which left the VNCserver accepting TCP connections while sending no frames. That correlates in time with
these bursts, but I have not traced a causal path through aquamarine and am not
claiming one — I mention it only as the context in which the log flood was found. The
log flood itself is reproducible and self-evident.
Reproduction
Run Hyprland in a QEMU/KVM guest configured with a
qxlvideo device (Unraid's"VM console video driver: QXL", or
-device qxl-vga). Confirm/dev/dricontains acard*node and norenderD*, then watch$XDG_RUNTIME_DIR/hypr/<sig>/hyprland.loggrow while moving windows or toggling outputs.