diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-29 12:12:35 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-29 12:12:35 +0100 |
| commit | 4d1c5acf5748b9a9b9b40cfb6ee1118593bacecd (patch) | |
| tree | 2f1e9d8b41100fe500ee0458248f99d882339e08 /AGENTS.md | |
| parent | 0a7db3b48cce5f064c20f280e345a8bf1feb5ecb (diff) | |
| download | quickshell-4d1c5acf5748b9a9b9b40cfb6ee1118593bacecd.tar.gz quickshell-4d1c5acf5748b9a9b9b40cfb6ee1118593bacecd.zip | |
sound: drop the OSD's canvas grain, it broke Qt's garbage collector
The desktop shell's recurring 100% CPU spin is a Qt bug set off by the
volume OSD. Its grain Canvas called createImageData on every paint, and
Qt 6.11 subtracts that pixel buffer from the V4 unmanaged-heap counter
without having added it. The first GC after an OSD show wrapped the
size_t counter, and from then on every JS allocation ran a full GC until
a restart. The OSD shows on volume keys, track changes and resume, which
is why the spin followed all three.
Found by running each always-on unit of the desktop in its own qs with
GC stats: only the sound module wrapped, 3 s after a resume, and a
volume nudge reproduced it on demand. Qt's qml tool with a bare Canvas
confirms it is createImageData alone: 17,856 of 17,856 GCs wrapped, the
same canvas drawing with fillRect 0 of 5. With the grain removed, 8 OSD
shows under forced GCs left the counter intact.
The grain was alpha 14/255, so the OSD looks nearly the same. vram.sh's
comment blamed the fdinfo parse for the underflow; corrected, and the
lesson is in AGENTS.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'AGENTS.md')
| -rw-r--r-- | AGENTS.md | 14 |
1 files changed, 14 insertions, 0 deletions
@@ -402,6 +402,20 @@ changing that component. The ones that generalise: is not a function`, which shows only in `qs log`; a Repeater bound to it simply builds nothing. Use `reduce((a, x) => a.concat(...), [])`. Seen with the assistant page's cloud Prefer rows (2026-10-23). +- **Canvas `createImageData` breaks Qt's garbage collector for good.** Qt + 6.11 subtracts the pixel buffer from the V4 engine's unmanaged-heap counter + without ever having added it, so the first GC after one is created wraps + the `size_t` counter below zero, and from then on every JS allocation runs + a full GC: a core pinned, keybinds and the drawer lagging, until a + restart. Reproduced in Qt's own `qml` tool with nothing but a `Canvas` + (17,856 of 17,856 GCs wrapped; the same canvas drawing with `fillRect`, 0 + of 5). The volume OSD's grain did it on every show, which is why the + desktop shell spun after volume keys, track changes and resumes alike. + `QV4_GC_TIMELIMIT=0`, set earlier for a misdiagnosed "stuck incremental + GC", does not help. Never use `createImageData`, `getImageData` or + `putImageData`. Found by running each always-on unit in its own `qs` with + `--log-rules qt.qml.gc.allocatorStats.debug=true` and reading the + unmanaged counter after each GC. - **A `Socket` whose connect failed never retries.** Quickshell 0.3.1 keeps the failed `QLocalSocket`, so setting `connected = true` again (or toggling it, or changing `path`) is a no-op: `setConnected` only connects when no |
