プロプライエタリ nvidia ドライバでの GPU オフロードで、 bumblebee が不要になる

# 概要

オンボードグラフィックスとディスクリート GPU を両方積んでいるような状況、たとえば典型的にはグラボを積んだラップトップなどにおいて、通常はオンボードのグラフィックスを利用し、ゲーム等の特別に重い処理のみディスクリート GPU に任せる、 GPU オフロードという技術がある。

従来、 nvidia の GPU をプロプライエタリのドライバで利用する場合、 GPU オフロードには [bumblebee](https://bumblebee-project.org/) や [primus](https://github.com/amonakov/primus) などを使う必要があった。
また、 bumblebee や primus は OpenGL 用のものであり、 Vulkan で GPU オフロードするにはまた別の実装が必要であった。

しかし、 nvidia-drivers 435 あたりからはプロプライエタリドライバに PRIME render offload の対応が入り、 bumblebee なしで OpenGL や Vulkan の GPU オフロードができるようになった。

以下では、この新機能を gentoo で利用する手順を説明する。

この情報は **2019-10-17 時点のもの**であり、現時点または将来に渡る安定した動作は保証しないことに留意せよ。
(まあそもそも nvidia-drivers 自体が安定していないという話はある🙄)

**追記 2019-12-06**: `libglvnd` USE flag と media-libs/libglvnd-1.3.0 の mask が解除されたため、以下の手順の大半は不要になった。
現時点で (これからも) 必要な手順は以下の通り:

* `libglvnd` USE flag を通常の手順で有効化する (と同時に x11-drivers/nvidia-drivers の関連するフラグも設定する)
    + もはや profile/package.use.mask の編集は不要
* `>=x11-base/xorg-server-1.20.6` と `>=media-libs/libglvnd-1.3.0` と `>=x11-drivers/nvidia-drivers-435.21` を emerge する
    + もはや package.unmask の編集は不要
    + パッチ適用も不要

(追記終わり)

# 環境

* Gentoo Linux (amd64)
* `x11-base/xorg-server-1.20.5`
    + 今のところ入手できるパッチが 1.20.5 用なので、 1.20.6 などが出たときはまた手を加える必要があるかもしれない (たぶん次は 1.21 だと思うけど)。
* `x11-drivers/nvidia-drivers-435.21`
    + 430.50 だとたぶん無理。試してないけど。
    + 435.21 だと bumblebee や primus が (少なくとも私の環境では) 正常に動作しないようなので、 bumblebee を使うつもりならしばらくは 430.50 を使い続けた方がよさそう。

未リリースのソフトウェアやパッチが必要になりそうなので、 gentoo amd64 以外の環境でどうなるか (いつごろ利用可能になるか、あるいは既に可能か) は知らない。

# 手順

## 1. 関連機能を unmask し有効化する

### portage の設定を編集

まず [`libglvnd` USE flag](https://packages.gentoo.org/useflags/libglvnd) が mask されているため、これを unmask し、かつ有効化する。

```conf:/etc/portage/profile/package.use.mask
>=media-libs/mesa-19.2.1 -libglvnd
>=x11-base/xorg-server-1.20.5 -libglvnd
>=x11-drivers/nvidia-drivers-435.21 -libglvnd
```

また、 PRIME render offload には kernel modesetting を使うため、 `kms` フラグも同時に有効化する。
`compat` フラグを有効化してしまうと non-GLVND の libGL がインストールされてしまうため、これは無効化すること。

```conf:/etc/portage/package.use/nvidia
# Proprietary nvidia-drivers require glvnd for PRIME render offloading.
# See <http://us.download.nvidia.com/XFree86/Linux-x86_64/435.21/README/primerenderoffload.html>
# and <https://bugs.gentoo.org/692206>.
>=media-libs/mesa-19.2.1 libglvnd
>=x11-base/xorg-server-1.20.5 libglvnd
>=x11-drivers/nvidia-drivers-435.21 libglvnd -compat
>=x11-drivers/nvidia-drivers-435.21 kms
```

また、 [media-libs/libglvnd](https://packages.gentoo.org/packages/media-libs/libglvnd) も mask されているため、これも unmask する。

```conf:/etc/portage/package.unmask/libglvnd
# See <https://bugs.gentoo.org/692206>.
# 将来的に標準で unmask されたら、この行は削除すべし
<media-libs/libglvnd-9999
```

## 2. X サーバにパッチを当ててリビルド

**更新 (2019-11-23)**: x11-base/xorg-server-1.20.6 がリリースされ、これに以下で必要なコードは全て入っている。
そのため、 1.20.6 以上をインストールするならパッチを導入する工程は不要。

### パッチを当てる

<https://bugs.gentoo.org/692206#c30> の人が必要な変更を抽出してくれているので、これを xorg-server-1.20.5 に当てる。

<https://github.com/LuoJinghua/gentoo-overlay/tree/master/x11-base/xorg-server/files> にある `xorg-server-1.21-*.patch` のような名前のパッチ計6つを `/etc/portage/patches/x11-base/xorg-server-1.20.5` に配置する。

```terminal
$ ls /etc/portage/patches/x11-base/xorg-server-1.20.5/ | cat
xorg-server-1.21-autobind-gpu.patch
xorg-server-1.21-multigpu-00-7f962c70.patch
xorg-server-1.21-multigpu-01-37a36a6b.patch
xorg-server-1.21-multigpu-02-8b67ec7c.patch
xorg-server-1.21-multigpu-03-56c0a71f.patch
xorg-server-1.21-multigpu-04-b4231d69.patch
```

### emerge

ビルドする。
xorg-server だけでなく、 USE flag を変更した他のパッケージもリビルドを忘れないこと。

```terminal
$ sudo emerge -1uv mesa xorg-server nvidia-drivers
```

この手順はうどんワールドでも何でもいい。
とにかく更新した use flag が反映されていればおｋ。
また、ビルド時にパッチが当たっていることも念のため確認しておいた方がいい。

```terminal
$ sudo emerge -1v xorg-server

These are the packages that would be merged, in order:

Calculating dependencies... done!
[ebuild   R    ] x11-base/xorg-server-1.20.5:0/1.20.5::gentoo  USE="glamor ipv6 libglvnd suid systemd udev wayland xorg -debug -dmx -doc (-elogind
) -kdrive -libressl -minimal (-selinux) -static-libs -unwind -xcsecurity -xephyr -xnest -xvfb" 0 KiB

Total: 1 package (1 reinstall), Size of downloads: 0 KiB


>>> Verifying ebuild manifests

>>> Emerging (1 of 1) x11-base/xorg-server-1.20.5::gentoo
 * xorg-server-1.20.5.tar.bz2 BLAKE2B SHA512 size ;-) ...                                                                                  [ ok ]
>>> Unpacking source...
>>> Unpacking xorg-server-1.20.5.tar.bz2 to /var/tmp/portage_tmpfs/portage/x11-base/xorg-server-1.20.5/work
>>> Source unpacked in /var/tmp/portage_tmpfs/portage/x11-base/xorg-server-1.20.5/work
>>> Preparing source in /var/tmp/portage_tmpfs/portage/x11-base/xorg-server-1.20.5/work/xorg-server-1.20.5 ...
 * Applying xorg-server-1.20.4-shm-reindent-shm_tmpfile-to-follow-our-standards.patch ...                                                  [ ok ]
 * Applying xorg-server-1.20.4-shm-Pick-the-shm-dir-at-run-time-not-build-time.patch ...                                                   [ ok ]
 * Applying xorg-server-1.20.4-shm-Use-memfd_create-when-possible.patch ...                                                                [ ok ]
 * Applying xorg-server-1.12-unloadsubmodule.patch ...                                                                                     [ ok ]
 * Applying xorg-server-1.18-support-multiple-Files-sections.patch ...                                                                     [ ok ]
 * Applying xorg-server-1.21-autobind-gpu.patch ...                                                                                        [ ok ]
 * Applying xorg-server-1.21-multigpu-00-7f962c70.patch ...                                                                                [ ok ]
 * Applying xorg-server-1.21-multigpu-01-37a36a6b.patch ...                                                                                [ ok ]
 * Applying xorg-server-1.21-multigpu-02-8b67ec7c.patch ...                                                                                [ ok ]
 * Applying xorg-server-1.21-multigpu-03-56c0a71f.patch ...                                                                                [ ok ]
 * Applying xorg-server-1.21-multigpu-04-b4231d69.patch ...                                                                                [ ok ]
 * User patches applied.
(後略)
```

## 3. Xorg の設定

```conf:/etc/X11/xorg.conf.d/60-nvidia-offload.conf
# See <http://us.download.nvidia.com/XFree86/Linux-x86_64/435.21/README/primerenderoffload.html>.
Section "ServerLayout"
	Identifier "layout"
	Option "AllowNVIDIAGPUScreens"
EndSection
```

## 4. 再起動と確認

再起動したのち X を起動し、以下のことを確認する。
全てで問題がなければ、 PRIME render offload が利用可能のはずである。

### glxinfo
```terminal:成功例
$ __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia glxinfo | grep vendor
server glx vendor string: NVIDIA Corporation
client glx vendor string: NVIDIA Corporation
OpenGL vendor string: NVIDIA Corporation
```

ベンダーが `NVIDIA Corporation` になっていればおｋ。

```terminal:失敗例
$ __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia glxinfo | grep vendor
X Error of failed request:  BadValue (integer parameter out of range for operation)
(後略)
```

`BadValue (integer parameter out of range for operation)` などと言われた場合、おそらく `x11-drivers/nvidia-drivers` の `kms` USE flag が有効化されていない。

### xrandr

```terminal:成功例
$ xrandr --listproviders | grep NVIDIA
Provider 1: id: 0x1b8 cap: 0x0 crtcs: 0 outputs: 0 associated providers: 0 name:NVIDIA-G0
```

`name:NVIDIA-G0` のような provider が含まれていればおｋ。

### X のログ

```terminal:成功例
$ grep 'NVIDIA(G0)' /var/log/Xorg.0.log | head -1
[    23.123] (==) NVIDIA(G0): Depth 24, (==) framebuffer bpp 32
```

`NVIDIA(G0)` のような行が含まれていればおｋ。

```terminal:成功例
$ grep PRIME /var/log/Xorg.0.log
[    23.161] (II) NVIDIA: The X server supports PRIME Render Offload.
```

`The X server supports PRIME Render Offload.` という行が含まれていればおｋ。

もし "The X server **does not** supports ..." (強調は筆者) となっていたら、 X サーバ側に必要なパッチが当たっていない。
(参考: [PRIME offload not working - NVIDIA Developer Forums](https://devtalk.nvidia.com/default/topic/1061404/#5375004))

# 利用方法

基本的には [Chapter 35. PRIME Render Offload](http://us.download.nvidia.com/XFree86/Linux-x86_64/435.21/README/primerenderoffload.html) で紹介されている通り。

OpenGL (GLX) のアプリケーションなら、環境変数2つを `__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia` のように設定する。

```terminal
$ glxinfo | grep vendor
server glx vendor string: SGI
client glx vendor string: Mesa Project and SGI
OpenGL vendor string: Intel Open Source Technology Center
$ __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia glxinfo | grep vendor
server glx vendor string: NVIDIA Corporation
client glx vendor string: NVIDIA Corporation
OpenGL vendor string: NVIDIA Corporation
$
```

EGL や Vulkan なら、環境変数は `__NV_PRIME_RENDER_OFFLOAD=1` だけで良い。

```terminal
$ __NV_PRIME_RENDER_OFFLOAD=1 vkcube
(キューブが回転する)
```

(vkcube は `dev-util/vulkan-tools` で `cube` USE フラグを有効化するとインストールできる。)

Vulkan アプリケーションで `__GLX_VENDOR_LIBRARY_NAME=nvidia` を付けていても特に問題は見られないので、必要かわからない場合はとりあえず両方とも付けておけばよさそう。

もし上のようなコマンドが失敗したり vkcube がクラッシュするようなら、 `x11-drivers/nvidia-drivers` の `compat` USE フラグがちゃんと無効化されているか確認すること。
これが有効のままだと、 libpthread あたりでクラッシュして coredump する。

# 他の話題
## EGL
~~OpenGL は GLX のみの対応で、 EGL は今のところ (435.21) 未対応っぽい。~~

> NVIDIA's EGL implementation does not yet support PRIME render offload.
>
> ―― <http://us.download.nvidia.com/XFree86/Linux-x86_64/435.21/README/primerenderoffload.html>

**更新 2019-11-22**: EGL 対応

440.31 のドキュメントを確認したところ、 EGL にも対応した模様。
EGL を使う場合にも、 `__NV_PRIME_RENDER_OFFLOAD=1` のみの設定でオフロードできるらしい。

> To configure a graphics application to be offloaded to the NVIDIA GPU screen, set the environment variable `__NV_PRIME_RENDER_OFFLOAD` to `1`. If the graphics application uses Vulkan or EGL, that should be all that is needed.

確認してみよう。

```terminal:オフロードした場合
$ __NV_PRIME_RENDER_OFFLOAD=1 eglinfo | grep -A 2 'platform:'
GBM platform:
EGL API version: 1.4
EGL vendor string: Mesa Project
--
Wayland platform:
eglinfo: eglInitialize failed

X11 platform:
EGL API version: 1.5
EGL vendor string: NVIDIA
--
Device platform:
EGL API version: 1.5
EGL vendor string: NVIDIA
```

```terminal:オフロードしない場合
$ eglinfo | grep -A 2 'platform:'
GBM platform:
EGL API version: 1.4
EGL vendor string: Mesa Project
--
Wayland platform:
eglinfo: eglInitialize failed

X11 platform:
EGL API version: 1.4
EGL vendor string: Mesa Project
--
Device platform:
EGL API version: 1.5
EGL vendor string: NVIDIA
```

## `DRI_PRIME`
PRIME は本来、環境変数 `DRI_PRIME=1` で有効化できるはずなのだが、どうもこれが (現状の私の環境だと) 動かないっぽい。

```terminal
$ DRI_PRIME=1 glxinfo | grep vendor
libGL error: MESA-LOADER: failed to open nouveau (search paths /usr/lib64/dri)
libGL error: failed to load driver: nouveau
server glx vendor string: SGI
client glx vendor string: Mesa Project and SGI
OpenGL vendor string: Intel Open Source Technology Center
$
```

どこに原因があるのかはわからないが、とにかく動かない。

これが問題になるのは、たとえば [lutris](https://lutris.net/) のランチャーの "System options" で "Use discrete graphics" などを有効化すると `DRI_PRIME=1` でゲームを起動してくれるなどの機能があり、これが意図した通りにディスクリート GPU を使ってくれないということである。
仕方ないので、この場合は同じく "System options" の "Environment variables" から2つの環境変数を手動で設定してやるなどする必要がある。

![screenshot-2019-10-17-021319+0900.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106067/6b6e1fef-b3b4-7cbf-857b-02f6285675bf.png)


# 参考
* [Bumblebee (optirun, primusrun) doesn't work with Flatpak · Issue #869 · flatpak/flatpak](https://github.com/flatpak/flatpak/issues/869#issuecomment-542312708)
* [Chapter 35. PRIME Render Offload](http://us.download.nvidia.com/XFree86/Linux-x86_64/435.21/README/primerenderoffload.html)
* [692206 – [Tracker] Unmasking libglvnd USE flag](https://bugs.gentoo.org/692206)
* [PRIME offload not working - NVIDIA Developer Forums](https://devtalk.nvidia.com/default/topic/1061404/#5375004)

# あとがき: [--my-next-gpu-wont-be-nvidia](https://github.com/swaywm/sway/blob/d19f4f7bf866660d2199cb726bc3708eb42f98dd/sway/main.c#L98-L101)

> If the nvidia module is loaded on your system, you are not permitted to file bugs of any sort.
>
> ―― <https://github.com/swaywm/sway/issues/3039#issuecomment-434663746>