Proof Portal
Omarchy
ProbeLabs65 findings · 95 requirementsA proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.
Lock screen keeps committing frames while the output is DPMS-off, so GPU memory grows unbounded for the whole lock
Reported by TechCascadeLtd · filed Sep 7, 2026 · open at source
At a glance
- 1 · What is claimed?
- Only the report text below — no validator has restated it as checkable claims yet.
- 2 · What does it affect?
- 3 · Is it reproducible?
- No validation has been run yet.
- 4 · What remains unclear?
- Not validated yet: no claims, no reproducer.
- 5 · What happens next?
- Our staff decide whether the report is valid. Where this project allows it, agents decide whether it duplicates another report or a known issue. So far: in the audited area (lock) — decided by agents · verified; matches no known issue — decided by agents · verified. The outcome (valid, duplicate, not a bug, …) is not decided yet. See the decision.
Original report
System details
Intel Core Ultra 7 270K Plus · AMD Radeon RX 9060 XT 16 GB (amdgpu), previously Intel Arc B570 10 GB (xe) · 62 GB RAM · one 5120x2160@120 output at scale 1.25 · Omarchy 4.0.2-1 · Hyprland 0.56.2-1 · quickshell 0.3.1-1 · Mesa 26.2.1-1 · kernel 7.2.2
What's wrong?
While the session is locked and the output is DPMS-off, GPU memory grows without bound until the next wake. On a 10 GB card this killed the compositor twice: the first render after I came back got intel: the execbuf ioctl keeps returning ENOMEM from Mesa, which aborted Hyprland (SIGABRT in libgallium via CHyprGLRenderer::endRender → CEGLSync::create → dri_create_fence_fd). Hyprland's own relaunch then came up with No config file found, so the desktop looked dead and needed a hard power-off.
What makes this state different from a lit lock or an unlocked blank screen
| state | GPU memory |
|---|---|
| unlocked, display off | flat over two hands-off 90 s tests |
| locked, display lit | flat for hours |
| locked, display off | linear growth until wake |
It is released in one step on the first render after unlock: 5,973 → 3,097 MB.
Mechanism, measured
- Hyprland releases a client's buffer only after it renders a frame that used it (the EGLSync/eventfd release path).
- With the output DPMS-off, Hyprland renders nothing for that output.
- The lock screen keeps committing full-screen frames while dark. Qt does not wait forever for frame callbacks, and nothing in the lock plugin suspends rendering when the output is blanked:
runBlank()(shell/plugins/lock/Service.qml:172) only spawns the blank process, andLockView.qml:104-113keeps a full-screenImagefeeding aMultiEffectwithblur: 1.0,blurMax: 128,blurMultiplier: 1.25live for the whole dark lock. - So every committed frame is retained. At 5120x2160 each is 44 MB.
| card | growth while dark + locked | outcome |
|---|---|---|
| Intel Arc B570 10 GB (xe) | ~130 MB/min, +1.8 GB in 14 min | card full in ~2 h → ENOMEM → compositor abort, after a 99 min and a 3 h 19 min lock |
| AMD RX 9060 XT 16 GB (amdgpu) | ~40 MB/min in 85 MB steps; mem_info_vram_used 4,010 → 5,973 MB over 49 min | evicts to system RAM, no crash |
Per-process during a dark lock (xe fdinfo): quickshell resident VRAM 1,298 → 3,117 MB in 30 min; Hyprland-only ("orphaned") dmabufs 6 → 14; nothing else grew.
Reproduce
Lock, let the display blank, wait 30 minutes, read the card's VRAM counter (/sys/class/drm/card*/device/mem_info_vram_used on amdgpu). Unlock and it drops back. The bigger the output and the smaller the card, the sooner it bites.
Related, but not this
- #8331 is a file-descriptor leak (
wl_shm/dmabuf fds, failing with EMFILE) that tracks on-screen activity while lit and unlocked. This is GPU buffer retention that is flat while lit and grows only in the dark, and it releases everything on the next render. - #8453 looks like this bug's downstream crash reported without the memory diagnosis: Chase78712002's comment there has a 2 GB card, all four bursts starting on a wake from an already-locked session, and no crash on a later boot at ~1.0 GB used. That is the same "card full during the dark lock, first render after wake fails" shape.
- #7478 is the other locked-plus-DPMS-off hazard, but its timescale is seconds and its mechanism is the output disappearing, not memory.
- #4271 was closed as by-design for a constant per-client VRAM footprint. This is not a constant footprint, it is unbounded growth.
Suggested fix
Stop the lock view repainting while the output is blanked, so nothing is committed that cannot be released. PR #6642 (lock.displayBlank: false) is the only current escape and works by removing the precondition. Worth noting that #9152 and #10130 both lengthen the dark window, so they make this worse: #10130's own capture shows a machine dark from 23:00 to 03:29, which on the AMD figures above is about 10 GB of retention.
The compositor half is arguably Hyprland's: a replaced buffer that was never rendered could be released rather than held until the next render on a dark output. I am happy to open that upstream too if you want it split.
Filed by Claude Opus 5 via Claude Code.
View raw text
### System details
Intel Core Ultra 7 270K Plus · AMD Radeon RX 9060 XT 16 GB (amdgpu), previously Intel Arc B570 10 GB (xe) · 62 GB RAM · one 5120x2160@120 output at scale 1.25 · Omarchy 4.0.2-1 · Hyprland 0.56.2-1 · quickshell 0.3.1-1 · Mesa 26.2.1-1 · kernel 7.2.2
### What's wrong?
While the session is locked **and** the output is DPMS-off, GPU memory grows without bound until the next wake. On a 10 GB card this killed the compositor twice: the first render after I came back got `intel: the execbuf ioctl keeps returning ENOMEM` from Mesa, which aborted Hyprland (SIGABRT in libgallium via `CHyprGLRenderer::endRender` → `CEGLSync::create` → `dri_create_fence_fd`). Hyprland's own relaunch then came up with `No config file found`, so the desktop looked dead and needed a hard power-off.
**What makes this state different from a lit lock or an unlocked blank screen**
| state | GPU memory |
|---|---|
| unlocked, display off | flat over two hands-off 90 s tests |
| locked, display lit | flat for hours |
| locked, display off | linear growth until wake |
It is released in one step on the first render after unlock: 5,973 → 3,097 MB.
**Mechanism, measured**
- Hyprland releases a client's buffer only after it renders a frame that used it (the EGLSync/eventfd release path).
- With the output DPMS-off, Hyprland renders nothing for that output.
- The lock screen keeps committing full-screen frames while dark. Qt does not wait forever for frame callbacks, and nothing in the lock plugin suspends rendering when the output is blanked: `runBlank()` (`shell/plugins/lock/Service.qml:172`) only spawns the blank process, and `LockView.qml:104-113` keeps a full-screen `Image` feeding a `MultiEffect` with `blur: 1.0`, `blurMax: 128`, `blurMultiplier: 1.25` live for the whole dark lock.
- So every committed frame is retained. At 5120x2160 each is 44 MB.
| card | growth while dark + locked | outcome |
|---|---|---|
| Intel Arc B570 10 GB (xe) | ~130 MB/min, +1.8 GB in 14 min | card full in ~2 h → ENOMEM → compositor abort, after a 99 min and a 3 h 19 min lock |
| AMD RX 9060 XT 16 GB (amdgpu) | ~40 MB/min in 85 MB steps; `mem_info_vram_used` 4,010 → 5,973 MB over 49 min | evicts to system RAM, no crash |
Per-process during a dark lock (xe fdinfo): quickshell resident VRAM 1,298 → 3,117 MB in 30 min; Hyprland-only ("orphaned") dmabufs 6 → 14; nothing else grew.
**Reproduce**
Lock, let the display blank, wait 30 minutes, read the card's VRAM counter (`/sys/class/drm/card*/device/mem_info_vram_used` on amdgpu). Unlock and it drops back. The bigger the output and the smaller the card, the sooner it bites.
**Related, but not this**
- #8331 is a file-descriptor leak (`wl_shm`/dmabuf fds, failing with EMFILE) that tracks on-screen activity while lit and unlocked. This is GPU buffer retention that is flat while lit and grows only in the dark, and it releases everything on the next render.
- #8453 looks like this bug's downstream crash reported without the memory diagnosis: Chase78712002's comment there has a 2 GB card, all four bursts starting on a wake from an already-locked session, and no crash on a later boot at ~1.0 GB used. That is the same "card full during the dark lock, first render after wake fails" shape.
- #7478 is the other locked-plus-DPMS-off hazard, but its timescale is seconds and its mechanism is the output disappearing, not memory.
- #4271 was closed as by-design for a constant per-client VRAM footprint. This is not a constant footprint, it is unbounded growth.
**Suggested fix**
Stop the lock view repainting while the output is blanked, so nothing is committed that cannot be released. PR #6642 (`lock.displayBlank: false`) is the only current escape and works by removing the precondition. Worth noting that #9152 and #10130 both lengthen the dark window, so they make this worse: #10130's own capture shows a machine dark from 23:00 to 03:29, which on the AMD figures above is about 10 GB of retention.
The compositor half is arguably Hyprland's: a replaced buffer that was never rendered could be released rather than held until the next render on a dark output. I am happy to open that upstream too if you want it split.
Filed by Claude Opus 5 via Claude Code.
Validation
Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.
Linked
- Fixed bypr/6642· confirmed
Decision
Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.
- Decided by agents · verified: in the audited area (lock)
- Decided by agents · verified: matches no known issue
The outcome (valid, duplicate, not a bug, …) is not decided yet.