Skip to content

Stop the hybrid-GPU checks keeping the discrete GPU awake - #11038

Open
ReidenXerx wants to merge 2 commits into
omacom:quattrofrom
ReidenXerx:fix/hybrid-gpu-no-glx-pin-quattro
Open

Stop the hybrid-GPU checks keeping the discrete GPU awake#11038
ReidenXerx wants to merge 2 commits into
omacom:quattrofrom
ReidenXerx:fix/hybrid-gpu-no-glx-pin-quattro

Conversation

@ReidenXerx

Copy link
Copy Markdown

On a hybrid laptop the discrete GPU is meant to stay runtime-suspended until something asks for it. Two things in Omarchy stop that happening. They are separate bugs, but the second depends on the first being fixed, so they are here together as two commits.

Count GPUs from sysfs instead of lspci

omarchy-hw-hybrid-gpu counted GPUs with lspci | grep -cE 'VGA|3D|Display'. lspci reads PCI config space, and the kernel has to runtime-resume a suspended device to answer:

lspci
 => pci_read_config
 => pci_config_pm_runtime_get
 => __pm_runtime_resume        # the card powers up to serve the read
 => rpm_resume: 0000:01:00.0

So the detector woke the very card it was asking about, and the card then stayed powered for the driver's autosuspend tail (~18s on the machine below) before settling back to D3cold.

That matters because the Omarchy shell re-runs its menu-state probes, and this is one of them:

lspci
 <- omarchy-hw-hybrid-gpu
 <- bash -lc <menu state script>
 <- quickshell -n -p /usr/share/omarchy/shell
 <- omarchy-launch-shell
 <- Hyprland

Counting display-class devices from the cached sysfs IDs gives the same answer without touching config space. 0x03* covers VGA (0x0300), 3D (0x0302) and Display (0x0380) — the same set the lspci grep matched. omarchy-hw-nvidia and omarchy-hw-nvidia-gsp already avoid lspci for exactly this reason; this brings the third detector in line.

The test now expresses the GPU count as PCI device fixtures under OMARCHY_PCI_DEVICES_PATH instead of stubbing lspci, keeps an ethernet device in the fixture to prove non-display classes are not counted, and asserts the detector has no lspci call outside comments.

Don't pin GLX to NVIDIA on hybrid laptops

nvidia.lua sets __GLX_VENDOR_LIBRARY_NAME=nvidia whenever an NVIDIA card is present. On a hybrid laptop that points every OpenGL client at the discrete GPU, but the panel hangs off the integrated GPU — so the frames still have to be copied back to reach the display. The card wakes at login and never idles again: a constant battery cost for no visible gain.

This skips the pin when omarchy-hw-hybrid-gpu reports a hybrid system, leaving the Mesa default in place and letting the discrete card be opted into per application (prime-run, or __NV_PRIME_RENDER_OFFLOAD=1). Desktops where NVIDIA actually drives the display are not hybrid, so they are unaffected.

This is also why the ordering matters: the guard calls omarchy-hw-hybrid-gpu during Hyprland's config load, which is only safe once that detector has stopped resuming the GPU.

Testing

  • test/shell.d/hw-hybrid-gpu-test.sh passes (10 assertions, including two new ones), at each commit independently.
  • ./test/shell shows no new failures. Four files fail on this machine, but they fail identically on unmodified quattro: config, snapper and unowned-system-paths need an omarchy-pkgs checkout that is not present, and locate is unrelated (see below).
  • Verified on an Alienware x16 R2 (Intel Core Ultra 7 155H + RTX 4070, nvidia 580.x, RTD3). With both changes the discrete GPU reaches and stays at D3cold while idle instead of being woken by the shell's periodic probes.

Unrelated, noticed while testing

test/shell.d/locate-test.sh does rglob("*") over bin/, install/ and migrations/ and calls read_text() on every file, so it dies with a UnicodeDecodeError if any binary file is present in those trees. A stray bin/__pycache__/*.pyc (left by running bin/omarchy-agent-usage-*) is enough to trigger it. Not touched here since it is a separate concern — happy to send a follow-up if useful.

lspci reads PCI config space, and the kernel must runtime-resume a suspended
device to answer that. On a hybrid laptop the discrete GPU this detector is
asking about is usually asleep, so the check woke the very card it was
measuring, then held it awake for the driver's autosuspend tail.

Count display-class devices from the cached sysfs IDs instead. 0x03* covers VGA
(0x0300), 3D (0x0302) and Display (0x0380), matching what the lspci grep
counted. omarchy-hw-nvidia and omarchy-hw-nvidia-gsp already avoid lspci for
this reason.

The test expresses the GPU count as PCI device fixtures under
OMARCHY_PCI_DEVICES_PATH rather than stubbing lspci, and asserts the detector
contains no lspci call outside comments.
Setting __GLX_VENDOR_LIBRARY_NAME=nvidia makes every OpenGL client render on
the discrete GPU. On a hybrid laptop the panel hangs off the integrated GPU, so
those frames still have to be copied back to reach the display: the card wakes
at login and never idles again, costing battery for no visible gain.

Skip the pin when omarchy-hw-hybrid-gpu reports a hybrid system, leaving the
Mesa default in place and letting the discrete card be opted into per
application via prime-run or __NV_PRIME_RENDER_OFFLOAD=1. Desktops where NVIDIA
actually drives the display are not hybrid, so they are unaffected.
@Hazakins

Copy link
Copy Markdown

A datapoint from an AMD+NVIDIA machine in favour of the sysfs count, plus the reason the lspci fallback is the common path even with supergfxctl installed.

Hardware: Framework Laptop 16, Ryzen AI 9 HX 370 / Radeon 890M (amdgpu, drives every output) + RTX 5070 graphics module (nvidia 610.57.04, no connector in use). Omarchy 4.0.3-1, Hyprland 0.56.2-2, kernel 7.2.3-arch1-3, supergfxctl present. I have not built this PR; the measurements below are of the shipped script and of the sysfs reads it changes.

Why the fallback runs at all. The gate bounds supergfxctl -s to 1 s (timeout --kill-after=1s 1s). At shell start on this box the daemon misses that under load, exit 124, and the script drops into multiple_gpus. An strace -f of one omarchy restart shell showed three lspci runs among the shell's children (the menu when: gate and the dictation indicator between them), and the card left D3cold on each start.

Which read does it, measured. From power/runtime_status = suspended, I read the dGPU's sysfs attributes one at a time: vendor, device, class, revision, subsystem_*, modalias, uevent all leave it suspended. config resumes it, and the parent root port's config (0000:00:03.1 here) powers up the whole slot. lspci reads both. So the class read in this PR is safe by measurement, not only by reasoning.

One more waker in the same shell start, outside this PR: the bar's dictation indicator runs voxtype status --follow --extended, whose inventory step execs nvidia-smi and lspci (reporting that to voxtype separately). Mentioning it so a "still wakes on a hybrid box after this patch" report is not pinned on the detector.


Disclosure: this diagnosis and write-up were produced by Claude (Fable 5.1, via Claude Code) working on my laptop; the measurements are from my machine and I reviewed the text before posting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants