PRIME
PRIME 是一项用于管理现代台式机和笔记本电脑中混合显卡的技术(NVIDIA Optimus,Radeon 的 AMD 动态可切换显卡)。PRIME GPU 卸载和反向 PRIME 是 Linux 内核中用于支持 muxless 混合显卡的技术方案。
PRIME GPU 卸载
我们的目的是在性能更强的显卡上渲染应用程序,并将结果发送到连接了显示器的显卡上。
可以使用命令 xrandr --setprovideroffloadsink provider sink 来让渲染卸载提供程序将其输出发送到接收端(即连接了显示器的提供程序)。提供程序和接收端的标识符可以是数字 (0x7d, 0x56) 或区分大小写的名称 (Intel, radeon)。
xf86-video-* 或内置的 modesetting)驱动程序时,此设置不再必要,因为它们默认启用了 DRI3,因此会自动进行这些分配。不过,再次显式设置它们也不会造成负面影响。示例
$ xrandr --setprovideroffloadsink radeon Intel
你也可以使用提供程序索引而不是提供程序名称。
$ xrandr --setprovideroffloadsink 1 0
用于开源驱动—PRIME
若要将独立显卡用于最需要的应用程序(例如游戏、3D 建模软件等),请在命令前添加 DRI_PRIME=1 环境变量。
$ DRI_PRIME=1 glxinfo | grep "OpenGL renderer"
OpenGL renderer string: Gallium 0.4 on AMD TURKS
- 除了数值,你也可以指定 PCI 设备名称。格式类似于
/sys/bus/pci/devices/,但需以pci-为前缀,并将分号和点替换为下划线,例如DRI_PRIME=pci-0000_01_00_0。你可以通过lspci -nnd ::03xx列出按 PCIe ID 分类的图形设备。 - 对于 Vulkan 渲染,你可以添加
!以仅将所选 GPU 暴露给应用程序,例如DRI_PRIME=1!。
其他应用程序仍将使用功耗较低的集成显卡。这些设置在 X 服务器重启后即会丢失,你可能希望编写一个脚本并将其在桌面环境启动时自动运行(或者将其放入 /etc/X11/xinit/xinitrc.d/)。但请注意,这可能会缩短电池寿命并增加发热量。
有关更多信息,请参阅 Gentoo:AMDGPU#Identifying which graphics card is in use。
为了使 DRI_PRIME 在 Vulkan 应用程序上工作,需要安装 vulkan-mesa-layers,对于 32 位应用程序,还需要安装 lib32-vulkan-mesa-layers。
关于 Windows 游戏的说明
在 Wine 或 Proton 下运行 Windows DirectX 游戏时,你需要使用环境变量直接指示 DXVK。
DXVK_FILTER_DEVICE_NAME="[your preferred card name]"
通过 vulkaninfo 获取显卡名称;DXVK 使用子字符串匹配。
PRIME 渲染卸载
NVIDIA 驱动程序自 435.17 版本起支持此方法。官方支持的 iGPU 驱动程序包括 modesetting、xf86-video-amdgpu (450.57) 和 xf86-video-intel (455.38)。
若要在 NVIDIA 显卡上运行程序,可以使用 nvidia-prime 提供的 prime-run 脚本。
$ prime-run glxinfo | grep "OpenGL renderer" $ prime-run vulkaninfo
PCI-Express 运行时 D3 (RTD3) 电源管理
开源驱动
内核 PCI 电源管理会在不使用 PRIME 卸载或反向 PRIME 时关闭 GPU。此功能由 modesetting、xf86-video-amdgpu、xf86-video-intel 和 xf86-video-nouveau 驱动程序支持。
可以使用以下命令检查每个 GPU 的当前 [1] 电源状态:
$ cat /sys/class/drm/card*/device/power_state
NVIDIA
对于使用 Intel Coffee Lake 或更高版本 CPU 以及部分 Ryzen CPU(如 5800H)的 Turing 架构显卡,可以实现 在不使用时完全关闭 GPU 电源。
需要根据 NVIDIA 的建议添加以下 udev 规则:
/etc/udev/rules.d/80-nvidia-pm.rules
# Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
# Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
一些用户还 报告 需要添加以下附加行:
/etc/udev/rules.d/80-nvidia-pm.rules
# Enable runtime PM for NVIDIA VGA/3D controller devices on adding device
ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
此外,添加以下 模块参数:
/etc/modprobe.d/nvidia-pm.conf
options nvidia "NVreg_DynamicPowerManagement=0x02"
NVreg_DynamicPowerManagement=0x03。或者,你可以安装 nvidia-prime-rtd3pmAUR,它提供了这两个配置文件。
手动或使用 AUR 包设置好 udev 规则和 模块参数后,你需要重启笔记本电脑。
要检查 NVIDIA GPU 是否已关闭,可以使用此命令:
$ cat /sys/bus/pci/devices/0000:01:00.0/power/runtime_status
你将看到 suspended 或 running。如果显示 suspended,说明 GPU 已关闭。此时功耗将为 0 瓦,从而延长电池寿命。
在某些情况下(如 NVIDIA RTX A1000),可能不会列出上述任何选项,结果显示为 active。但这并不意味着 GPU 处于 running 状态。在这种情况下,你可以使用以下命令检查状态:
$ cat /sys/bus/pci/devices/0000:01:00.0/power/runtime_suspended_time
当 GPU 处于 suspended 状态时,每次运行该命令时计数器都会增加。当 GPU 状态变为 running 时,它将停止增加。
如果你注意到 runtime_suspended_time 没有增加,可以使用此命令检查你的 D3 状态:
$ cat /proc/driver/nvidia/gpus/0000:01:00.0/power
如果报告 Runtime D3 status: Not supported:
- nvidia-open 用户:请升级到 610 或更高版本的驱动程序,在该版本中,运行时 D3 电源管理已 默认获得完全支持。(在较早版本的开源驱动上,即便是下方的变通方法也 无效。)
- 闭源驱动用户:你可能需要按照 此论坛帖子 中的步骤禁用 GSP 固件。
要检查是什么在使用 GPU,可以使用以下命令:
# lsof +c0 /dev/nvidia*
与 nvidia-smi 不同,它会报告每个使用该设备的进程,并且不会唤醒 GPU。
我们还需要 启用 nvidia-persistenced.service,以避免在 NVIDIA 设备资源不再使用时,内核强制拆除设备状态。[4]
配置应用程序使用 GPU 渲染
即使未启用动态电源管理,也需要对应用程序进行渲染卸载 [5]。
要在启用动态电源管理的情况下运行卸载到 NVIDIA GPU 的应用程序,请添加以下 环境变量:[6]
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia command
在 Steam 游戏上使用时,启动项命令行可以设置为:
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia %command%
桌面环境集成
安装 switcheroo-control 并 启用 switcheroo-control.service。
GNOME、KDE Plasma、Cinnamon、COSMIC 和 Budgie 桌面环境将遵循 桌面入口文件 中的 PrefersNonDefaultGPU 属性,并自动在指定的 GPU 上启动应用程序。
此外,在 GNOME、Cinnamon 和 COSMIC 上,你可以通过右键点击图标并分别选择 使用独立显卡启动 (GNOME)、使用专用 GPU 运行 (Cinnamon) 和 使用 (GPU 名称) 运行 (COSMIC) 来启动应用程序。
故障排除
如果你安装了 bumblebee,你应该将其卸载,因为它会列出 nvidia_drm 驱动程序的黑名单,而该驱动程序是 X 服务器进行卸载所必需的。
PRIME 同步
使用 PRIME 时,主 GPU 渲染屏幕内容/应用程序,并将其传递给辅助 GPU 进行显示。引用 一个 NVIDIA 论坛帖,"传统的垂直同步 (vsync) 可以将应用程序的渲染与系统内存的复制同步,但需要一种额外的机制将系统内存的复制与 iGPU 的显示引擎同步。与传统的垂直同步不同,这种机制必须涉及 dGPU 和 iGPU 驱动程序之间的通信。"
这种同步通过 PRIME 同步实现。要检查显示器是否启用了 PRIME 同步,请检查 xrandr --prop 的输出。
要启用它,请运行:
$ xrandr --output <output-name> --set "PRIME Synchronization" 1
- 使用 NVIDIA 驱动程序进行 PRIME 同步的前提条件是 启用 modesetting。
- PRIME 同步不适用于 AMDGPU DDX 驱动程序 (xf86-video-amdgpu)。
反向 PRIME
如果第二个 GPU 具有主 GPU 无法访问的输出接口,你可以使用 反向 PRIME 来利用它们。这将涉及使用主 GPU 渲染图像,然后将其传递给第二个 GPU。
它可能开箱即用,但如果没有,请执行以下步骤。
配置
首先,确定集成 GPU 的 BusID。
lspci -d ::03xx
00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 630 (Mobile) 01:00.0 VGA compatible controller: NVIDIA Corporation TU117M [GeForce GTX 1650 Mobile / Max-Q] (rev a1)
在上面的示例中,Intel 显卡的地址为 00:02.0,转换为 PCI:0:2:0。
按照如下方式设置你的 xorg.conf 并调整 BusID。
/etc/X11/xorg.conf
Section "ServerLayout"
Identifier "layout"
Screen 0 "intel"
Inactive "nvidia"
Option "AllowNVIDIAGPUScreens"
EndSection
Section "Device"
Identifier "nvidia"
Driver "nvidia"
EndSection
Section "Screen"
Identifier "nvidia"
Device "nvidia"
EndSection
Section "Device"
Identifier "intel"
Driver "modesetting"
BusID "PCI:0:2:0"
EndSection
Section "Screen"
Identifier "intel"
Device "intel"
EndSection
得益于 AutoBindGPU[9],辅助 GPU 将自动设置为输出接收端和卸载源。如果未发生这种情况,或者你想自行配置,请运行 xrandr --setprovideroutputsource provider source 将提供程序设置为源的输出。例如:
$ xrandr --listproviders Providers: number : 2 Provider 0: id: 0x48 cap: 0xf, Source Output, Sink Output, Source Offload, Sink Offload crtcs: 4 outputs: 6 associated providers: 1 name:modesetting Provider 1: id: 0x257 cap: 0x2, Sink Output crtcs: 4 outputs: 5 associated providers: 1 name:NVIDIA-G0 $ xrandr --setprovideroutputsource NVIDIA-G0 modesetting
完成后,独立显卡的输出接口应出现在 xrandr 中,你可以执行类似以下操作:
$ xrandr --output HDMI-1 --auto --above LVDS1
来配置内部和外部显示器。
已知问题
如果重启后你只有一个提供程序,可能是因为 Xorg 启动时 nvidia 模块尚未加载。你需要启用早期模块加载。详情请参阅 NVIDIA#Early loading。
故障排除
XRandR 仅指定 1 个输出提供程序
删除/移动 /etc/X11/xorg.conf 文件以及 /etc/X11/xorg.conf.d/ 中任何与 GPU 相关的其他文件。在此更改后重启 X 服务器。
如果视频驱动程序在 /etc/modprobe.d/ 或 /usr/lib/modprobe.d/ 中被列入黑名单,请加载模块并重启 X。如果你对 NVIDIA GPU 使用 bbswitch 模块,可能会出现这种情况。
另一个可能的问题是 Xorg 可能会尝试自动将监视器分配给第二个 GPU。请检查日志:
$ grep "No modes" ~/.local/share/xorg/Xorg.0.log
AMDGPU(0): No modes.
要解决此问题,请在 xorg.conf 中添加带有 inactive 设备的 ServerLayout 部分:
/etc/X11/xorg.conf
Section "ServerLayout" Identifier "X.org Configured" Screen 0 "Screen0" 0 0 # Screen for your primary GPU Inactive "Card1" # Device for your second GPU EndSection
当应用程序使用独立显卡渲染时,仅显示黑屏
在某些情况下,PRIME 需要合成管理器才能正常工作。如果你的窗口管理器不处理合成,你可以在其上使用一个 合成器。
如果你使用 Xfce,可以进入 菜单 > 设置 > 窗口管理器微调 > 合成器 并启用合成,然后重试你的应用程序。
基于 GL 的合成器黑屏
目前,基于 GL 的合成器和 PRIME 卸载存在问题。虽然基于 Xrender 的合成器(xcompmgr、xfwm、compton 的默认后端、cairo-compmgr 等)可以正常工作,但基于 GL 的合成器(Mutter/muffin、Compiz、带 GLX 后端的 compton、Kwin 的 OpenGL 后端等)最初会显示黑屏,就像没有运行合成器一样。虽然你可以通过调整卸载窗口的大小来强制显示图像,但这并不是一个实用的解决方案,因为它不适用于全屏 Wine 应用程序等情况。这意味着 GNOME3 和 Cinnamon 等桌面环境在使用 PRIME 卸载时存在问题。
此外,如果你使用的是 Intel IGP,你或许可以通过将 IGP 作为 UXA 而不是 SNA 运行来修复 GL 合成问题,但这也可能导致卸载过程出现问题(例如,xrandr --listproviders 可能不会列出独立 GPU)。
详情请参见 FDO Bug #69101。
解决此问题的另一种方法是在 Intel 驱动程序中启用 DRI3。请参阅下面的问题以获取示例配置。
GNOME
你可能会发现 禁用全屏取消重定向 (fullscreen undirect) 可以使 PRIME 卸载在全屏应用程序上正常工作。
使用 PRIME 和切换窗口/工作区时发生内核崩溃/oops
使用带有集成显卡配置文件的 DRI3 似乎可以解决此问题。
要启用 DRI3,你需要为集成显卡创建一个配置,添加 DRI3 选项:
Section "Device"
Identifier "Intel Graphics"
Driver "intel"
Option "DRI" "3"
EndSection
之后,你可以使用 DRI_PRIME=1 而无需运行 xrandr --setprovideroffloadsink radeon Intel,因为 DRI3 将处理卸载。
使用反向 PRIME 时,第二个显示器出现故障/重影同步问题
当不使用 合成管理器(例如在 i3 中)时,此问题可能会影响用户。[10]
如果你在 Gnome 下遇到此问题,一个可能的解决方法是在 /etc/environment 中设置一些环境变量 [11]:
CLUTTER_PAINT=disable-clipped-redraws:disable-culling CLUTTER_VBLANK=True
启动 GL 应用程序时报错 "radeon: Failed to allocate virtual address for buffer:"
当内核驱动程序中的电源管理正在运行时,会出现此错误。你可以通过在 引导加载程序 的内核参数中添加 radeon.runpm=0 来克服此错误。
使用闭源驱动和反向 PRIME 时,启用 VSync 的 Vulkan 应用程序/游戏持续卡死/冻结
某些 Vulkan 应用程序(特别是使用 VK_PRESENT_MODE_FIFO_KHR 和/或 VK_PRESENT_MODE_FIFO_RELAXED_KHR 的应用程序,包括使用 DXVK 运行的 Windows 游戏)在反向 PRIME 系统上运行时会导致 GPU 持续锁定(~5-10 秒冻结,~1 秒正常工作)[12]。
GPU 锁定将使任何输入变得不可用(包括切换 TTY 和使用 SysRq 功能)。
目前没有针对此 NVIDIA bug 的已知修复程序,但存在一些变通方法:
- 关闭 Vsync(某些应用程序无法关闭)。
- 关闭 PRIME 同步[13](会导致屏幕撕裂)。
xrandr --output HDMI-0 --set "PRIME Synchronization" 0 #replace HDMI-0 with your xrandr output ID
你可以通过从 vulkan-tools 包运行 vkcube 来简单地验证你的配置是否受此问题影响。
在 Wayland 下打开某些程序时有延迟
如果你启用了 RTD3(来自 #NVIDIA),在使用 Wayland 时,打开某些程序会遇到一些延迟。根据应用程序的不同,这可能由两个源引起:Vulkan 或 OpenGL,但引起延迟的机制相同。两者都必须确定要委托给哪个设备。此决策基于配置文件。对于 OpenGL,配置文件位于 /usr/share/glvnd/egl_vendor.d/,而对于 Vulkan,它们位于 /usr/share/vulkan/icd.d/。实际的延迟是由确定候选者需要迭代所有潜在的渲染配置引起的。即使首选配置出现在其他配置之前(即数字较小,iGPU 在外部 GPU 之前),它仍会遍历所有可用选项。在尝试休眠的(外部)GPU 配置时,它会被唤醒(这需要 ~1 秒或更长时间),然后再委托打开程序,浪费了时间和电池寿命。这是一个 NVIDIA 驱动程序问题。更多详情请点击此处。
一种解决方案是确保独立 GPU 不被启动,即确保它不被迭代。这可以通过将配置显式设置为环境变量来实现。这些变量可以直接在运行程序时传递,也可以全局设置(例如在你的 /etc/environment 文件中)。请注意,全局设置该变量需要你在刻意尝试使用外部 GPU 运行程序时取消设置该变量(或者将其分别设置为 nvidia 文件)。
/etc/environment
__EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json VK_DRIVER_FILES=/usr/share/vulkan/icd.d/intel_icd.json __GLX_VENDOR_LIBRARY_NAME=mesa
运行带有 DXVK 的 Wine 游戏时报错
使用 PRIME 卸载时,遇到 Major opcode of failed request: 156 (NV-GLX) 是一个已知问题。唯一的已知变通方法是将 X 会话 完全在 NVIDIA GPU 上启动。在仅使用 NVIDIA 和 PRIME 卸载方法之间切换的一种用户友好的方式是使用 optimus-manager 工具,或自己编写一些自动化脚本。
Wayland 上 Vulkan/OpenGL 应用程序段错误 (segfault)
VK_KHR_wayland_surface 需要内核模式设置。如果你正在使用 Wayland 合成器且无法在 dGPU 上启动应用程序而必须强制它们在 Xwayland 下运行,请确保启用了模式设置。
这意味着如果内核参数中存在 nvidia_drm.modeset=0,请将其移除。