X Window Systemを使ってリモートサーバでOpenGLなプログラムを走らせる方法
動機
SSHでログインしたリモートサーバ(SSHサーバ)上でOpenGLによるGUIアプリケーションを動かし、ローカルコンピュータ(SSHクライアント)の画面に表示したい。ときのメモ書き。
TL; DR
- SSHクライアント(Xサーバ)側で、X起動時に
+iglx
オプション。 ssh -XY
でログインする。- ローカルとリモートで
libGL.so
の系列を揃える必要がある。ローカルがmesaならリモートもmesaに合わせる。LD_PRELOAD
環境変数を使う。
参考
- OpenGL hardware acceleration through remote X11 SSH connection « Evgeny Pokhilko's Weblog
- Using the GUI on a remote machine — Installation 2017 documentation
基本事項
X Window System
『ビットマップディスプレイ上でウィンドウシステムを提供する表示プロトコル』(wikipediaより引用)。GUIを構築するための基本的なツールキットを提供する。実際に絵が表示される画面を管理する「Xサーバ」をデーモンとして実行し、各GUIプログラム(GnomeやAwesomeWMのようなウィンドウマネージャやその他のGUIアプリケーションなど)は「Xクライアント」となってXサーバに描画命令を送信する。Xサーバ・Xクライアント間の通信プロトコルもX Window Systemの一部として規定されている。
LinuxをGUIマシンとして用いる場合、普通は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/config
でForwadX11 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ドライバの一部としてパッケージされており、APIはOpenGLの規格に従って整備されているが、内部の実装はGPUベンダまたはドライバの配布元によってバラバラである。
一例として、nVidiaのGeForceを積んだリモートサーバ(nVidiaのプロプライエタリなドライバが入っている)に、intelのCPU統合グラフィックスしかないローカルマシン(mesaのオープンソースドライバで動いている)で接続したとしよう。このとき、リモートサーバで動かすOpenGLアプリケーションはnVidiaのドライバに含まれるlibGL.so
をリンクして使用するが、nVidiaのlibGL.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を買うしかない。