Merge pull request #11690 from Yarlord/fix-hybrid-nvidia-vaapi
Only force NVIDIA VA-API/GLX env when NVIDIA drives the display
This commit is contained in:
4 files changed
+111
-88
No files matched your search
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Detect whether NVIDIA drives the display (rather than a hybrid iGPU).
|
||||
|
||||
# On hybrid laptops the display is usually wired to the integrated GPU while
|
||||
# the NVIDIA card only handles offloaded rendering. Forcing NVIDIA's VA-API and
|
||||
# GLX drivers globally then breaks Chromium-based browsers (black video with
|
||||
# audio still playing), so the Hyprland NVIDIA env only applies when NVIDIA is
|
||||
# the display GPU.
|
||||
#
|
||||
# sysfs exposes which VGA device drives the display via boot_vga. When no
|
||||
# device claims it (attribute missing or unreadable), assume NVIDIA is the
|
||||
# display GPU to preserve the current behavior on machines we cannot tell.
|
||||
#
|
||||
# Read the cached sysfs IDs rather than lspci, which reads PCI config space and
|
||||
# resumes runtime-suspended GPUs.
|
||||
pci_devices_path="${OMARCHY_PCI_DEVICES_PATH:-/sys/bus/pci/devices}"
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
nvidia_boot=0
|
||||
other_boot=0
|
||||
|
||||
for device in "$pci_devices_path"/*; do
|
||||
[[ $(< "$device/class") == 0x03* ]] || continue
|
||||
boot_vga=0
|
||||
if [[ -f $device/boot_vga ]]; then
|
||||
boot_vga=$(< "$device/boot_vga")
|
||||
fi
|
||||
if [[ $(< "$device/vendor") == "0x10de" ]]; then
|
||||
if (( boot_vga == 1 )); then
|
||||
nvidia_boot=1
|
||||
fi
|
||||
else
|
||||
if (( boot_vga == 1 )); then
|
||||
other_boot=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if (( other_boot == 1 && nvidia_boot == 0 )); then
|
||||
exit 1
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
+12
-3
@@ -1,6 +1,7 @@
|
||||
local paths = require("default.hypr.paths")
|
||||
|
||||
local nvidia = paths.omarchy_path .. "/bin/omarchy-hw-nvidia"
|
||||
local nvidia_display = paths.omarchy_path .. "/bin/omarchy-hw-nvidia-display"
|
||||
local nvidia_gsp = paths.omarchy_path .. "/bin/omarchy-hw-nvidia-gsp"
|
||||
local nvidia_without_gsp = paths.omarchy_path .. "/bin/omarchy-hw-nvidia-without-gsp"
|
||||
|
||||
@@ -8,12 +9,20 @@ local nvidia_without_gsp = paths.omarchy_path .. "/bin/omarchy-hw-nvidia-without
|
||||
-- lspci reads PCI config space, which resumes a runtime-suspended GPU, and on a
|
||||
-- hybrid laptop that wake alone outlasts Hyprland's 1.5s config reload budget.
|
||||
if o.shell_succeeds(o.shell_quote(nvidia)) then
|
||||
-- On hybrid laptops the display is wired to the iGPU, so forcing NVIDIA's
|
||||
-- VA-API/GLX drivers globally breaks Chromium-based browsers (black video).
|
||||
-- Only apply those when NVIDIA actually drives the display.
|
||||
local nvidia_is_display = o.shell_succeeds(o.shell_quote(nvidia_display))
|
||||
if o.shell_succeeds(o.shell_quote(nvidia_gsp)) then
|
||||
hl.env("NVD_BACKEND", "direct")
|
||||
hl.env("LIBVA_DRIVER_NAME", "nvidia")
|
||||
hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
|
||||
if nvidia_is_display then
|
||||
hl.env("LIBVA_DRIVER_NAME", "nvidia")
|
||||
hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
|
||||
end
|
||||
elseif o.shell_succeeds(o.shell_quote(nvidia_without_gsp)) then
|
||||
hl.env("NVD_BACKEND", "egl")
|
||||
hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
|
||||
if nvidia_is_display then
|
||||
hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
echo "Apply the NVIDIA video driver fix on hybrid laptops"
|
||||
|
||||
if omarchy-hw-nvidia && ! omarchy-hw-nvidia-display; then
|
||||
# Hyprland applies env at startup. Reloading the updated defaults cannot
|
||||
# clear the old overrides inherited by the compositor and its applications.
|
||||
omarchy-state set reboot-required
|
||||
echo "Reboot to stop forcing NVIDIA VA-API/GLX drivers on the integrated GPU."
|
||||
fi
|
||||
@@ -3,97 +3,58 @@
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
require_command lua
|
||||
|
||||
tmp_dir=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp_dir"' EXIT
|
||||
export OMARCHY_PCI_DEVICES_PATH="$tmp_dir/devices" OMARCHY_PATH="$ROOT" PATH="$ROOT/bin:$PATH"
|
||||
|
||||
# Each argument is a PCI device as "vendor:device:class", in sysfs's own format.
|
||||
write_pci_devices() {
|
||||
# Exit statuses, expected NVD_BACKEND/LIBVA/GLX values, then vendor:device:class[:boot_vga].
|
||||
while IFS='|' read -r description nvidia gsp without_gsp display expected_env devices; do
|
||||
rm -rf "$tmp_dir/devices"
|
||||
mkdir -p "$tmp_dir/devices"
|
||||
|
||||
local index=0
|
||||
local spec
|
||||
for spec in "$@"; do
|
||||
local slot
|
||||
slot=$(printf '0000:%02x:00.0' "$index")
|
||||
mkdir -p "$tmp_dir/devices/$slot"
|
||||
printf '%s\n' "${spec%%:*}" >"$tmp_dir/devices/$slot/vendor"
|
||||
printf '%s\n' "$(cut -d: -f2 <<<"$spec")" >"$tmp_dir/devices/$slot/device"
|
||||
printf '%s\n' "${spec##*:}" >"$tmp_dir/devices/$slot/class"
|
||||
index=$((index + 1))
|
||||
done
|
||||
}
|
||||
|
||||
hw_nvidia() {
|
||||
OMARCHY_PCI_DEVICES_PATH="$tmp_dir/devices" "$ROOT/bin/omarchy-hw-$1"
|
||||
}
|
||||
|
||||
assert_detects() {
|
||||
local description="$1" nvidia="$2" gsp="$3" without_gsp="$4"
|
||||
|
||||
local command
|
||||
for command in nvidia gsp without-gsp; do
|
||||
local expected
|
||||
case $command in
|
||||
nvidia) expected=$nvidia ;;
|
||||
gsp) expected=$gsp ;;
|
||||
without-gsp) expected=$without_gsp ;;
|
||||
esac
|
||||
|
||||
local detector=nvidia
|
||||
[[ $command == "nvidia" ]] || detector="nvidia-$command"
|
||||
|
||||
local actual=no
|
||||
hw_nvidia "$detector" && actual=yes
|
||||
|
||||
[[ $actual == "$expected" ]] ||
|
||||
fail "$description" "omarchy-hw-$detector: expected $expected, got $actual"
|
||||
slot=0
|
||||
for spec in $devices; do
|
||||
IFS=: read -r vendor device class boot_vga <<<"$spec"
|
||||
device_dir="$tmp_dir/devices/$slot"
|
||||
mkdir -p "$device_dir"
|
||||
printf '%s\n' "$vendor" >"$device_dir/vendor"
|
||||
printf '%s\n' "$device" >"$device_dir/device"
|
||||
printf '%s\n' "$class" >"$device_dir/class"
|
||||
if [[ -n $boot_vga ]]; then
|
||||
printf '%s\n' "$boot_vga" >"$device_dir/boot_vga"
|
||||
fi
|
||||
slot=$((slot + 1))
|
||||
done
|
||||
|
||||
for check in "nvidia:$nvidia" "nvidia-gsp:$gsp" "nvidia-without-gsp:$without_gsp" "nvidia-display:$display"; do
|
||||
status=0
|
||||
"omarchy-hw-${check%:*}" || status=$?
|
||||
[[ $status == "${check#*:}" ]] || fail "$description: ${check%:*}" "expected ${check#*:}, got $status"
|
||||
done
|
||||
|
||||
# Run the real Lua config and detectors; capture only Hyprland's env calls.
|
||||
actual_env=$(lua <<'LUA'
|
||||
package.path = os.getenv("ROOT") .. "/?.lua;" .. package.path
|
||||
require("default.hypr.helpers")
|
||||
local env = {}
|
||||
hl = { env = function(key, value) env[key] = value end }
|
||||
require("default.hypr.nvidia")
|
||||
print(table.concat({ env.NVD_BACKEND or "-", env.LIBVA_DRIVER_NAME or "-", env.__GLX_VENDOR_LIBRARY_NAME or "-" }, " "))
|
||||
LUA
|
||||
)
|
||||
[[ $actual_env == "$expected_env" ]] || fail "$description: driver environment" "expected $expected_env, got $actual_env"
|
||||
pass "$description"
|
||||
}
|
||||
|
||||
# AMD Cezanne integrated graphics.
|
||||
write_pci_devices 0x1002:0x15e7:0x030000
|
||||
assert_detects "a machine without an NVIDIA GPU detects nothing" no no no
|
||||
|
||||
# NVIDIA GA106M [RTX 3060 Mobile] alongside AMD Cezanne, the pair from issue #6660.
|
||||
write_pci_devices 0x1002:0x15e7:0x030000 0x10de:0x2560:0x030200
|
||||
assert_detects "a hybrid Ampere laptop detects a GSP GPU" yes yes no
|
||||
|
||||
# NVIDIA TU117M [GTX 1650 Mobile], the first generation with GSP firmware.
|
||||
write_pci_devices 0x10de:0x1f91:0x030000
|
||||
assert_detects "Turing is the oldest generation with GSP firmware" yes yes no
|
||||
|
||||
# NVIDIA GV100 [TITAN V], the newest generation without GSP firmware.
|
||||
write_pci_devices 0x10de:0x1d81:0x030000
|
||||
assert_detects "Volta is the newest generation without GSP firmware" yes no yes
|
||||
|
||||
# NVIDIA GP104 [GTX 1080].
|
||||
write_pci_devices 0x10de:0x1b80:0x030000
|
||||
assert_detects "Pascal detects a GPU without GSP firmware" yes no yes
|
||||
|
||||
# NVIDIA GM108M [GeForce 830M], the oldest part the 580xx driver supports.
|
||||
write_pci_devices 0x10de:0x1340:0x030000
|
||||
assert_detects "Maxwell detects a GPU without GSP firmware" yes no yes
|
||||
|
||||
# NVIDIA GK110 [GTX 780]. Kepler predates GSP but also predates 580xx, so
|
||||
# claiming it here would install a driver that cannot drive it.
|
||||
write_pci_devices 0x10de:0x1004:0x030000
|
||||
assert_detects "Kepler is too old for either driver" yes no no
|
||||
|
||||
# NVIDIA GF100 [GTX 470], older still.
|
||||
write_pci_devices 0x10de:0x06cd:0x030000
|
||||
assert_detects "Fermi is too old for either driver" yes no no
|
||||
|
||||
# NVIDIA GB203 [RTX 5080], newer than every other device ID here.
|
||||
write_pci_devices 0x10de:0x2c02:0x030000
|
||||
assert_detects "Blackwell detects a GSP GPU" yes yes no
|
||||
|
||||
# The GA106 audio function carries the NVIDIA vendor ID but is not a GPU.
|
||||
write_pci_devices 0x10de:0x228e:0x040300
|
||||
assert_detects "a non-display NVIDIA function is not a GPU" no no no
|
||||
|
||||
write_pci_devices
|
||||
assert_detects "a machine with no PCI devices detects nothing" no no no
|
||||
done <<'CASES'
|
||||
AMD only|1|1|1|1|- - -|0x1002:0x15e7:0x030000:1
|
||||
NVIDIA audio only|1|1|1|0|- - -|0x10de:0x228e:0x040300
|
||||
No PCI devices|1|1|1|0|- - -|
|
||||
Turing (first GSP)|0|0|1|0|direct nvidia nvidia|0x10de:0x1f91:0x030000
|
||||
Volta (last without GSP)|0|1|0|0|egl - nvidia|0x10de:0x1d81:0x030000
|
||||
Maxwell (first 580xx)|0|1|0|0|egl - nvidia|0x10de:0x1340:0x030000
|
||||
Kepler (unsupported)|0|1|1|0|- - -|0x10de:0x1004:0x030000
|
||||
AMD display with Ampere offload|0|0|1|1|direct - -|0x1002:0x15bf:0x030000:1 0x10de:0x25ac:0x030200:0
|
||||
Intel display with Maxwell offload|0|1|0|1|egl - -|0x8086:0x46a6:0x030000:1 0x10de:0x1340:0x030000:0
|
||||
NVIDIA display with inactive iGPU|0|0|1|0|direct nvidia nvidia|0x1002:0x15bf:0x030000:0 0x10de:0x2c02:0x030000:1
|
||||
Hybrid without boot_vga|0|0|1|0|direct nvidia nvidia|0x1002:0x15e7:0x030000 0x10de:0x2560:0x030200
|
||||
CASES
|
||||
Reference in new issue
Block a user