bokumin.org

RSS Sitemap Mail
ritual-clearcut

FreeBSDのFirefoxで動画をGPUデコードする

How to enable GPU video decoding for Firefox on FreeBSD

とある日、FreeBSDのノートPCでネットサーフィンしていると、異様にカクつくことに気づきました。htopで確認したところ、以下のような出力が出ました。

$ htop

    0[|||||||||||||||||||||||||||||||100.0%] Tasks: 81, 0 thr, 20 kthr; 3 running
    1[|||||||||||||||||||||||||||||||100.0%] Load average: 9.26 8.53 7.77 
    2[|||||||||||||||||||||||||||||||100.0%] Uptime: 02:26:49
    3[|||||||||||||||||||||||||||||||100.0%]
  Mem[|||||||||||||||||||||     2.54G/11.8G]
  Swp[                             0K/4.00G]

  PID USER       PRI  NI  VIRT   RES S  CPU%▽MEM%   TIME+  Command
 2283 nshingu     11   0 1523M 1097M S 284.6  9.1  3h14:37 /usr/local/lib/firefox/firefox
 4081 nshingu    114   0 3042M  616M R  71.4  5.1  0:25.28 /usr/local/lib/firefox/firefox
 2270 nshingu     28   0 12.4G 1281M S  24.7 10.6 18:40.96 firefox
 2284 nshingu     16   0 3844M  786M S   8.6  6.5  9:54.35 /usr/local/lib/firefox/firefox
 2841 nshingu     12   0 2797M  425M S   5.3  3.5  9:51.49 /usr/local/lib/firefox/firefox

メモリ/スワップには余裕がありますが、Load Averageが9近くあり、4コアすべてがCPU 100%で張り付いている状態です。メモリ不足ではありません。
原因は、Firefoxで開いたまま忘れて放置していたNASAのページでした。

https://www.nasa.gov/aeronautics/nasa-releases-powerful-lava-software-to-us-aerospace-industry/

使用しているPCはWyse 5470というシンクライアント向けのPCなので、スペック的に仕方のないことだと思います。このPCは普段動画を見る目的で使っていないですが、動画などが含まれるページを開いただけでここまで負荷が上がってしまうのは困りものです。
そこで、少しでも軽くなればと思い、グラフィックス周りを見直すことにしました。

結論

先に結論から言うと、FreeBSD の Intel 内蔵GPU機で、次の2つを両方行うと改善しました。

# VA-API のドライバをインストール
sudo pkg install libva-utils libva-intel-media-driver

# video グループに追加する
sudo pw groupmod video -m nshingu

片方だけでは効かず、ソフトウェアデコード(CPU使用)のままでした。通常の描画は、この設定と関係なく最初からGPUで動いていました。
以下これらの検証です。参考にしてもらえれば幸いです。

環境

機種: Dell Wyse 5470 (シンクライアント向けPC / ファンレス)
OS: FreeBSD 14.5-RELEASE amd64
CPU: Intel Celeron N4100 (4) @ 1.094GHz
GPU: GeminiLake [UHD Graphics 600] (CPU内蔵GPU)
Mem: 12GB
WM: fvwm3
Firefox 157.0

ドライバ、パッケージ、グループの確認

まず、ドライバ・パッケージ・ユーザのグループを確認していきます。

$ kldstat | grep -E 'i915|drm'
 3    1 0xffffffff82ff9000   1e6220 i915kms.ko
 4    2 0xffffffff831e0000    87090 drm.ko

$ pkg info | grep -E 'drm|mesa'
drm-61-kmod-6.1.128.1405000_10 Direct Rendering Manager (DRM) GPU drivers
drm-kmod-20260508              Direct Rendering Manager (DRM) GPU drivers metaport
gpu-firmware-kmod-20260519,1   Firmware modules for the drm-kmod drivers
libdrm-2.4.133,1               Direct Rendering Manager library and headers
mesa-demos-9.0.0               OpenGL demos distributed with Mesa
mesa-dri-26.1.3                OpenGL hardware acceleration drivers for DRI2+
mesa-libs-26.1.3               OpenGL libraries that support GLX and EGL clients

$ id 
uid=1001(nshingu) gid=1001(nshingu) groups=1001(nshingu),5(operator),68(dialer)

i915kms.ko と drm.ko が出ているので、ドライバは読み込み済みでした。drm-kmod や mesa-dri も入っていて、パッケージにも問題はありません。
ただ、groups に 44(video) がありませんでした。
はじめは、これが原因でllvmpipe(CPUによるソフトウェア描画)にフォールバックしているのではないかと考えました。

