X Window Systemを使ってリモートサーバでOpenGLなプログラムを走らせる方法

動機

SSHでログインしたリモートサーバ(SSHサーバ)上でOpenGLによるGUIアプリケーションを動かし、ローカルコンピュータ(SSHクライアント)の画面に表示したい。ときのメモ書き。

TL; DR

  • SSHクライアント(Xサーバ)側で、X起動時に+iglxオプション。
  • ssh -XYでログインする。
  • ローカルとリモートでlibGL.soの系列を揃える必要がある。ローカルがmesaならリモートもmesaに合わせる。LD_PRELOAD環境変数を使う。

参考

基本事項

X Window System

『ビットマップディスプレイ上でウィンドウシステムを提供する表示プロトコル』(wikipediaより引用)。GUIを構築するための基本的なツールキットを提供する。実際に絵が表示される画面を管理する「Xサーバ」をデーモンとして実行し、各GUIプログラム(GnomeやAwesomeWMのようなウィンドウマネージャやその他のGUIアプリケーションなど)は「Xクライアント」となってXサーバに描画命令を送信する。Xサーバ・Xクライアント間の通信プロトコルX Window Systemの一部として規定されている。

LinuxGUIマシンとして用いる場合、普通はX Window Systemのリファレンス実装であるXorgを使っていると思って差し支えない(2018/01現在。今後Waylandとか新しいのが主流になるかもしれないがここでは割愛)。

なお、XサーバとXクライアントは別のマシンで動いていても良い。ただし、ローカルコンピュータ(SSHクライアント)からリモートサーバ(SSHサーバ)にアクセスし、リモートサーバ上で実行したGUIアプリケーションの画面をローカルコンピュータに表示する場合、X Window Systemの観点から見るとローカルコンピュータで「Xサーバ」デーモンが実行されており、リモートサーバ上で動かすGUIアプリケーションが「Xクライアント」となってこのXサーバに接続することになる。SSHサーバとSSHクライアントの関係とは逆向きなので混乱しないよう注意。

OpenGL

2D/3DのコンピュータグラフィックスAPI。描画のコア機能はソフトウェアライブラリではなく対応するGPU上にハードウェア実装されるが、一般に「OpenGL」と呼ぶときはこのハードウェア実装をソフトウェアから利用するためのAPI群を指す。

GLX

Xサーバが動いているものと同じマシンでOpenGLアプリケーションを実行するとき、このアプリケーションはXクライアントとしてウィンドウの「枠だけ」はXサーバに管理してもらいつつ*1、その「枠」の中に表示する絵(つまり3D CG)はGPUに直接(X Window Systemを介さず)描画命令を送ってレンダリングする。これをdirect renderingと呼ぶ。

ここで、XサーバとOpenGLアプリケーション(Xクライアント)が異なるコンピュータで動いている場合、当然OpenGLアプリケーションはXサーバが動いているマシンのGPUに直接アクセスすることはできず、Xの通信プロトコルの枠組みの中でGPUに描画命令を送る必要がある。このようにXを介してOpenGLの描画命令を送ることをindirect renderingと呼び、このindirect renderingを実現するためにXの通信プロトコルを拡張したAPI定義がGLXである。

Xサーバ(ローカルコンピュータ)の設定

記事執筆時点で最新のXorgは1.19系列だが、Xorg1.17以降、Xサーバと別のマシンで動くXクライアントからのGLXアクセスはデフォルトで拒否するようになっているので、これを許可する。これは、Xサーバが動いているローカルコンピュータ(つまりSSHクライアント)で、X(/usr/bin/Xorg)を起動する際に+iglxオプションをつけることで実現できる。

ローカルコンピュータを起動する際にどこでXが起動されているかは各マシンの設定に依るので統一的なことは言えないが、多くの環境ではlightdmやgdmのようなdisplay managerがXを起動する責任を持つ。筆者はubuntuとarchlinuxを利用しており、いずれの環境でもlightdmをdisplay managerとして用いている(ubuntuではlightdmがデフォルト)ため、例としてlightdmの設定を示す。

Ubuntuの場合、/etc/lightdm/lightdm.conf.d/以下に50-xserver-command.confファイルを作り、以下の2行を指定する。

[SeatDefaults]
xserver-command=X -core +iglx

Archlinuxでpacman経由でlightdmをインストールした場合、/etc/lightdm/lightdm.confにすでにxserver-commandプレースホルダが用意されているため、これを編集すれば良い。

