広く使われているマシンエミュレーターであるQEMUには、3Dアクセラレーションに対応した仮想GPUを作る機能が存在します。そこで今回はこの機能を使って、ゲストマシン上でOpenGLベンチマークツールを動かしてみましょう。
QEMU/KVMと3D GPU
QEMU/KVMはLinux用に広く使われているマシンエミュレーターです。たとえば仮想マシン管理ツールであるlibvirt/virt-managerや、libvirtのGNOME製フロントエンドであり第572回「GNOME Boxesを使用する」 でも紹介されている「GNOME Boxes」は、QEMU/KVMをバックエンドとして利用しています。同様に第590回「Windows/macOS/Linuxで使える仮想マシン管理ツール『multipass』 」 で紹介したmultipassも、Linux版のバックエンドはQEMU/KVMです。
Virgil 3D プロジェクトによって、このQEMU/KVM上で3Dアクセラレーションに対応した仮想GPUを作成・利用できるようになりました。Virgil 3Dでは準仮想化デバイスのフレームワークである「virtio」を利用して仮想GPUを構築し、ゲストOSはMesaのvirglドライバーを利用してこの仮想GPU向けに描画命令を送ります。それに対してホスト上のQEMUはvirtio-gpuで受け取った命令を、virglrendererというコンポーネントがホスト上のGPUに対してOpenGL命令として発行するわけです。
Virgil 3Dプロジェクトの成果はすでにQEMUに取り込まれています。そしてUbuntu 19.04のQEMUにおいて、ゲストOSにおける3D GPU機能が有効化されたのです[1] 。これによりゲストOS上のUbuntuでも、それなりの速度で3Dアプリケーションが動作するようになりました。今回はその3D GPU機能を実際にQEMUのコマンドを実行しながら使ってみましょう。
[1] 19.10のリリースノート を読むと、まるで19.10から使えるようになったように見えるかもしれません。実はこのQEMU関連の文言はそっくりそのまま19.04のリリースノート にも掲載されています。Ubuntuのリリースノートはそういうことがよくあるのです。いずれにせよ、3D GPU対応は19.04でも確認できますが、カーネルがより新しい方が安定しているので、今回は19.10を使うことにします。
図1 ゲスト上でも3Dアプリケーションが動く
ちなみに性能はあくまで「それなり」です。完全なソフトウェアエミュレーションよりははるかにマシなので、Unity時代のUbuntuのような遅さは解消するものの[2] 、ホストと同じぐらいの性能が出るわけでもありません。
[2] UbuntuのデスクトップUIであったUnityは、あるバージョンから3Dアクセラレーション機能を必須とし、対応していないマシン上ではCPUによるエミュレーションを実施していました。結果として仮想マシン上のような3Dアクセラレーションが無効化されていて、CPUもそこまで速くない環境では、GNOME端末を起動するだけでも数秒のアニメーションを眺めさせられるといった、時間の使い方が贅沢なユーザー体験を提供していたのです。ちなみにここで言う「Unity」は、ゲームエンジン と何も関係ありません。
性能にこだわるならマシン上のGPUデバイスをPCIパススルーによって特定のホストに専有させたり、Intel GVT-gを用いてホストとゲストでGPUを共有する方法などを検討してください[3] 。今回紹介する方法は「ホスト側に特別な設定やハードウェアの準備を行うことなく、気軽にゲストOSでGPUアクセラレーションを動かせる」ことがメリットになります。
3D GPUを有効化する方法
さっそくQEMUを使って3D GPUを有効化してみましょう。まずあらかじめ、QEMU関連のパッケージをインストールしておきます。実施するホストマシンはUbuntu 19.04以上であればどれでも問題ありません。Ubuntu 19.10だとカーネルの更新により仮想化まわりの修正がいくつか入っているようなので、19.10のほうが良いでしょう。Ubuntu 18.04 LTSでUbuntu Cloud Archive のSteinかTrainを有効化するという手もあります。
$ sudo apt install qemu-system-x86 qemu-utils
またデスクトップ版のインストールイメージもダウンロードしておきます。
$ wget http://jp.releases.ubuntu.com/eoan/ubuntu-19.10-desktop-amd64.iso
こちらもUbuntu 19.10を使っていますが、少なくともvirtio-gpuに対応したMesa 11.1以降とLinux Kernel 4.4以降であれば、どのディストリビューションでも動くはずではあります。ただしUbuntuの場合、条件を満たす16.04(Kernel 4.4、Mesa 11.2)では起動の途中でエラーとなり、16.04.2(Kernel 4.8、Mesa 12.0)では若干不安定で、16.04.3(Kernel 4.10、Mesa 17.0)以降で安定するという状況でした。
次に仮想マシン用のイメージを作成しておいてください。ここではサイズを20GBにしていますが、Ubuntuをインストールできるならどのサイズでもかまいません。
$ qemu-img create -f qcow2 ubuntu.qcow2 20G
では、さっそく仮想の3D GPUを有効化した仮想マシンを立ち上げてみましょう。
$ qemu-system-x86_64 \
-enable-kvm -M q35 -smp 2 -m 4G \
-drive format=qcow2,file=ubuntu.qcow2,if=virtio \
-net nic,model=virtio \
-net user,hostfwd=tcp::2222-:22 \
-vga virtio \
-display gtk,grab-on-hover=on,gl=on \
-boot once=d -no-reboot -cdrom ubuntu-19.10-desktop-amd64.iso
それぞれのオプションは次のとおりです。
-enable-kvm
:CPUの仮想化支援機能を有効化します。
-M q35
:マシンタイプをICH9なQ35 チップセットにします。
-smp 2
:CPUの数を2にしています。いくつでもかまいません。
-m 4G
:メモリのサイズを4GBにしています。Ubuntuデスクトップが動くならいくつでもかまいません。
-drive format=qcow2,file=ubuntu.qcow2,if=virtio
:ディスクイメージを指定しています。こちらもvirtioフレームワークを利用しています。
-net nic,model=virtio
:NICもvirtioを利用します。ディスクも含めて、使用しなくても3D GPUの利用には問題ありません。
-net user,hostfwd=tcp::2222-:22
:ホストの2222番ポートがゲストの22番ポートに転送されるための設定です。SSHでホストからゲストにアクセスしたい際に便利です。
-vga virtio
:VGAカードのエミュレーションとしてvirtioを指定しています。今回のキモその1です 。
-display gtk,grab-on-hover=on,gl=on
:仮想マシンの画面を表示するウィンドウとしてGTKを指定しています。今回のキモその2です 。
-boot once=d
:1度だけCD-ROMドライブから起動するよう指定しています。
-no-reboot
:ゲストを再起動した時、再起動せずに終了します。インストール時に便利なオプションです。
-cdrom ubuntu-19.10-desktop-amd64.iso
:CD-ROMドライブ用イメージを指定しています。
上記を読めばわかるように、今回のポイントは「-vga virtio -display gtk,grab-on-hover=on,gl=on
」の部分です。
「-vga
」にてvirtioを指定します。結果としてQXLは使えません。一応SPICEでvirtio-gpuにアクセスできるようですが、あくまでローカルマシン内部に完結する必要があります 。リモートデスクトップでVirgil 3Dをサポートする案もあるものの、現状どうなっているかは不明です。
「-display
」はgtkがsdlのいずれかを指定します。ただしUbuntuのパッケージリポジトリにあるQEMUの場合、古くなったSDL1.xがアーカイブから削除されたものの、SDL2がまだ安定していなかったためにSDLのサポートを切っています 。よって使えるのは事実上GTKだけです[4] 。
[4] この判断が下されたのがおおよそ2018年ぐらいなので、20.04では状況が変わるかもしれません。ただし、まずはSDL2をmainコンポーネントに格上げする必要があります。どうしてもSDLを使いたいなら、snap版のqemu-virgil を使うという手もあります。
しかしながらGTKディスプレイには「マウスカーソルが表示されない 」という不具合が存在します。これは要するに「Alt-Ctrl-G」でマウスイベントをゲストウィンドウ内部に「つかんだ」時に、カーソルが表示されなくなるという話です。実際にはマウスイベントは届いていますので、クリック等は動作します。この不具合に対する回避策として指定しているのが「grab-on-hover=on
」です。このオプションを指定すると「Alt-Ctrl-G」で明示的につかむ状態にしなくても、ホストのマウスカーソルをQEMUのウィンドウ上にもってくるだけで、つかんだ場合と同じ状態にできます。このときのマウスカーソルの描画はホストが担当するので、消えることもありません。もちろんつかんだ状態なので、Alt-Tabのようなキーイベントもゲスト内部にのみ届きます。
最後に指定している「gl=on
」が3Dアクセラレーションの有効化です。これを指定した場合のみ、ゲスト内部で3D GPUを利用できます。
オプションを把握したところで、とりあえずUbuntuをインストールしましょう。Ubuntuのインストールが完了したら再起動します。「 -no-reboot
」オプションを指定していたらQEMUプロセスが終了しますので、次のようにインストール用に指定していたオプションを外して「再起動」してください。
$ qemu-system-x86_64 \
-enable-kvm -M q35 -smp 2 -m 4G \
-drive format=qcow2,file=ubuntu.qcow2,if=virtio \
-net nic,model=virtio \
-net user,hostfwd=tcp::2222-:22 \
-vga virtio \
-display gtk,grab-on-hover=on,gl=on
ちなみに上記はBIOSモードで起動しています。第441回「QEMU/KVMでUEFIファームウェアを使う」 で詳しく紹介したOVMFを利用したUEFIモードで起動した場合も、3D GPUを利用可能です。ただし、Ubuntu 19.10のOVMFイメージはVGAを利用できない問題 が存在します。具体的には起動時にディスプレイ画面に「Guest has not initialized the display (yet).」と表示されるのです。問題となるのは「/usr/share/ovmf/OVMF.fd」だけです。「 /usr/share/OVMF/OVMF_CODE.fd」と「/usr/share/OVMF/OVMF_VARS.fd」は問題ありませんので、そちらを使うようにしてください。2020年4月にリリース予定のUbuntu 20.04 LTSでは修正されている見込みです。
実際にUEFIモードで起動するには、たとえばインストール時は次のように「-drive
」オプションをpflash用に2行追加します。なお、BIOSモードでインストールした仮想マシンイメージをUEFIモードで起動することはできませんので、UEFIで利用したいならあらためてインストールし直しましょう。
$ cp /usr/share/OVMF/OVMF_VARS.fd vars.fd
$ qemu-system-x86_64 \
-enable-kvm -M q35 -smp 2 -m 4G \
-drive if=pflash,format=raw,readonly,file=/usr/share/OVMF/OVMF_CODE.fd \
-drive if=pflash,format=raw,file=vars.fd \
-drive format=qcow2,file=ubuntu.qcow2,if=virtio \
-net nic,model=virtio \
-net user,hostfwd=tcp::2222-:22 \
-vga virtio \
-display gtk,grab-on-hover=on,gl=on \
-boot once=d -no-reboot -cdrom ubuntu-19.10-desktop-amd64.iso
3D GPUの動作確認
3D GPUを有効化したインスタンスを起動したら、次は3Dアクセラレーションが効いているか確認しましょう。まず最初に確認すべきはdmesg
の結果です。
$ dmesg | grep drm
[ 2.291108] [drm] pci: virtio-vga detected at 0000:00:01.0
[ 2.291113] fb0: switching to virtiodrmfb from VESA VGA
[ 2.305099] [drm] virgl 3d acceleration enabled
[ 2.309482] [drm] number of scanouts: 1
[ 2.309487] [drm] number of cap sets: 2
[ 2.320084] [drm] cap set 0: id 1, max-version 1, max-size 308
[ 2.320204] [drm] cap set 1: id 2, max-version 2, max-size 556
[ 2.320660] [drm] Initialized virtio_gpu 0.1.0 0 for virtio0 on minor 0
[ 2.327230] virtio_gpu virtio0: fb0: virtio_gpudrmfb frame buffer device
ポイントは上記の「virgl 3d acceleration enabled」です。これによりvirglドライバーを使っていて、なおかつ3Dアクセラレーションが動いていることがわかります。
ちなみにQEMUによるインスタンス起動時に「-display
」オプションから「gl=on
」を外して起動すると、次のようになります。
[ 2.305099] [drm] virgl 3d acceleration not supported by host
「-vga virtio
」を指定しているためにvirglドライバーが使われている点は一緒ですが、3Dアクセラレーションは無効化されていますね。「 -vga qxl
」などに変更すると、virglではなくqxlドライバーが使われるようになります。
次にOpenGL関連の動作確認では定番のglxXXXなコマンドを実行してみましょう。あらかじめmesa-utilsパッケージをインストールしておきます。
$ sudo apt install mesa-utils
まずはグラフィックスデバイスの情報を表示するglxinfo
コマンドの実行です。かなりいろいろな情報が表示されますが、今回はOpenGL関連の情報だけに限定して表示します。
$ glxinfo | grep OpenGL
OpenGL vendor string: Red Hat
OpenGL renderer string: virgl
OpenGL core profile version string: 4.3 (Core Profile) Mesa 19.2.1
OpenGL core profile shading language version string: 4.30
OpenGL core profile context flags: (none)
OpenGL core profile profile mask: core profile
OpenGL core profile extensions:
OpenGL version string: 3.1 Mesa 19.2.1
OpenGL shading language version string: 1.40
OpenGL context flags: (none)
OpenGL extensions:
OpenGL ES profile version string: OpenGL ES 3.1 Mesa 19.2.1
OpenGL ES profile shading language version string: OpenGL ES GLSL ES 3.10
OpenGL ES profile extensions:
ポイントは「renderer string」です。ここでvirglが使われていることがわかります。「 gl=on
」を外して起動するとvirglではなくllvmpipeとなります。さらにllvmpipeのとき、別の行では「Acceleraed: no」と表示されます。つまりソフトウェアレンダラーが使われるのです。
Extended renderer info (GLX_MESA_query_renderer):
Vendor: VMware, Inc. (0xffffffff)
Device: llvmpipe (LLVM 9.0, 128 bits) (0xffffffff)
Version: 19.2.1
Accelerated: no
Video memory: 3936MB
(中略)
OpenGL renderer string: llvmpipe (LLVM 9.0, 128 bits)
glxgears
コマンドでFPSを計測してみたところ、2400FPSから3500FPSで推移しました。「 gl=on
」なしだと2000FPSを超えることはなかったので、それなりに性能が改善しているようです。
図2 glxgearsコマンドは2400FPSから3500FPSの間を推移する
またglxgearsを動かしている間のCPUの負荷は高いものの、ずっと100%に張り付くというほどでもありません。ただしglxgears
実行中のCPU負荷そのものは、「 gl=on
」の有無で大きな違いはないようです。細かいところまで違いを見るとするなら、「 gl=on
」なしのほうがコンスタントに負荷が高く、「 gl=on
」ありだと負荷が下がることもある、ぐらいでしょうか。
図3 LLVMpipeだとCPU負荷はほぼ一定
ベンチマーク
最後に3D描画を利用したベンチマークを動かしてみましょう。今回は第524回「Hades Canyon/Kaby Lake GのdGPUを有効化する」 でも利用したUNIGINE Heaven のみを動かします。
第524回 ではPhoronix Test Suiteをインストールしましたが、今回はUNIGINE Heaven単体をインストールして実行する方法を紹介します。
$ wget https://assets.unigine.com/d/Unigine_Heaven-4.0.run
$ chmod +x Unigine_Heaven-4.0.run
$ ./Unigine_Heaven-4.0.run
Creating directory Unigine_Heaven-4.0
Verifying archive integrity... All good.
Uncompressing Unigine Heaven Benchmark.............................................................................
Unigine Heaven Benchmark installation is completed. Launch heaven to run it
ベンチマークの実行は次のとおりです。
$ cd Unigine_Heaven-4.0/
$ ./heaven
ウィンドウが開いたら「RUN」を選択し、画面左上の「Benchmark」をクリックするとベンチマークを開始します。QEMU上のフルスクリーンだとなぜかベンチマークが開始しなかったので、今回は最初のウィンドウで「Full screen」のチェックを外しています。
図4 QEMU上での実行結果
図5 ホスト上での実行結果
フルスクリーンではないため、QEMU上のほうが若干画面サイズが小さい状態ではありますが、FPSとスコアともにホストの6割ぐらいの性能が出ています。QEMU側はウィンドウを小さくすることで、さらに性能があがります。
ベンチマーク中のCPU使用率もそこまで高くなることはなく、またUNIGINE Heavenの描画がもたつくこともありませんでした。よってちょっとした3Dアプリケーションを動かすだけであれば十分な性能と言えるでしょう。
8月にリリースされたvirglrenderer 8.0ではさらに性能が改善しているそうです 。さらにGSoC 2018ではVulkan対応 がテーマとなっていたようです。もしかすると今後は「3DゲームはQEMUの中で動かす」ことが現実的になる時代が来るかもしれませんね。