video グループを疑ったのは、FreeBSD Forumsの次のスレッドを見つけたからです。

https://forums.freebsd.org/threads/graphics-support.72701/

このスレッドは2019年のもので、Intel内蔵GPUのマシンにFreeBSD 11.3とGNOME 3を入れたところ、GNOME Shellが「/dev/dri/cardN の読み書き権限が必要なので、ユーザーを video グループに追加してください」という趣旨のメッセージを出した、という相談です。症状は今回と違いますが、/dev/dri 以下のデバイスを開くには video グループが関係する、という点は共通しています。スレッドでは、次のような指摘もされていました。

  • drm-kmod を入れるだけでは足りず、i915kms を実際に読み込む必要がある
  • /dev 以下の権限は chmod 666 で済ませてはいけない(再起動で元に戻るうえ、誰でも書き込める状態になってしまう)

/dev/dri の権限

ls -l だけではシンボリックリンクしか見えないので、-L を付けて実体を確認します。

$ ls -lL /dev/dri/
total 0
crw-rw----  1 root video  0x7e Sep 29 17:17 card0
crw-rw----  1 root video 0x12d Sep 29 17:17 renderD128

$ [ -r /dev/dri/renderD128 ] && echo "renderD128: readable" || echo "renderD128: denied"
renderD128: denied

crw-rw---- root video は、root か video グループのメンバーしか開けないという意味です。当然、ユーザ(nshingu)は video に入っていないので、renderD128 は開けません。

Firefox のプロセスのグループ

$ procstat -s $(pgrep -o firefox)
  PID COMM              EUID  RUID SVUID  EGID  RGID SVGID UMASK FLAGS GROUPS         
 7021 firefox           1001  1001  1001  1001  1001  1001   022 -     1001,5,68

FirefoxのGROUPSにも 44 がない状態です。

Firefox が開いている /dev/dri

$ fstat | grep -E 'dri|drm'
nshingu  firefox     7021   15 /dev        126 crw-rw----   drm/0 rw
nshingu  firefox     7021   42 /dev        126 crw-rw----   drm/0 rw
nshingu  firefox     7021   43 /dev        126 crw-rw----   drm/0 rw
nshingu  firefox     7021   44 /dev        126 crw-rw----   drm/0 rw

開いているのは drm/0 (card0) だけで、drm/128 (renderD128) は開いていません。Xorg経由の描画は動いている感じです。

通常の描画はGPUで動いていた

videoグループがないので、GPUではなくCPUで描画しているのではと疑っていたので、glxinfo でも確認しました。

$ glxinfo | grep -E 'renderer|OpenGL version'
OpenGL renderer string: Mesa Intel(R) UHD Graphics 600 (GLK 2)
OpenGL version string: 4.6 (Compatibility Profile) Mesa 26.1.3

$ glxinfo | grep "direct rendering"
direct rendering: Yes

video グループに入っていなくても、Intel UHD 600・direct rendering: Yes と表示されました。つまり、glxinfo では video グループの有無を判定できません。Xorgが開いたGPUをアプリに渡しているのだと予想しています。

Firefoxのabout:supportページでGraphics欄を見ても、通常の描画は video グループがなくてもGPUで動いていました。

Compositing: WebRender  (Software が付かない)
WebGL 1 Driver Renderer: Intel -- Mesa Intel(R) UHD Graphics 600 (GLK 2)
WebGL 1 Driver Version: 4.6 (Core Profile) Mesa 26.1.3

ということで、llvmpipeにフォールバックしているから重いという最初の推測は誤りでした。負荷の原因は通常の描画ではなく、動画のデコード(VA-API)の方にあるようです。

VA-API のパッケージを入れる

VA-APIのドライバが何も入っていなかったので、VA-API関連のパッケージを入れていきます。

$ sudo pkg install libva-utils libva-intel-media-driver
The following 3 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
	gmmlib: 22.10.0 [FreeBSD]
	libva-intel-media-driver: 26.1.5 [FreeBSD]
	libva-utils: 2.24.0 [FreeBSD]

Number of packages to be installed: 3

The process will require 819 MiB more space.
142 MiB to be downloaded.

インストール後は、次のコマンドで確認できます。iHD_drv_video.so があれば、ドライバは入っています。

$ pkg info | grep -i libva
libva-2.24.1                   VAAPI wrapper and dummy driver
libva-intel-media-driver-26.1.5 VAAPI driver for Intel HD 5000 (Gen8) or newer
libva-utils-2.24.0             Collection of tests and utilities for VAAPI

