Windows on Linux で快適ゲーム生活 (original) (raw)

これは天久保 Advent Calendar 2023の6日目の記事です(遅刻。

天久保には居住していませんが「天久保」は町域とは関係ありませんので書きます。

Linux Gaming とその方法

Linux 環境でゲームをプレイすることは「どのように Linux 上で Windows のプログラムを動作させるか?」という問題と同じと言ってもよいでしょう*1。 なぜならほとんどの PC 向けのゲームは Windows のみにしかリリースされないからです*2

この互換性問題を解決するため、大きく分けて2つのアプローチが存在します。

  1. Windows API の互換レイヤーを用いて動作させる
  2. Linux 上で Windows仮想マシンとして起動させ、その上でプログラムを動かす*3

1 の互換レイヤーを用いた方法としては Wine やその Valve によるフォークである Proton が有力でしょう。 特に Proton は発展がめざましく、DirectX を Vulkan に変換する互換レイヤ*4などの恩恵もあり多くのゲームが快適に動作するようです*5

2 の仮想マシンを用いた方法は単純ではありますが本物の実装を用いるため(仮想マシンであることに気付かれないようにすれば)強力な方法です。

Proton と挫折

筆者も近年まではゲームを遊びたいときは Proton を用いていましたが、Wine であることを検知して動作を止めるゲーム*6や、演出としてウィンドウを操作するようなゲームがプレイできないなどの問題を抱えていました。 そんな状態で決定打となったのが Meta Quest2 *7を購入したことです。Linux における Quest 向け VR コンポジタの実装としては ALVR が存在しますが、当時(2020Q4)はまともに動作しなかった上、Linux における AMDGPU の動画エンコード支援の品質が信用できないものであったため、「Linux ネイティブ」の夢は諦めることになりました。

環境構築

材料紹介

PCI パススルーを用いて GPU を完全に Windows ゲストに渡すため、GPU は2つ必要です*8。 この機能を利用するために CPU は IOMMU*9を持つものが必要です。マザーボードも対応している必要がありますが、どちらも最近のものなら問題ないでしょう。 メモリは割り当てた分は仮想マシンを起動している間持っていかれるので多く積まれているべきです*10。 CPU コアも数があると十分に割り当てても余るのであるとうれしいですがオーバーコミットできるのでメモリほどは問題になりません。GPU は画面出力がないと動作してくれません。これを解決するのにダミープラグが便利です*11。もし無い場合はそこらへんに転がっているモニタの入力端子を使ってください。

仮想マシンの構築

ここで構築!といきたいところですが、PCI パススルーを用いた仮想マシンの構築方法についての情報はそこそこあるので紹介程度に留めておきます(基本はやるだけなので)。 いくつかハマりそうなところを書いておくとこんな感じ:

ちなみに筆者は AMD CPU + AMD GPU のときに Resizable BAR が有効だと VM を起動しようとした瞬間にシステムがクラッシュする問題にハマりました。お前が始めた物語やろがい*12

PCI パススルーに成功すると Windows はパススルーした GPU の画面を認識してそちらもデスクトップとして使うようになります。virt-manager のようなクライアントからだとこの画面を操作することができないため、GPU をパススルーする前に Windows をセットアップしておき、Parsecのようなリモートデスクトップを設定しておくと便利です。

Looking Glass を導入する

このまま Parsec を使い続けてもよいのですが、やはりほとんどのリソースを共有しているのにも関わらずネットワークを経由して接続されるのが気になります。 そこで Looking Glass を用いて Windows デスクトップを操作するようにします。VM とホストの間でメモリを共有することで超低遅延な画面共有を実現するらしいです*13。すごいですね。 クライアント自体にキーボード、マウス、クリップボードと音声を共有する機能も付いているのでこだわりがなければこれだけで事は足ります。遅延にこだわる場合は適当にデバイスをパススルーしてあげましょう。 例によってドキュメントがあるのでこれを参照して設定すればなんとかなります。共有メモリの量の計算が面倒なら 128MB とか書いておけばいいんです!*14

遊ぶ

この状態でも多くのゲームが遊べる状態ですが、アンチチートに引っ掛かって起動しないことがままあります。 この内 Easy Anti Cheat についてはなぜか VRChat が回避方法を書いています。 hyperv においてvendor_idを(適当に)指定し、sysinfodmidecodeを使って得た値を使って適切に設定することで回避できるらしいです。そうなんだ。

ファイルを共有する

QEMU / libvirt では virtio-fs を用いてゲストとホストの間でファイルシステムを共有できます。 しかしホスト側のデーモンである virtiofsd に VM との共有メモリを利用している場合に動作しないバグが存在しているため共有に失敗してしまいます。 歴史的経緯により virtiofsd には現在使われている Rust による実装と QEMU に存在した C による実装(以下 virtiofsd-qemu) があり、QEMU の実装では共有メモリを利用していてもファイル共有ができたようです。 virtiofsd-qemu は QEMU8 以降には含まれないため、QEMU7 をビルドします。

$ curl -LO https://download.qemu.org/qemu-7.2.6.tar.xz && tar -xf qemu-7.2.6.tar.xz ... $ cd qemu-7.2.6 && mkdir build && cd build ... $ ../configure --disable-strip
--disable-docs
--disable-gettext
--disable-sparse
--disable-guest-agent
--disable-guest-agent-msi
--disable-qga-vss
--disable-hax
--disable-whpx
--disable-hvf
--disable-nvmm
--disable-xen
--disable-vfio-user-server
--disable-dbus-display
--enable-tools
--enable-virtiofsd
--localstatedir=/var
--sysconfdir=/etc
--meson=meson
--enable-seccomp
--disable-tcg
--disable-kvm
--disable-gio
--disable-cfi
--disable-tpm
--disable-keyring
--disable-spice
--disable-spice-protocol
--disable-u2f
--disable-netmap
--disable-vde
--disable-vmnet
--disable-vnc
--disable-vhost-kernel
--disable-vhost-net
--disable-vhost-crypto
--disable-vhost-user-blk-server
--disable-virtfs
--disable-bochs
--disable-cloop
--disable-dmg
--disable-qcow1
--disable-vdi
--disable-vvfat
--disable-qed
--disable-parallels ... $ make ...

ビルドが成功したら./build/tools/virtiofsd/virtiofsdにバイナリができているはずです。これを適当な位置に配置します。virt-manager を使って virtiofs デバイスを追加した場合、XML は以下のようになっているはずです。

この<filesystem>の中に<binary path="{virtiofsdのパス}"/>を追加します。 ここまででホスト側の設定は完了です。 次にゲスト側の設定をします。WinFspvirtio-win-guest-tools が入った状態でVioFsSvcサービスを起動します。 うまくいくとZ:ドライブが出現し、その中のファイルが見えるはずです。 筆者は systemd-homed の作る loopback なホームディレクトリ内のディレクトリをマウントしようとしたら失敗した*15のでそこそこハマりどころがありそうです。

逆WSL2

まとめ

いかがでしたか? ゲームに限らず、すぐに使える Windows 環境を持っておくと役に立つこともあるはずです。 年の瀬は Linux Gaming でのんびりというのも悪くないんじゃないでしょうか。

明日(今日)は azarashi さんです。