Proof Portal

Project overview

Omarchy

ProbeLabs65 findings · 95 requirements

A 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.

Inbox
ReportR-1725GitHub issuePublicDecided· Out of scope

Shell animations run at ~60 fps regardless of monitor refresh rate on NVIDIA (Qt falls back to the basic render loop)

Reported by PavelAlennikov · filed Aug 17, 2026 · open at source

Decided: Out of scope — decided by agents · verified 4 days ago.

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?
AreaOutside the audited area· Decided by agents · verified
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?
Decided by agents and verified by a second, independent agent — see the decision.

Original report

Shell animations run at ~60 fps regardless of monitor refresh rate on NVIDIA (Qt falls back to the basic render loop)revision 1 · captured 2026-09-28 21:29 UTC via poll

System

  • Omarchy 4.0.0, Hyprland 0.56.2, quickshell-git 0.3.0.r20
  • Qt 6.11.1 (qt6-declarative, qt6-wayland)
  • CPU: AMD Ryzen 9 7950X3D, GPU: NVIDIA RTX 4090 (nvidia-open-dkms 610.57.04)
  • Monitor: 4K @ 240 Hz (DP-1)

What happens

Every animation the shell draws — the tray drawer slide, menu and panel transitions, OSDs — runs at ~60 fps no matter the monitor refresh rate. On a high-refresh monitor this reads as visibly choppy, "low fps" motion, while Hyprland itself (window animations, cursor) is perfectly smooth at 240 Hz.

Measured from inside the shell with a FrameAnimation counter: the bar surface renders at 62 fps on a 240 Hz monitor.

Why (root cause)

Qt disables threaded OpenGL rendering on the proprietary NVIDIA driver. In qtbase src/plugins/platforms/wayland/.../qwaylandeglclientbufferintegration.cpp:

// On desktop NVIDIA resizing QtQuick freezes them when using threaded rendering QTBUG-95817
// In order to support threaded rendering on embedded platforms where resizing is not needed
// we check if XDG_CURRENT_DESKTOP is set which desktop environments should set
if (qstrcmp(vendor, "NVIDIA") == 0 && qEnvironmentVariableIsSet("XDG_CURRENT_DESKTOP")) {
    m_supportsThreading = false;
}

With ThreadedOpenGL reported as unsupported, Qt Quick picks the basic render loop, whose animations are driven by a ~16 ms timer with no vsync awareness — hence a hard 60 fps ceiling for the whole shell. Verified with QSG_INFO=1: basic render loop on NVIDIA; Mesa systems (radeonsi hardware and even llvmpipe) report threaded render loop and are unaffected, which is why this only bites NVIDIA users.

Reproduction

On any NVIDIA + Wayland machine:

mkdir -p /tmp/qs-loop-test && cat > /tmp/qs-loop-test/shell.qml <<'EOF'
import Quickshell
import QtQuick
PanelWindow {
  anchors { top: true; left: true }
  implicitWidth: 1
  implicitHeight: 1
}
EOF
env -u QSG_RENDER_LOOP -u QSG_RHI_BACKEND QSG_INFO=1 \
  QT_LOGGING_RULES="qt.scenegraph.general=true;qt.rhi.general=true" \
  timeout 4 quickshell -p /tmp/qs-loop-test > /tmp/qs-loop.log 2>&1
grep -iE "render loop|VENDOR" /tmp/qs-loop.log

NVIDIA output: basic render loop + OpenGL ES 3.2 NVIDIA. Mesa output: threaded render loop.

Independently reproduced on a second NVIDIA machine (RTX 5080, driver 610.57.04): same basic render loop. Two Mesa machines (RX 9070 XT radeonsi hardware; llvmpipe software) both report threaded render loop.

Evidence

https://github.com/user-attachments/assets/6bfbf4fd-b9f0-41d1-baaf-4e936742a0a3