$ ls /usr/local/lib/dri/
iHD_drv_video.so

ドライバはあるが video グループがない状態の vainfo

$ vainfo
Trying display: wayland
Trying display: x11
libva info: VA-API version 1.24.0
libva error: vaGetDriverNames() failed with unknown libva error
vaInitialize failed with error code -1 (unknown libva error),exit

$ vainfo --display drm --device /dev/dri/renderD128
Trying display: drm
Failed to open the given device!

ドライバが入っても、videoグループがないと vainfo は失敗します。videoグループが必要になるのは、renderD128 を直接開く処理だけです。
最初のエラーは「unknown libva error」としか表示されないので、ここから権限が原因だとは分かりません。DRMを直接指定したときのFailed to open the given deviceが、権限を疑う手がかりになります。

対処

# VA-API のドライバをインストール
sudo pkg install libva-utils libva-intel-media-driver

# video グループに追加する
sudo pw groupmod video -m nshingu

グループの変更は、すでに動いているプロセスには反映されません。一度ログアウトしてログインし直し、id コマンドで groups に 44 が含まれていればOKです。

修正後の確認

$ id
uid=1001(nshingu) gid=1001(nshingu) groups=1001(nshingu),5(operator),44(video),68(dialer)

$ pgrep firefox | xargs -n1 procstat -s
  PID COMM              EUID  RUID SVUID  EGID  RGID SVGID UMASK FLAGS GROUPS         
 5314 firefox           1001  1001  1001  1001  1001  1001   022 -     1001,5,44,68
 (子プロセスを含む全14プロセスで GROUPS に 44 あり)

$ [ -r /dev/dri/renderD128 ] && echo "renderD128: readable" || echo "renderD128: denied"
renderD128: readable

$ fstat | grep -E 'drm/128|renderD128'
nshingu  firefox     7396   15 /dev        301 crw-rw----  drm/128 rw
nshingu  firefox     7396   41 /dev        301 crw-rw----  drm/128 rw
nshingu  firefox     7411   35 /dev        301 crw-rw----  drm/128 rw
nshingu  firefox     7411   36 /dev        301 crw-rw----  drm/128 rw
 (以下略。7411 は計8本)

グループに 44 が入り、Firefoxが drm/128 (renderD128) も直接開くようになりました。
続いて vainfo です。

$ vainfo
libva info: Trying to open /usr/local/lib/dri/iHD_drv_video.so
libva info: Found init function __vaDriverInit_1_24
libva info: va_openDriver() returns 0
vainfo: Driver version: Intel iHD driver for Intel(R) Gen Graphics - 26.1.5 (intel-media-26.1.5)
      VAProfileH264Main               :	VAEntrypointVLD
      VAProfileH264High               :	VAEntrypointVLD
      VAProfileHEVCMain               :	VAEntrypointVLD
      VAProfileHEVCMain10             :	VAEntrypointVLD
      VAProfileVP8Version0_3          :	VAEntrypointVLD
      VAProfileVP9Profile0            :	VAEntrypointVLD
      VAProfileVP9Profile2            :	VAEntrypointVLD
      ...

デコードに対応しているのは、MPEG2、H.264、VC1、JPEG、VP8、HEVC (Main / Main10)、VP9 (Profile0 / 2) です。AV1は対応していません。この世代のGPUがAV1のデコードに対応していないためです。

Firefox 側の設定と判定

# about:config
media.hardware-video-decoding.enabled        true
media.hardware-video-decoding.force-enabled  false
media.hardware-video-decoding-vulkan.enabled false

# about:support の Decision Log
HARDWARE_VIDEO_DECODING          default  available
HW_DECODED_VIDEO_ZERO_COPY       default  available
HARDWARE_VIDEO_DECODING_VULKAN   user     disabled (pref を false にしているため)

ここに出ているのは使ってよいという判定であって、実際にGPUでデコードしている証拠ではないです。実際にGPUを使っているかどうかは、この後の top -H で、スレッド名を見て確認します。

動画の多いページでCPU負荷を比べる

以前張り付いたNASA LAVAの記事に、再度チャレンジしてみます。
条件を1つ変えるたびに、Firefoxを再起動してページを開き、1分後に top -H を確認しました。