リモートサーバに接続

SSHのコネクションの中でXの通信プロトコルを扱えるようにするX11 Forwardingを用いてリモートサーバに接続する。ssh -XYコマンドオプションを使うか、$HOME/.ssh/configForwadX11 yesおよびForwardX11Trusted yesを指定する。

なお、一般にSSHサーバ上でGUIアプリケーションを動かすためにはForwardX11 yesつまりssh -Xだけで十分であるが、ForwardX11はXの通信プロトコルの一部を無効化する。これはXがキー入力などの情報も扱っているため、Xクライアントに悪意がある場合はXサーバが動いているマシンの機密情報を盗まれてしまう危険があるためだが、このとき無効化されるものの中にGLXも含まれる。 したがってGLXを使えるようにするためには、SSHサーバ上で動くXクライアントを「信頼」する必要があり、このためのオプションがForwardX11Trusted yesつまりssh -Yコマンドオプションである*2

別解(非推奨)

/usr/bin/Xorgを起動する際、-listen tcpオプションをつけることでSSHとは別にX専用のTCPコネクションを張ることができる。しかし、このためにはXサーバの該当ポートはファイヤウォールに穴を開ける必要があり、XサーバとXクライアントの認証もxauthを使って独自に管理する必要があるなど、手間とセキュリティリスクが大きいため、本稿では推奨しない。ただし、SSHの暗号化プロセスなどをすべてスキップすることによりパフォーマンスは最大化されるため、よほど性能要求が厳しい場合には検討しても良いかもしれないが、得られる性能向上はそこまで大きくない。

OpenGLアプリケーションの実行

以上の手順を踏んでリモートサーバでOpenGLアプリケーションを実行すれば、ローカルコンピュータの画面にその結果を表示することができるはずである。

しかし、場合によっては以下のようなエラーによりアプリケーションがうまく起動しないことがある(glxgearsは歯車が回転するアニメーションのOpenGLのデモプログラム)。

$ glxgears
X Error of failed request:  255
  Major opcode of failed request:  155 (GLX)
  Minor opcode of failed request:  1 (X_GLXRender)
  Serial number of failed request:  898
  Current serial number in output stream:  1064

これは様々な理由によって起こるため一概に原因を示すことができないが、よくある原因として「ローカルとリモートでlibGL.soの中身が違う」というものがある。libGL.soは一般にGPUドライバの一部としてパッケージされており、APIOpenGLの規格に従って整備されているが、内部の実装はGPUベンダまたはドライバの配布元によってバラバラである。

一例として、nVidiaGeForceを積んだリモートサーバ(nVidiaプロプライエタリなドライバが入っている)に、intelのCPU統合グラフィックスしかないローカルマシン(mesaのオープンソースドライバで動いている)で接続したとしよう。このとき、リモートサーバで動かすOpenGLアプリケーションはnVidiaのドライバに含まれるlibGL.soをリンクして使用するが、nVidialibGL.soによって生成されるGLXのコマンドをローカルのXサーバに送っても、ローカルのXサーバがGPUを駆動するために利用しているmesaのlibGL.soはこのGLXコマンドを解釈できず、上記のようなエラーとなる。

これを解決するためには、リモートで動かすOpenGLアプリケーションが「mesaの」libGL.soを利用するように強制してやれば良い。これにはLD_PRELOAD環境変数を使う。つまり

export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/mesa/libGL.so.1.2.0
glxgears

のようにすることで正常に起動するようになると思う(LD_PRELOADの値は各自のローカルで動いているGPUやサーバで動いているOSの種類により異なる)。ただし、描画性能は壊滅的である場合が多い。現代的なGPUを積んだマシンでglxgearsを実行すると数万FPSに達することもあるそうだが、この方法でではせいぜい5FPS程度になる。これはどうしようもないので、解決したければサーバに積んであるものと同じベンダのGPUを買うしかない。

*1:OpenGLアプリケーションをフルスクリーンで実行する場合はウィンドウ枠の管理も不要になるので一旦Xを無効化してGPUレンダリング結果だけを直接画面に送るのか?この辺は勉強不足でよくわかっていない

*2:このときssh -Yは「Xクライアントを信頼する」だけであり、実際に「Xプロトコルをforwardする」という意味は含まない。したがって-Xなしで-Yだけ指定してもGUIアプリケーションを動かせないことに注意