Статья основана на моих оригинальных англоязычных записях тут и тут.
Приветствую, несчастные владельцы ноутбуков с технологией с NVIDIA Optimus, а именно те, кому посчастливилось иметь зелёную GPU до поколения Turing (GTX 1650 и выше). Список поколений микроархитектур NVIDIA можно найти на википедии. Как известно, начиная с версии 435.xx
в проприетарном драйвере появилась нормальная поддержка энергосбережения для этих архитектур, так что если у вас NVIDIA GTX 1650
и выше, всё должно работать из коробки и простыню ниже можно не читать.
Сразу оговорюсь, что решение, представленное ниже, не зависит от дистрибутива и DE и не прибито к Wayland
или X11
. Хотя некоторые лайфхаки могут быть специфичны для GNOME, так как он является моей основной средой рабочего стола.
Единственным требованием является использование проприетарного драйвера NVIDIA.
Дано
- Ноутбук
MSI GS63VR
сGTX 1060
на борту; - ОС:
Fedora 38
; - Проприетарный драйвер NVIDIA (из RPMFusion);
- Основной рабочий сеанс:
GNOME Shell
/Wayland
.
Что хочется получить
- Полноценное отключение дискретной GPU по умолчанию, соответственно, продлевая жизнь от батареи;
- Возможность включить дискретную GPU и запустить какие-либо приложения на ней с помощью технологии
PRIME Offloading
штатными средствами DE; - Возможность переключения на сеанс, запущенный полностью на NVIDIA (без использования
PRIME
) по необходимости; - Возможность выключить дискретную GPU в любое время (вернуть в состояние по умолчанию);
- Включение/отключение GPU должно происходить без перезагрузки системы, а так же без выхода из рабочей сессии;
- По возможности отслеживать запущенные процессы на GPU и мониторить показания.
Мир OpenSource-решений предлагает нам 2 стула:
- Bumblebee + bbswitch. Несмотря на то, что сам Bumblebee устарел и использование
VirtualGL
при наличииPRIME Offloading
смысла не имеет,bbswitch
продолжает работать довольно неплохо и делает ровно то, что мне нужно. Я даже попытался использоватьbumblebeed
в качестве исключительно обёртки надbbswitch
(для загрузки/выгрузки модулей ядра и собственно дёргания/proc/acpi/bbswitch
), сделав для этого целый bash-костыль. Несмотря на то, что это даже работает, претендовать на нормальное решение никак не может: обратная связь отсутствует, а во время работы GPU его могут «увидеть» другие приложения, которые мы напрямую не запускали через скрипт, иbumblebeed
про них не в курсе. В таком случае, GPU не сможет выключиться, когда придёт время, так как модули используются этими приложениями (пункты4
и5
). Второй минус - нам приходится запускать нужные приложения через скрипт, DE не предлагает нам пункта меню с запуском на дискретной GPU (пункт2
). - system76-power и optimus-manager/prime-select и им подобные: такие утилиты позволяют менять конфигурацию модулей в
initramfs
и конфигX11
в зависимости от выбранного режима. Как можно понять, это требует иногда длительного ожидания пересборкиinitramfs
, а затем перезагрузки ОС, что противоречит пунктам4
и5
.
Спустя около года мучений я пришёл к законченному решению, которым делюсь с вами.
Решение
Что ж, раз из коробки ничего не придумали для моих хотелок, пришлось это запилить. Так как решение с bbswitch
в целом меня устраивало, не хватает лишь адекватной обёртки над ним. Вырисовывается следующая простейшая архитектура:
- bbswitchd: backend-демон, занимающийся только тем, что загружает/выгружает нужные модули, а так же пишет в
/proc/acpi/bbswitch
. Эдакий Bumblebee на минималках, однако, работающий из коробки и поставляющийся с необходимыми политикамиSELinux
, правиламиudev
и конфигурациейmodprobe
. Общение происходит через UNIX-сокет (для этого не забыть добавить пользователя в группуbbswitchd
). В комплекте идёт простейшая утилитаbbswitch
для включения и выключения GPU:$ bbswitch Usage: bbswitch on | off | status $ bbswitch on # Turn discrete GPU on $ bbswitch status # Request current status ON $ bbswitch off # Turn discrete GPU off
- bbswitch-gui: соответственно, графический фронтэнд на
Python
+GTK3
, позволяющий:- Включать и выключать питание дискретной GPU;
- Просматривать список процессов, использующих в данный момент дискретный GPU с возможностью быстро убить выбранные процессы;
- Мониторить параметры видеокарты (с помощью NVML API), а так же список загруженных модулей ядра;
- Адекватно обрабатывать ошибки при загрузке модулей и работе с
bbswitch
; - Висеть в системном лотке как
AppIndicator
и не мешать.
Более детальная информация со скриншотами и примерами использования доступна в README на GitHub для обоих проектов.
Оба приложения опакечены для Fedora (Copr):
$ sudo dnf copr enable polter/bumblebee
$ sudo dnf install bbswitch-gui
И для Ubuntu (PPA):
$ sudo add-apt-repository ppa:polter-rnd/bbswitch-gui
$ sudo apt update
$ sudo apt-get install bbswitch-gui
После установки видим следующие конфигурационные файлы:
-
Запрет на автоматическую загрузку модулей
nvidia
, а так же опцияload_state=0
для модуляbbswitch
для выключения дискретной GPU при загрузке:$ cat /lib/modprobe.d/bbswitch.conf options bbswitch load_state=0 blacklist nvidia blacklist nvidia-drm blacklist nvidia-modeset blacklist nvidia-uvm blacklist nouveau
-
Автоматическая загрузка модуля
bbswitch
:$ cat /lib/modules-load.d/bbswitch.conf # Load bbswitch.ko at boot bbswitch
-
Udev-правило для запрета оконному менеджеру
mutter
обнаруживать дискретную GPU:$ cat /lib/udev/rules.d/60-bbswitch-nvidia-mutter.rules DRIVERS=="nvidia", SUBSYSTEM=="drm", TAG+="mutter-device-ignore"
-
Udev-правило для автоматического удаления файлов устройств NVIDIA:
$ cat /lib/udev/rules.d/90-bbswitch-nvidia-dev.rules # Put this file in /lib/udev/rules.d or /etc/udev/rules.d # Prevent the nvidia card from "randomly" turning on DEVPATH=="/module/nvidia", ACTION=="remove", RUN+="/bin/sh -c 'rm -f /dev/nvidiactl /dev/nvidia[0-9] /dev/nvidia-modeset /dev/nvidia-uvm /dev/nvidia-uvm-tools'"
-
А так же
systemd
-сервис и соответствующий сокет:$ systemctl status bbswitchd.service ● bbswitchd.service - GPU power state management daemon Loaded: loaded (/usr/lib/systemd/system/bbswitchd.service; disabled; preset: disabled) TriggeredBy: ● bbswitchd.socket ...
$ systemctl status bbswitchd.socket ● bbswitchd.socket - Socket for bbswitchd daemon Loaded: loaded (/usr/lib/systemd/system/bbswitchd.socket; enabled; preset: enabled) Active: active (running) since Fri 2023-08-11 17:54:11 MSK; 5h 17min ago Triggers: ● bbswitchd.service ...
После перезагрузки получаем максимально удобный механизм запуска приложений на внешней GPU: после включения дискретного адаптера DE начнёт предлагать запустить приложения на ней (при наличии switcheroo-control, который установлен из коробки в большинстве дистрибутивов).
Если GPU не может выключиться из-за того, что какие-либо приложения её используют, это видно в главном окне (вместе с информацией об утилизации ресурсов) и при необходимости можно их убить.
Tips & Tricks
Ниже несколько лайфхаков, которые делают использование всего этого более мягким и шелковистым.
Автозапуск bbswitch-gui
Чтобы bbswitch-gui
запускался свёрнутым в трей, можно создать файл ~/.config/autostart/io.github.polter-rnd.bbswitch-gui.desktop
со следующим содержимым (опция -m
означает запуск в свёрнутом состоянии):
[Desktop Entry]
Type=Application
Encoding=UTF-8
Name=BBswitch GUI
Comment=GUI tool for managing NVIDIA GPU power states and utilization
Exec=/usr/bin/bbswitch-gui -v -m
Icon=bbswitch-gui
Categories=System;Monitor;Utility;X-GNOME-Utilities;
Keywords=nvidia;bbswitch;optimus;prime;
X-AppInstall-Keywords=nvidia;bbswitch;optimus;prime;
X-GNOME-Keywords=nvidia;bbswitch;optimus;prime;
X-GNOME-UsesNotifications=true
Запуск gnome-shell всегда на встроенной видеокарте
Если выйти из Wayland
-сессии со включенной NVIDIA GPU
(например, чтобы зайти в X11
сессию), то при следующем входе gnome-shell
увидит включенную видеокарту и подключится к ней. В результате не получится отключить дискретную GPU
, т.к. процесс композитора к ней подключён.
Для решения этого переопределим конфигурацию сервиса org.gnome.Shell@wayland.service
:
$ systemctl --user edit org.gnome.Shell@wayland.service
Следующим содержимым:
[Service]
ExecStartPre=/usr/bin/mkdir -p /tmp/egl_vendor.d
ExecStartPre=/usr/bin/rm -f /tmp/egl_vendor.d/10_nvidia.json
ExecStartPre=/usr/bin/ln -fs /usr/share/glvnd/egl_vendor.d/50_mesa.json /tmp/egl_vendor.d
Environment=__EGL_VENDOR_LIBRARY_DIRS=/tmp/egl_vendor.d
ExecStartPost=/usr/bin/ln -fs /usr/share/glvnd/egl_vendor.d/10_nvidia.json /tmp/egl_vendor.d
Теперь видеокарта NVIDIA
скрыта от gnome-shell
при запуске на Wayland
, в то время, как для остальных процессов она остаётся доступна.
Если вы не пользуетесь Wayland
и хотите такое же поведение для X11
сессии, переопределять нужно org.gnome.Shell@x11.service
.
Переключение на сеанс, запущенный полностью на NVIDIA GPU
Чтобы реализовать пункт 3
в моем случае было проще всего настроить X11
для работы в NVIDIA
-only режиме, в то время, как дефолтная Wayland
-сессия всегда запускается на встроенном видеоадаптере:
$ cat /etc/X11/xorg.conf.d/10-nvidia.conf
Section "OutputClass"
Identifier "nvidia"
MatchDriver "nvidia-drm"
Driver "nvidia"
Option "AllowEmptyInitialConfiguration"
Option "SLI" "Auto"
Option "BaseMosaic" "on"
Option "PrimaryGPU" "yes"
EndSection
Section "ServerLayout"
Identifier "layout"
Option "AllowNVIDIAGPUScreens"
EndSection
Таким образом, при входе в сеанс X11
при включенной дискретной GPU
иксы запустятся полностью на NVIDIA
, в тоже время, при выключенной - запустятся на встроенном графическом адаптере. Удобно (при наличии сеанса Wayland
, из которого можно всегда выключить дискретный адаптер при необходимости).
Сохранение конфигурации экранов при переключении X11/Wayland
В моём случае в сеансе Wayland
используется дробное масштабирование (экспериментальная возможность mutter
). Однако, при запуске сеанса X11
наличие дробного масштаба в конфигурации мониторов делает её невалидной, и оконный менеджер сбрасывает её на конфигурацию по-умолчанию. Чтобы комфортно сохранять конфигурацию в отдельных файлах для X11
и Wayland
, можно сделать следующее:
$ cp ~/.config/monitors.xml ~/.config/monitors.xml.wayland
$ cp ~/.config/monitors.xml ~/.config/monitors.xml.x11
$ ln -sf ~/.config/monitors.xml.${XDG_SESSION_TYPE} ~/.config/monitors.xml
Таким образом мы сохранили конфигурацию в отдельные файлы и выставили символьную ссылку на конфигурацию текущего сеанса.
Осталось добавить костыль в ~/.bash_profile
для переключения конфигурации при входе:
if [ "${XDG_SESSION_TYPE}" = "wayland" -o "${XDG_SESSION_TYPE}" = "x11" ]
then
MONITORS_XML="${HOME}/.config/monitors.xml"
if [ -L "${MONITORS_XML}~" ]
then
cp "${MONITORS_XML}" $(readlink -f "${MONITORS_XML}~")
rm -f "${MONITORS_XML}~"
fi
ln -sf ${MONITORS_XML}{.${XDG_SESSION_TYPE},}
fi