# ドライバあり / video あり (修正後)
CPU:  6.0% user,  1.9% system, 91.9% idle
load averages: 0.77, 0.85, 1.00
 8052 firefox{WRRenderBackend  3.12%
 8052 firefox{Renderer}        2.85%
 (av: で始まるスレッドは上位に出ず)

# ドライバあり / video なし
CPU: 53.4% user,  6.6% system, 39.6% idle
 8396 firefox{av:hevc:d  33.79%
 8396 firefox{av:hevc:d  33.68%
 8396 firefox{av:hevc:d  33.67%
 (id に 44 なし / fstat は drm/0 のみで drm/128 なし)

# ドライバなし (pkg delete libva-intel-media-driver) / video あり
CPU: 27.4% user,  3.1% system, 69.0% idle
 9338 firefox{av:hevc:d  18.19%
 9338 firefox{av:hevc:d  17.83%
 9338 firefox{av:hevc:d  17.66%
 9338 firefox{av:hevc:d   7.26%
 9338 firefox{av:h264:d   2.69%
 9338 firefox{av:h264:d   2.47%
 
 
# ドライバなし / video なし (最初の張り付き状態)
CPU: 92.6% user,  4.0% system,  3.4% interrupt,  0.0% idle
 2313 firefox{av:hevc:df2}  28.56%
 2313 firefox{av:hevc:df0}  26.45%
 2313 firefox{av:h264:df0}  26.08%
 2313 firefox{av:h264:df2}  25.18%
 2313 firefox{av:h264:df1}  24.82%
 2313 firefox{av:hevc:df0}  24.74%
 (av: スレッドは hevc 6本 + h264 6本の計12本、合計で約294%)

ドライバと video の両方がそろった A だけが、GPUでデコードされているのが分かると思います。片方でも欠けると av: スレッド(ffmpegのソフトウェアデコード)が出てきます。CPU負荷を下げたいなら、両方入れる必要があります。
※ あくまで参考例です。動画のループ位置やタブの数でも数字は変わるので、大まかな傾向として見てもらえたらと思います。

なお、次の項目は、どの条件でも同じ表示でした。

glxinfo renderer      Intel UHD 600 
direct rendering      Yes 
about:support         WebRender, Intel 

まとめ

videoグループとVA-APIドライバの両方を入れることで、動画のハードウェアデコードが使えるようになり、CPU負荷が下がりました。片方だけでは、CPUデコードのままです。
通常の描画(WebRender / Intel UHD 600)は、videoグループやVA-APIドライバとは無関係に、最初からGPUで動いていました。
glxinfo や about:support のGraphics欄では、この問題は切り分けられません。id、fstat | grep drm/128、vainfo、top -H のスレッド名で確認するのが確実です。
CPU張り付きの原因をGPUが使われていないと考えてしまっていましたが、今回の場合は描画はGPU、動画デコードだけがCPUという状態でした。

これでYoutubeも快適に!という訳にはいかないですが、まあそこそこの状況にならいけるようになったのかなと思ってます。
今まで設定をしていなかった部分を設定してみたという話でした。

補足

i915kms.ko / drm.ko が読み込まれていない場合

パッケージをインストールします。drm-kmod はメタポートで、OSに合った drm-61-kmod などを選んでくれます。

pkg install drm-kmod gpu-firmware-kmod

sysrcコマンドで、起動時に読み込む設定をいれます。こうすることで、/etc/rc.conf に kld_list=”i915kms” が追記されます

sysrc kld_list+="i915kms"

手動で読み込ませて確認していきます。drm.ko は依存で自動的に読み込まれるのでそこは大丈夫です。

kldload i915kms
kldstat | grep -E 'i915|drm'

読み込みに失敗するときは、dmesgを見るようにすれば大丈夫です。多くは、カーネルとdrm-kmodのバージョン不一致が原因です。FreeBSDをアップグレードした直後で不具合がでれば、pkg upgrade で更新してください。パッケージで合わなければ、portsから graphics/drm-kmod を手動ビルドしましょう。

デバイスの権限とXorgのドライバ

crw-rw—- root video なら正常ですが、root wheel のままなら /etc/devfs.rules の設定が必要です。

ls -lL /dev/dri/

modesetting なら問題ありません。scfb になっていたら下の設定を書きます

grep -E 'Driver|scfb|modesetting|intel' /var/log/Xorg.0.log | head

xf86-video-intel は古めで、今回の私の環境の場合、Gemini Lakeなのでmodesetting が無難です

# /usr/local/etc/X11/xorg.conf.d/driver-intel.conf
Section "Device"
    Identifier "Card0"
    Driver     "modesetting"
EndSection

参考

FreeBSD Forums「Graphics support」
https://forums.freebsd.org/threads/graphics-support.72701/
第5章 X Window System | FreeBSD Documentation Portal
https://docs.freebsd.org/ja/books/handbook/x11/#x-config

おわり