ball_comparison.mp4 — a minimal bar-widget with a bouncing ball and a live FrameAnimation fps counter, recorded at 240 fps, stock shell vs QSG_RHI_BACKEND=vulkan: the counter reads 62 vs 240 on the same machine.

https://github.com/user-attachments/assets/3c5776d6-2a0e-4141-8b9a-a1cd81619945

tray_comparison.mp4 — the same difference on the stock tray drawer animation.

Per-frame analysis of the recordings (mean-absolute-difference between consecutive frames, 0.25 s sliding window): stock peaks at 64 frame changes/s, Vulkan at 240/s.

Proposed fix

Launch the shell with QSG_RHI_BACKEND=vulkan on NVIDIA systems (PR attached). Rationale for the Vulkan backend rather than forcing QSG_RENDER_LOOP=threaded on GL:

  • For the Vulkan backend the threaded loop is Qt's own default on every platform — no driver workaround gets overridden.
  • Forcing threaded on GL re-exposes the QTBUG-95817 resize freeze the vendor check exists to prevent, and an analogous forced-threaded setup is known to segfault on AMD iGPUs (quickshell#720).
  • The shell's QML is backend-agnostic (no raw GL anywhere), and quickshell maintains the Vulkan path of its native interop (e.g. quickshell#648).

Gated on three conditions, so it is a strict no-op everywhere the bug does not apply: the NVIDIA Vulkan ICD is installed, the GPU is Turing or newer (current driver family with a solid Wayland Vulkan WSI; legacy branches keep today's behavior), and the NVIDIA GPU is the primary display adapter — on hybrid laptops where the iGPU drives the display, clients get Mesa's EGL, the threaded loop is available anyway, and forcing Vulkan would only add cross-GPU risk for no benefit.

Tested on the machine above: 12+ hours including overnight idle/screensaver cycles, lock screen (ScreencopyView + dual-GPU dmabuf interop), and a full day of regular use. Shell animations run at the full 240 Hz.

Workaround for affected users (until fixed)

-- ~/.config/hypr/monitors.lua
hl.env("QSG_RHI_BACKEND", "vulkan")

then omarchy restart shell.

View raw text
## System

- Omarchy 4.0.0, Hyprland 0.56.2, quickshell-git 0.3.0.r20
- Qt 6.11.1 (qt6-declarative, qt6-wayland)
- CPU: AMD Ryzen 9 7950X3D, GPU: NVIDIA RTX 4090 (nvidia-open-dkms 610.57.04)
- Monitor: 4K @ 240 Hz (DP-1)

## What happens

Every animation the shell draws — the tray drawer slide, menu and panel transitions, OSDs — runs at ~60 fps no matter the monitor refresh rate. On a high-refresh monitor this reads as visibly choppy, "low fps" motion, while Hyprland itself (window animations, cursor) is perfectly smooth at 240 Hz.

Measured from inside the shell with a `FrameAnimation` counter: the bar surface renders at **62 fps** on a 240 Hz monitor.

## Why (root cause)

Qt disables threaded OpenGL rendering on the proprietary NVIDIA driver. In `qtbase src/plugins/platforms/wayland/.../qwaylandeglclientbufferintegration.cpp`:

```cpp
// On desktop NVIDIA resizing QtQuick freezes them when using threaded rendering QTBUG-95817
// In order to support threaded rendering on embedded platforms where resizing is not needed
// we check if XDG_CURRENT_DESKTOP is set which desktop environments should set
if (qstrcmp(vendor, "NVIDIA") == 0 && qEnvironmentVariableIsSet("XDG_CURRENT_DESKTOP")) {
    m_supportsThreading = false;
}
```

With `ThreadedOpenGL` reported as unsupported, Qt Quick picks the **basic render loop**, whose animations are driven by a ~16 ms timer with no vsync awareness — hence a hard 60 fps ceiling for the whole shell. Verified with `QSG_INFO=1`: `basic render loop` on NVIDIA; Mesa systems (radeonsi hardware and even llvmpipe) report `threaded render loop` and are unaffected, which is why this only bites NVIDIA users.

## Reproduction

On any NVIDIA + Wayland machine:

```bash
mkdir -p /tmp/qs-loop-test && cat > /tmp/qs-loop-test/shell.qml <<'EOF'
import Quickshell
import QtQuick
PanelWindow {
  anchors { top: true; left: true }
  implicitWidth: 1
  implicitHeight: 1
}
EOF
env -u QSG_RENDER_LOOP -u QSG_RHI_BACKEND QSG_INFO=1 \
  QT_LOGGING_RULES="qt.scenegraph.general=true;qt.rhi.general=true" \
  timeout 4 quickshell -p /tmp/qs-loop-test > /tmp/qs-loop.log 2>&1
grep -iE "render loop|VENDOR" /tmp/qs-loop.log
```

NVIDIA output: `basic render loop` + `OpenGL ES 3.2 NVIDIA`. Mesa output: `threaded render loop`.

Independently reproduced on a second NVIDIA machine (RTX 5080, driver 610.57.04): same `basic render loop`. Two Mesa machines (RX 9070 XT radeonsi hardware; llvmpipe software) both report `threaded render loop`.

## Evidence

https://github.com/user-attachments/assets/6bfbf4fd-b9f0-41d1-baaf-4e936742a0a3

`ball_comparison.mp4` — a minimal bar-widget with a bouncing ball and a live `FrameAnimation` fps counter, recorded at 240 fps, stock shell vs `QSG_RHI_BACKEND=vulkan`: the counter reads 62 vs 240 on the same machine.

https://github.com/user-attachments/assets/3c5776d6-2a0e-4141-8b9a-a1cd81619945

`tray_comparison.mp4` — the same difference on the stock tray drawer animation.

Per-frame analysis of the recordings (mean-absolute-difference between consecutive frames, 0.25 s sliding window): stock peaks at **64 frame changes/s**, Vulkan at **240/s**.

## Proposed fix

Launch the shell with `QSG_RHI_BACKEND=vulkan` on NVIDIA systems (PR attached). Rationale for the Vulkan backend rather than forcing `QSG_RENDER_LOOP=threaded` on GL:

- For the Vulkan backend the threaded loop is Qt's **own default** on every platform — no driver workaround gets overridden.
- Forcing threaded on GL re-exposes the [QTBUG-95817](https://bugreports.qt.io/browse/QTBUG-95817) resize freeze the vendor check exists to prevent, and an analogous forced-threaded setup is known to segfault on AMD iGPUs ([quickshell#720](https://github.com/quickshell-mirror/quickshell/issues/720)).
- The shell's QML is backend-agnostic (no raw GL anywhere), and quickshell maintains the Vulkan path of its native interop (e.g. [quickshell#648](https://github.com/quickshell-mirror/quickshell/issues/648)).

Gated on three conditions, so it is a strict no-op everywhere the bug does not apply: the NVIDIA Vulkan ICD is installed, the GPU is Turing or newer (current driver family with a solid Wayland Vulkan WSI; legacy branches keep today's behavior), and the NVIDIA GPU is the primary display adapter — on hybrid laptops where the iGPU drives the display, clients get Mesa's EGL, the threaded loop is available anyway, and forcing Vulkan would only add cross-GPU risk for no benefit.

Tested on the machine above: 12+ hours including overnight idle/screensaver cycles, lock screen (ScreencopyView + dual-GPU dmabuf interop), and a full day of regular use. Shell animations run at the full 240 Hz.

## Workaround for affected users (until fixed)

```lua
-- ~/.config/hypr/monitors.lua
hl.env("QSG_RHI_BACKEND", "vulkan")
```

then `omarchy restart shell`.

  • 6bfbf4fd-b9f0-41d1-baaf-4e936742a0a3
  • 3c5776d6-2a0e-4141-8b9a-a1cd81619945

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

Not linked to any known issue, other report or change yet.

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.

  • Out of scopeDecided by agents · verified · 2026-10-09 04:35 UTC