これは Jasper St. Pierre 氏が自身のブログに投稿した「The Linux Graphics Stack」(2012年6月16日現在) の日本語訳です。
まだ完訳ではありませんが、長くなってきたので途中まで公開します。随時、更新・修正の予定。
ひととおり読んで完成。誤訳や誤植があればコメント等にどうぞ。
なお、文中でこんなふうに下線が付いた箇所や訳注:で始まっている箇所は訳者が適当にリンクを付与したり補足したもので、原文には存在しません。また、翻訳できなかった文は「原文のままで斜体」にしてあります。
注記 翻訳したものを公開してよいかどうかを原文の作者には問い合わせましたが 残念ながら返事がありませんでした。 もしかしたら本投稿は期間限定になるかもしれません。
これは Linux のグラフィックス・レイヤ (Linux Graphics Stack) の概要について簡単に紹介したブログの投稿です。私は、そのレイヤについて Owen Taylor 氏や Ray Strode 氏、Adam Jackson 氏といった人達と何回か話をした後、この概要を最初は自分自身のために作成しました。私は毎月かそれくらいのペースでその会話を思い出し、もう一度ゼロから学習する必要がありました。なぜならば、私は毎回彼らと話したことを忘れてしまっていたからです。それから、私はやさしく概要をまとめたおあつらえ向きのドキュメントが存在するかどうか彼らに尋ねることにしました。もしあれば、これで思い悩むことがなくなると思いました。しかしながら、彼らはそのようなドキュメントの存在について知りませんでした。そのため、この概要を作成することにしました。以下に作成した内容を Adam Jackson と David Airlie の両氏にレビューしてもらいました。ちなみに、彼らは実際にこのレイヤを使った開発に携わっています。
さらに、私はこのレイヤの大部分をフリー・ソフトウェアのドライバに対してのみ考察したいと考えました。すなわち、あなたがここで読んだ内容は AMD Catalyst™ や NVidia といったプロプラエタリな商用ドライバにはあてはまらない可能性があると言うことです。AMD や NVidia 社には彼ら専用の OpenGL があるかもしれないし、あるいは社内で独自に手を加えた mesa ライブラリの開発ブランチがあるかもしれません。そこで、以下の内容は radeon とか nouveau といったライセンス・フリーなドライバと Intel のドライバに関してまとめてあります。
この内容に関する質問をお持ちとか、何か不明確な文章を見つけたとか、筆者の私が大きな間違いをしているとか、あるいは誤植などで読みにくいといった苦情をお持ちなら、私に教えていただくか、このブログの下にあるコメント欄で指摘していただければと思います。
まず最初に、このグラフィックス・レイヤの全体像を解説することにします。それにより、このレイヤの中で機能する各部についての概要を把握します。すぐに理解できなくても、心配しないで下さい。いつでもご自由に本投稿を読み返してもらって結構です。ここに本投稿へのリンクを貼り付けておきます。
それでは始めましょう。まず、正確にいうとグラフィックス・レイヤには、あなたが実行しているプログラムが何を描画しているかによって二つの異なる「処理の流れ」が存在します。
OpenGL を使った 3D 描画
- 起動したプログラムが “OpenGL” を使った描画処理を開始します。
- “mesa” というライブラリに実行の制御が移ります。これは OpenGL の API を実装しています。このライブラリは、グラフィックス・カード専用のドライバを使って、API をハードウェア専用のコードに翻訳します。もしドライバが内部で gallium のライブラリを使用しているならば、別途、OpenGL の API から TGSI (Tungsten Graphics Shader Infrastructure) と呼ばれる共通中間形式への翻訳を担当する共有コンポーネントも利用します。API が gallium に渡された後にグラフィックス・カード専用のドライバは全て TGSI からハードウェアのコマンドに翻訳されます。
- libdrm というライブラリが、特別に非公開のグラフィックス・カード専用の ioctl システムコールを使って Linux のカーネルとやりとりします。
- Linux のカーネルには特別なパーミッションがあり、その権限を使ってグラフィックス・カード上に描画で使うメモリを確保します。
- 実行の制御が “mesa” のライブラリに戻ると、このライブラリは DRI2 を使い Xorg と通信して、確保したメモリのうちバッファとして使用するサイズとウィンドウを描画する位置などを (同期処理で) 確認しあいます。
Cairo を使った 2D 描画
- 起動したプログラムが “cairo” を使った描画処理を開始します。
- ここで、そのプログラムはグラデーションが施された円をいくつか描画しているとしましょう。”cairo” のライブラリは円をいくつかの台形に分解し、これらとグラデーションの情報を XRender という拡張機能をサポートした Xorg の描画サーバへ送ります。もし Xorg の描画サーバがこの拡張機能をサポートしていない場合、”cairo” はローカルにインストールされている libpixman というライブラリを使ってピックスマップを生成し、その画像データを Xorg のその他の API を使ってを描画サーバへ送ります。
- (改めて) Xorg の描画サーバが XRender 処理の要求を受け取ります。描画サーバは複数のグラフィックス・カードに特化したドライバを使って描画します:
- ソフトウェアによる描画、またはグラフィックス・カード専用のドライバが XRender 機能をサポートしていない場合、Xorg の描画サーバは libpixman ライブラリを使って (前述の “cairo” が処理していたことと似たような方法で) 描画します。
- ハードウェア・アクセラレーション機能による描画の場合、Xorg のドライバは libdrm というライブラリを介して Linux のカーネルとやりとりし、テクスチャという形式になった画像データと描画制御用のコマンド列をグラフィックス・カードへ送ります。
Xorg の描画サーバが画面に何かを描画する際は、描画サーバが自ら KMS とグラフィックス・カード専用のドライバを使って、フレームバッファを設定しその中にグラフィックスを描画します。
Xウィンドウ・システム、X11、Xlib、 Xorg
X11 はグラフィックスとは直接関係はありません。これはイベントを配送するシステムの一つです。イベントとはウィンドウなどのいろいろなオブジェクトに付随している属性の概念です。グラフィックスとは関係の無いその他のものがたくさん X11 の上位のレイヤに配置されています (例えばクリップボードの機能とかドラッグ&ドロップの機能など)。便宜上、ここにあげたものだけ紹介します。あとで、Xウィンドウ・システムの全体像、X11とその奇妙な設計思想について調査して投稿しようと思います。
-
X11
これはXウィンドウ・システムが使用する(針金でつくったような)プロトコルです。
-
Xlib
これはXウィンドウ・システムのクライアント側の実装であり、ウィンドウ・システム上でいろいろなウィンドウを管理するためのユーティリティがたくさんあります。X11 のプロトコルをサポートしたツールキット (例えば GTK+ とか Qt) によって使用されます。最近のデスクトップ環境で、この実装だけを使ったアプリケーションにお目にかかることはほとんどありません。
-
XCB
まれに Xlib の代わりにその実装を引用されることがあります。これは X11 プロトコルの大部分を実装しています。この API は Xlib よりもかなり低いレイヤに位置しています。実際のところ現在の Xlib は XCB の上位で構築されているのは承知の事実です。
-
Xorg
これはXウィンドウ・システムのサーバ側の実装です。
【注記】 私は、ここで何かの名称を引用する際は、正しく付与するようかなり注意を はらったつもりです。ここで「Xサーバ」(X server) といった場 合、それは一般的なXサーバについて話をしています。これは Xorg のこと かもしれませんし、Apple のXサーバの実装かもしれませんし、はたまた Kdrive のことかもしれません。そこにある商標名は関係ありません。また、 「X11」とか「Xウィンドウ・システム」(X Window System) とい った場合は、プロトコルの設計内容とかシステム全体について話をしていま す。さらに、「Xorg」といった場合は、ほとんどのXサーバが使用し全ての Xサーバに当てはまるとはかぎらない Xorg の実装の詳細を意味するものと します。ちなみに、単に「X」といった場合はそれはバグ (誤植) です。
X11 というプロトコルは拡張が可能なように設計されています。これは新しい機能のサポートを、別のプロトコルを作ったり、既存のクライアントが動かなくなるといった問題を引き起こすことなく、追加できるということです。例えば、シェイプの拡張機能 (Shape Extension) を使えば xeyes とか oclock といったアプリをおしゃれな形状にすることができます (この機能は四角形以外のウィンドウを提供してくれます)。もし、この魔法のような機能がどのようにして降って湧いたのかを知りたいというのであれば、その答えをお教えしましょう。それは降って湧いたのではありません。拡張機能のサポートはユーザがその恩恵にあずかる前に、サーバとクライアントの双方に追加されている必要があります。コアなプロトコルそのものに拡張できる機能があるので、クライアントはサーバでどんな拡張機能がサポートされているかを問い合わせることができるようになっており、どの機能が利用でき、どの機能が利用できないかを知ることができるのです。
さらに X11 は「ネットワーク透過」な仕組みを持つように設計されています。これに関して最も留意すべき点は、Xサーバと全てのクライアントが同一マシン上に存在していても問題ないということです。すなわち、この二つの間の通信はネットワーク越しに行われます。その結果、最近のデスクトップ環境の場合、ノートPCを箱から出してすぐには利用できません。X11 の他にも DBus といったプロセス間通信がたくさんシステムの中を駆け巡っているからです。それはチャットの会話のようにネットワークの上を走り抜けていくため膨大な量のトラフィックになります。サーバとクライアントが同じマシン上に存在している時は、ネットワークを利用する代わりに、UNIX ソケット越しに通信が行われるようになっているので、カーネルはデータをいちいちコピーする必要がありません。
それでは、ちょっとの間、Xウィンドウ・システムと膨大な拡張機能に話を移すことにしましょう。
cairo
cairo は描画ライブラリの一つで、Firefox のようなアプリケーションから直接利用したり、または GTK+ のようなライブラリを介して利用することでベクトル形式の図形を描画します。GTK+ バージョン3の描画モデルは cairo を使って全て実装されています。もし HTML5 の <canvas> というタグをご存じであればお分かりかと思いますが、事実上 cairo も全く同じ API を実装しています。<canvas> タグは元々 Apple が開発したものですが、そのベクトルの描画モデルは Postscript のベクトル描画モデルとして有名です。このモデルは PDF や Flash、SVG、Direct2D、Quartz 2D、OpenVG といった標準的なものの他にも、ここに一覧ができてしまいそうな程たくさんのベクトル・グラフィックスの技術をサポートしています。
cairo はその Xlib 用のバックエンドを介して、X11 のサーフィスへの描画をサポートしています。
cairo は今まで GTK+ のようなツールキットで利用されてきました。GTK+ のバージョン 2 (実際は GTK+ バージョン 2.8) からオプションとして追加され、cairo の機能が利用できるようになっていました。GTK+ バージョン 3 では cairo は必須になっています。
XRender 拡張
X11 には XRender という特別な拡張機能があります。これはアンチエイリアス付きでいろいろな図形やグラデーションを描画したり (従来はエイリアスで描画していました)、行列変換などといったいろいろなサポートを追加してくれます。この拡張機能の元々の意図は、特定の描画を行うために特別に高速化したいろいろなコード・パスをグラフィックス・ドライバに追加させることでした。ところが、ソフトウェアによるラスタ変換 (rasterization/訳注: ベクトル・データを画面のドット・データに変換する処理) の方が (何らかの間接的な理由で) 同じくらい高速になるケースがあるようです。まぁ、それはともかく、XRender は一列に並んでいる台形 (左右の角が傾いた矩形) を処理します。Carl Worth 氏と Keith Packard 氏が台形をソフトウェアで「高速に」ラスタ変換するアルゴリズムを思いつきました。さらに台形は簡単に二つの三角形に分解でき、これをハードウェアの高速な描画機能を使って配置していきます。Cairo には show-traps という素晴らしいユーティリティが含まれており、これを使えばモザイクと台形を重ねて図画の詳細をみることができます。
これは赤い色をした円を描画したものです。これは二つ種類の台形の集合から構成されています。一つ目は線で描画した台形の集合で、もう一つは塗りつぶした台形の集合です。show-traps がデフォルトで描画する図形はあまりかっこよくないので、このユーティリティをちょっとハックしてそれぞれ別々の色で台形を描画するようにしました。線で描画した台形は黒色になっています。
こちらはサイケ調の配色にしたものです。
pixman
Xサーバと cairo は共に、どこかの時点でピクセル形式のデータを処理する必要がでてきます。以前まで、これを実現するラスタ変換アルゴリズムや任意の種類のバッファ (ARGB32、RGB24、RGB565) に対するピクセル単位のアクセス方法、グラデーションの生成、マトリックス計算といった類の実装を cairo と Xorg とでそれぞれ別個に所有していました。現在はXサーバと cairo が共に低レベルのライブラリを共有するような仕組みを持っています。このライブラリは pixman と呼ばれ rasterization 全般を担当しています。但し、このライブラリの目的は公開 API や描画 API を提供することではありません。このライブラリは全く API というレベルのしろものではありません。これは単にいろいろな描画システムの間でコードを重複しないようにする回避策の一つにすぎません。
OpenGL, mesa, gallium
(ここから面白くなりますよ): 最近のハードウェア・アクセラレーション事情についてです。ここで私は皆さんが既に OpenGL が何であるか知っているものと仮定します。libGL.so というライブラリは一個だけではなく、またそのソース・ファイルも一個だけではありません。いろいろなグラフィックス・カード・ベンダーは自分たちの libGL.so を提供しようとしています。nVidia は自分たちの OpenGL の実装を含めた libGL.so ライブラリを Windows や OS X 向けに配布しています。
もしオープン・ソースのドライバをお使いなら、そこで利用している libGL.so ライブラリの実装はおそらく mesa プロジェクトのものでしょう。mesa プロジェクトでいろいろなことをやっていますが、その中で最も有名なことは OpenGL の実装の配布です。これが OpenGL API のオープン・ソースな実装の一つになります。mesa にはサポートしている複数のバックエンドが含まれています。具体的に言うと、CPU ベースの実装が三つあります。swrast (今となっては旧式で使用してはいけない) と softpipe (遅い) と llvmpipe (論理的には高速) です。さらに mesa にはハードウェアに依存したドライバがいろいろ含まれています。Intel 社は mesa をサポートし、自分たちのチップセットに対応したたくさんのドライバを mesa に同梱しています。mesa では radeon と nouveu のドライバもサポートしていますが、これらは別のアーキテクチャ向けにビルドされています: それが gallium です。
gallium は何も特別なものではありません。とても簡単にドライバを実装できるようにするコンポーネントの集まりです。これは、OpenGL や GLSL、Direct3D といった API を実装し図形の形状を監視するトラッカーを使って、その形状を中間表現 (これは Tungsten Graphics Shader Infrastructure、あるいは TGSI と呼ばれます) に変換して、それを受け取ったバックエンドが、ハードウェアが理解できる命令に変換するという考えです。
残念ながら Intel のドライバは gallium を使っていません。Intel のドライバの開発者は mesa とドライバとの間に任意のレイヤを持たせることを嫌っていると、私の同僚が教えてくれました。
少しだけ脇にそれて: いろいろな略語
この分野には紛らわしい略語がたくさんありますが、それぞれ <H3> タグつきの段落を用意して説明したくはないので、ここに一覧としてまとめて記述することにします。今のような (何でも検索できる) 時代なら特に問題ではないでしょうが、やはり簡単なリファレンスにしておけば便利でしょう:
- GLES
OpenGL には、いろいろな目的に応じてプロファイルがいくつか用意されています。GLES はその一つで、何を対象にしているかによって “GL Embedded System” とか “GL Embedded Subset” の略になります。組込み市場をターゲットにした最新の取り組みです。iPhone は GLES 2.0 をサポートしています。 - GLX
OpenGL にはプラットフォームとかウィンドウシステムといった概念はありません。そのため OpenGL と X11 のようなプラットフォームとの間でやりとりするにはいくつかのバインディングが必要になってきます。例えば、OpenGL のシーン (場面) を X11 のウィンドウの中に埋め込むといった場合です。GLX はその「接着剤」となるバインディングを担当します。 - WGL
上と同じですが、”X11″ ではなく “Windows” のウインドウが対象です。つまり、 Microsoft のオペレーティング・システムの一部になります。 - EGL
EGL と GLES はよく混同されます。EGL はプラットフォーム非依存の新しい API で、Khronos グループ (OpenGL の開発と標準化を行ったグループ) によって開発されました。この API は一個の OpenGL のシーン (場面) を組み立ててプラットフォーム上で動かす機能を提供しています。OpenGL のように、これはグラフィクス・ベンダーが実装します。WGL/GLX のような代替のバインディングもいくつかありますが、GLUT のようにそれらと一緒に利用するライブラリにはなっていません。 - fglrx
fglrx は、以前は AMD のプロプラエタリとして提供していた Xorg の OpenGL ドライバのことを指していましたが、今は “Catalyst” という名前で知られています。fglrx は “FireGL and Radeon for X” の略です。これはプロプラエタリなドライバの一つであるため、自分自身で libGL.so 相当の実装を持っています。それが mesa をベースにしているかは不明です。ここで私が言いたいことは、この用語に “GL” と “X” という文字が含まれているため、AIGLX や GLX といった一般的な技術とよく混同されやすいということです。 - DIX, DDX
Xorg のXプロトコルによるグラフィックスの実装は二つの部分から構成されています。それは DIX (Driver Independent X) と DDX (Driver Dependent X) というサブシステムです。本投稿で Xorg のドライバについて話をしている時は、さらに正確にいうと、これは DDX ドライバのことを指しているものとします。
Xorg のドライバ、DRM、DRI
前に、Xorg にはハードウェアの特別な部品を使って高速に 3D 描画する機能があると説明しましたが、そこに少しばかり話を戻します。さらに、私はこの機能が X11 の描画コマンドからいくつかの OpenGL の呼び出しに変換するといった方法で実装されているわけではないことも説明しました。それでは、もし Xorg のドライバが mesa のレイヤで実装されていると仮定した場合、Xorg を mesa に依存させることなく 3D 描画するにはどうすれば良いのでしょうか?
その答えは、mesa と Xorg とで共有するための新しいインフラ (部品) を開発するということです。mesa は OpenGL のレイヤを実装し、Xorg は X11 の描画レイヤを実装しています。そして、両方ともグラフィックス・カード専用のコマンド・セットに変換されます。変換されたコマンド・セットは、”Direct Rendering Manager” とか DRM と呼ばれるものを使ってカーネル空間にアップロードされます。libdrm というライブラリは、一般的にプライベートで使用する ioctl の命令セットをカーネル空間で呼び出して、グラフィックス・カード上にメモリを確保し、そこにコマンドとテクスチャと呼ばれる形式の画像データなど必要なものを詰め込みます。ここで使う ioctl のインタフェースには二つの種類があります: Intel の GEM と Tungsten Graphics の TTM です。この二つを明確に区別するものはありません。すなわち、これらは共に同じことをやります (もちろん実装はそれぞれ異なりますが)。当初 GEM は TTM よりもシンプルに設計されていると大きく宣伝されましたが、今に至っては、TTM とほぼ同じくらい複雑なものになっています (笑)。
これは次のような動きになります: 例えば glxgears のような 3D 描画アプリを起動すると、アプリは mesa のライブラリをロードします。それから、このライブラリは libdrm というライブラリをロードして GEM または TTM 用の ioctl を呼び出し、カーネルのドライバと直接やりとりします。そう、glxgears が直接カーネルのドライバとやりあって、回転しているギアを表示し、ベンチマークの結果を提示します。
ls /usr/lib64/libdrm_* (訳注: あるいは ls /usr/lib/x86_64-linux-gnu/libdrm_*) を実行した結果を見ると、ハードウェア別にドライバが存在していることに気づくことでしょう。GEM/TTM の機能が完全でない場合は、mesa とXサーバのドライバが持っているプライベートな ioctl を使ってカーネルとやりとりをします。ここでは詳細は触れません。また、libdrm ライブラリは実際にそれらのシステムコールを呼び出すようなことはしません。
Xサーバは、クライアントに渡すためにそこで何が起こっているのかを知っておく必要があるので、同期みたいなことを実施できるようになっています。先ほど起動した glxgears とカーネル、そしてXサーバとの間の同期処理で DRI (正確には DRI2) を呼び出します。”DRI” は “Direct Rendering Infrastructure” の略です (ちょっと珍しい頭文字の並びです)。”DRI” は mesa と Xorg のプロジェクトをくっつけたようなプロジェクトです。DRI 1 は全く良い出来ではありませんでした。そのため DRI 1 を捨てて DRI 2 で置きかえることになりました。
KMS
少々話しが逸れますが、例えばここで新しいXサーバで動かしたいとか、Xサーバを使わずに端末 (VT) 上にグラフィックスを表示したいと考えているとしましょう。そのような場合はどうしますか? この場合、使用するハードウェアの諸元を設定してグラフィックスのデータを格納できるようにしなければなりません。実は libdrm ライブラリとカーネルの中に、そのようなことを実現する特別なサブシステムが用意されています。それは KMS と呼ばれています。これは “Kernel Mode Setting” の略です。これ用の ioctl の命令セットを使えば、グラフィックス・モードを設定したり、フレームバッファをマッピングするなどして、TTY の上に直接グラフィックスを表示させることが可能です。以前は (今もそうですが)、グラフィックス・カード専用の ioctl 命令を用意し、libkms というユーザランドの共有ライブラリがそれらの API を利用するために提供されていました。最終的には、カーネル空間に新しい API が追加されました。これは文字通り “dumb ioctls” と呼ばれています。現在は新しい dumb ioctl を利用できるところで、libkms ではなく、それらの命令セットを利用することが推奨されています。
While it’s very low-level, it’s entirely possible to do. 最近のディストリビューションには Plymouth という起動時にスプラッシュスクリーンを表示するアプリがインストールされています。これは、Xサーバを介さずにグラフィックス・カードをセットアップするといったアプリの典型例です。
“エキスポーズ” モデル、リダイレクション、TFP、合成、AIGLX
私は以前から「合成ウィンドウ・マネージャ」(compositing window manager) という用語を使ってきました (但し、合成が意味することを説明する時や、ウィンドウ・マネージャが何をするのかを説明する時以外)。ご存じのとおり、UNIX システム上でXウィンドウ・システムが設計された 80 年代は、HP とか Digital Equipment Corp.、Sun Microsystems、SGI といったグラフィックス専門の企業がたくさん存在していました。これらの企業もXウィンドウ・システムをベースとした製品を開発していました。X11 のプロトコルでは、意図的にウィンドウを制御するポリシーを明確にせず、その役割を「ウィンドウ・マネージャ」(Window manager) という別のプロセスに委譲しています。
例えば、その時代に人気があった CDE というデスクトップ環境には “focus follows mouse” というメカニズムがありました。これはユーザがマウスを移動し、その下にあるウィンドウにフォーカスを移動するというものです。これは、デフォルトで “click to focus” を採用していた Microsoft Windows や Mac OS X といった当時人気のあったウィンドウ・モデルとは異なっていました。
ウィンドウ・マネージャがどんどん複雑になるに従い、ドキュメントにもいろいろな種類のデスクトップ環境との間の互換性に関する仕様が追加されてきました。It, too, wouldn’t really mandate any policy either like “Click to Focus” either.
さらに、80 年代に登場したコンピュータの多くはメモリを大量につんでいませんでした。そのため、全てのウィンドウの中身をピクセル・データとして格納しておけなかったのです。Microsoft Windows と X11 はこの問題を同じ方法で解決しました。ウィンドウの内容は「劣化しやすい」(lossy) ものとしました。もっと簡単に言うと、ウィンドウがエキスポーズして目の前に露出してきたことをプログラム側に通知するという仕組みを採用しました。
次のようなウィンドウの集合をイメージしてみて下さい。
ここで、ユーザが今から GIMP のウィンドウをドラッグしながら右へ移動させていくとします:
すると、濃い灰色の領域がエキスポーズされて目の前に表示されてきました。ExposeEvent というイベントがそのウィンドウを所有するプログラム (訳注: この例では Firefox) に通知され、そのプログラムは自分のウィンドウの内容を再描画することになります。これは、Microsoft Windows や Linux 上でハングしたアプリの場合は、エキスポーズされた領域に何も描画されずに真っ黒く表示させて明示するためです。Microsoft Windows の場合、デスクトップ自身が別のプログラムであり、特別な権限を持たないため、他の通常のプロセス同様にハングする可能性があることは認識しておく必要があります (加えて、バグ報告ダイアログの「地獄」に遭遇することになります)。
現代のコンピュータはたくさんのメモリを積んでいます。これで我々は X11 のウィンドウらを「劣化させない」(lossless) ようにする機会を得ることができました。それはリダイレクション (Redirection) というメカニズムを利用します。一個のウィンドウをリダイレクションすると、Xサーバは直接メモリのバッファの中に描画するのではなく、そのウィンドウ用にバッキング・ピックスマップをいくつか生成します。これにより、そのウィンドウはすっぽりと隠れてしまい表示されなくなります。そして、別の何かが実際に画像データをメモリのバッファの中に格納して、(訳注:隠して見えなくなった領域に) 画像を表示するチャンスを得ます。
Composite Extension を利用すれば、合成ウィンドウ・マネージャとかコンポジッタ (compositor) が COW (Composite Overlay Window) というものを設定できるになります。コンポジッタは COW を保持し、そこに何かを描画します。例えば Compiz とか GNOME シェルを起動すると、これらのプログラムは OpenGL を使ってリダイレクションしたいろいろなウィンドウを画面に表示します。Xサーバは GL の拡張機能である “TFP” (Texture from Pixmap) を使ってそのウィンドウの中に描画することができます。これは OpenGL のプログラムに X11 のピックスマップをあたかも OpenGL のテクスチャであったかのように利用させる機能です。
合成ウィンドウ・マネージャは TFP や OpenGL を利用するまでもありません。そのメカニズムそのものが合成表示するもっとも簡単な方法だからです。もしその必要ができた場合、合成ウィンドウ・マネージャは普通に COW の上にウィンドウのピックスマップを描画します。例えば kwin4 という合成ウィンドウ・マネージャは Qt を利用して直接ウィンドウを合成しています。
合成ウィンドウ・マネージャはXサーバから TFP を使ってピックスマップを取得し、それを OpenGL が持つシーンの妥当な場所に描画します。その際は、ユーザがクリックしたオブジェクトが実際に X11 のウィンドウであるかのように見せかけてくれます。この見せかけの効果について説明するのは時間の無駄だと思うかもしれませんが、ちょっと脇にそれて GNOME シェルを使って試してみましょう。アクターであるウィンドウの大きさや位置を変更してみてください (GNOME シェルの Looking Glass の中に global.get_window_actors().forEach(function(w) { w.scale_x = w.scale_y = 0.5; }) と入力します)。すると、目の前にあったウィンドウらがすっと小さくなったでしょう。そこで何かクリックしてみると分かると思いますが、(あたかも見せかけの) 動画プレイヤーを素通りして、実際のウィンドウの下にあるオブジェクトをクリックしているはずです(元に戻すには、先ほど入力した Java スクリプトの中の 0.5 という数値を 1.0 に変更して下さい)。
それでは、これを元にもう一つの略語を紹介します: それは AIGLX です。AIGLX は “Accelerated Indirect GLX” の略です。X11 はネットワークを使ったプロトコルの一つですが、これにより OpenGL もネットワーク越しに機能しないといけないことになります。ネットワークを介して利用している OpenGL のことを “Indirect Context” と呼びます。これは “Direct Context” の対義語です。”Indirect Context” で使用するネットワーク・プロトコルは全く不完全で不安定です。
AIGLX なるものを持ち出してきた背景とその考えを理解するには、解決しようとしていた課題について理解する必要があります。それは、Compiz のような合成ウィンドウ・マネージャを高速にするというものです。Nvidia のプロプラエタリなグラフィックス・カード専用のドライバがカーネル・ランドでメモリ管理を行うための独自インタフェースを持っていたのに対し、当時のオープンソースのドライバにはそのようなものが未だありませんでした。ウィンドウのテクスチャをXサーバから持ってきてグラフィックス・カードコピーするということは、そのウィンドウが更新される度にそのテクスチャの複製を保持しておくということです。その処理は遅いのです。そのため、AIGLX をちょっとハッキングしてその中に OpenGL を実装し、データをグラフィックス・カードにコピーしないようにしました。これにより、Compiz のようなコンポジッタを利用するシーンが複雑な処理にならなくなったので、十分に機能するようになりました。
この成果に対して派手な宣伝がなされ、Phoronix でも取り上げられたにもかかわらず、 しばらくの間 AIGLX が実際に利用されることはありませんでした。さらに、今となっては、データのコピーを伴わない TFP の実装を利用した DRI のメカニズムの方が普及しているからです。
ご想像のとおり、ウィンドウのテクスチャをコピー (正確に言うと、サンプリング) できれば、そのテクスチャを OpenGL のテクスチャとして描画できます。多くのウィンドウ・マネージャにはウィンドウをフルスクリーン表示する時のために、リダイレクションを無効にする機能があります。リダイレクションを無効にすることはちょっとばかげているように聞こえるかもしれません。リダイレクションを持たないのは初期のウィンドウ・システムと同じだからです。しかし、リダイレクションが無いのがウィンドウの初期状態であるとするならば、最近の Linux デスクトップ環境の場合、それはほとんどのケースで通常の状態とは言えません。ここまでの考え方として、もしウィンドウが COW になることができ、合成機能を必要としないならば、リダイレクションしなくても問題はないということです。この機能は高いパフォーマンスを要求してくるゲーム等のプログラム向けに設計されたものです (例えば毎秒 60 フレームを描画するようなゲームなど)。
Wayland
本投稿では、ここまで「一枚岩」でできたXサーバの動きから、グラフィックスを処理するインフラまでの広範囲な部分をバラバラに解体し解説してきました。しかしながら、ここはXが担当しているこの大きな「岩を解体する」だけの場ではありません: ご存じのとおり、既に (Xが扱ってきた) 多くの入力デバイスの処理が evdev と共にカーネル・ランドへ移動し、(以前までカーネル・ランドで扱っていた) ホットプラグのサポートなどがユーザ・ランドの udev に移動しています。
今でもXウィンドウ・システムが現役である大きな理由は、そのシステムを置き換えようとする試みがたくさんありすぎたからです。そこで Xorg のメンバらが元のシステムを一つ一つ解体し、最近のデスクトップ環境で必要となるいろいろな機能を拡張モジュールとして分離させていきました。え〜と、これを何て表現したらよいのでしょう。ここでは、あえてXは賞味期限切れの状態とでもしておきます。
では、Wayland に話しを移します。Wayland は、ここまで作り上げてきたたくさんの既存のインフラを再利用しています。その際に最も議論されたことの一つが、ネットワークの透過性や描画プロトコルの並び替えが欠如している点です。今の時代では、Xが持つネットワークの透過性は完全に失敗作です。Linux が持つ膨大な数の機能 (イベントの配送) は、Xではなく DBus のような全く別のレイヤで処理されています。加えて、ドラッグ&ドロップとかクリップボードのサポート、さらにネットワークをサポートするためにXウィンドウ・システム単体が大きく変更されているという状態は見るも無惨です。
Wayland はここまで詳細を説明してきたグラフィックス・レイヤのほぼ全部を使って、ディスプレイにフレームバッファを表示させることができます。Wayland は未だ一個のプロトコルでしかありませんが、それを UNIX ソケットとローカルにあるいろいろなリソースを使って実現しています。そこで、一番思い切った大きな変更は、/usr/bin/Xorg に相当する /usr/bin/wayland といったバイナリが実行されていないということです。その代わり、Wayland は最近のデスクトップからの助言に従い、全てをウィンドウ・マネージャのプロセスに委譲しました。それらのウィンドウ・マネージャは、「Wayland語」でいうコンポジッタと呼ばれるもので、実際にはカーネル・ランドから evdev といったシステムを使ってイベントを引き出し、KMS と DRM を利用してフレームバッファを設定します。それから (例えば OpenGL といった) 描画レイヤが描画したい物と一緒にスクリーンの上にウィンドウを表示します。これを実現するには、(それらのサブシステムがどこか別の場所に移動したことを考えると) 大量のコードが必要そうに思えますが、おそらく 2,000-3,000 SLOC (訳注: Source Lines Of Code の略でコードの行数を表します) の規模になると思われます。muttter の一部を修正して、ウィンドウのフォーカスやウィンドウ・ポリシーのスタック、Xサーバとの同期などを含めると 4,000-5,000 SLOC くらいです (私自身が引きつけられた理由がおわかりになるかと思います)。
Wayland にはクライアント側とコンポジッタ側の双方が利用するであろう実装を持ったライブラリが用意されているとはいえ、それは Wayland プロトコルを簡単に参照できる程度の実装です。誰かが Python や Ruby で Wayland のコンポジッタを作成し、純粋な Python だけでプロトコルを実装できれば、libwayland というライブラリの助けは不要になるでしょう。
Wayland のクライアントらはコンポジッタと通信してバッファをそれぞれ要求します。コンポジッタは、OpenGL や cairo などを使って、クライアントが描画する先のバッファをそれぞれ返してきます。コンポジッタは、そのバッファを使ってやりたいことは何でも処理できるように配慮してくれます – 例えば、(描画する内容が素晴らしいので) そのまま表示するとか、 (そのアプリがうざいので) 火をつけるとか、キューブの上でクルクル回転させる (Linux にはこの手の Youtube みたいな動画がもっと必要なので) といった処理です。
コンポジッタはさらに入力とイベント処理の主導権をも握っています。もし前述の GNOME シェルでウィンドウの表示倍率を変更するみたいなことを試そうとした場合、まず最初は試行錯誤するかもしれません。そして、それからマウス・カーソルが他のウィンドウとは異なり倍率が変わっていないことに気づくことでしょう。マウス・カーソルの倍率が変更されていない理由は、実際に X11 のウィンドウ・サイズに倍率を適用して大きさを変更した訳ではなく、単に表示している倍率を変更したからです。Xサーバはどこにウィンドウがあるかを追跡しますが、Xサーバが考えている場所にウィンドウを実際に描画するのは合成ウィンドウ・マネージャ次第です (それ以外、すなわちXサーバが追跡していないオブジェクトの描画位置は不定です)。
Wayland のコンポジッタが evdev から受け取ったシグナルをウィンドウのイベントとして提供する役割りをになっているため、おそらくウィンドウの位置を把握することについてずっとよい解決方法を持ち、そして内部でいろいろ変換させることができるでしょう。これは、キューブの上でウィンドウをちょっと回転させることだけを意味しているのではなく、キューブの上にある複数のウィンドウの間で相互にいろいろ作用させることができるという意味です。
最後に
私は今でも Xorg のその実装がかなりの「一枚岩」でできているということを良く聞きます。これは本当だと思いますが、以前まで言われていた程ででは無いでしょう。これは Xorg の一部の開発者が無能だからというわけではなく、ハードウェアによる高速な XRender プロトコルとか、さらに古い話に戻すと XPolyFill といった非アンチ・エイリアスによる描画コマンドといった、我々がサポートしなければならない範囲が大きすぎることが「お荷物」になっていたと考えます。近いうちに Wayland の支持によってXが消えていくであろうことは明白だと思いますが、私はそういった動きの大部分が Xorg やデスクトップ開発者らの同意と支援から始まったのだということをはっきりさせたいと思います。彼らは頑固者でも役立たずでもありません。30年前のプロトコルに取り組み実装してきたこれまでの歴史を考えれば、彼らの業績は素晴らしいものです (特に新しいアーキテクチャの対応などは)。
さらに、私はこの投稿で取り上げた技術に携わっている全ての人達にエールを送りたいと思います。また、Owen Taylor 氏、Ray Strode 氏、Adam Jackson 氏らには私の間抜けな質問に対し辛抱強く回答してくれたこと、そして Dave Airlie 氏と Adam Jackson 氏にはこの投稿の技術的なレビューで助けていただいたことに、それぞれ個人的に感謝の意を伝えたいと思います。
私はこの投稿で取り上げてきた技術について一つ一つ詳しいレベルに踏み込んでいきましたが、その一方で、ここにはあなたの興味を引くものであればもっとたくさん詳しく勉強できる場所があります。いくつか紹介すると、例えば cairo が任意の矩形を台形に変換させるのに利用しているジオメトリのアルゴリズムと理論の学習です。あるいは Carl Worth 氏と Keith Packard 氏が作成した台形を高速に描画するソフトウェアのソースコードを参照して高速である要因を調べるとか。または DRI2 の設計を調べて DRI1との違いを考察するとか。はたまた、あるいはハードウェアに興味がおありなら、グラフィックス・カードのアーキテクチャを調べたり、データ・シートを参照してプログラムを作成するとか。このような分野で何か役に立ちたいを考えているのであれば、この投稿で取り上げた全てのプロジェクトがもっとハッピーになるように貢献してみて下さい。
将来、私はこれらの多くを記事にして投稿したいと考えています。なお、今日まで、Linux と GNOME コミュニティで使用されているレイヤの大部分についてかなり上位の視点からみた概要をまとめた優良なドキュメントは存在しません。
改版履歴
- 公開
- CSS ベースのダイアグラムを画像に変換し Planet GNOME の読者に分かりやすくした
- Looking Glass のサンプルを修正した
- TFP のリンクを修正した
- ウィンドウ・マネージャ・セクションのポリシーの大部分を変更した
- GTK+ と cairo の統合に関する情報を修正した (GTK+ バージョン2.8 であって “Near the end of cycle” ではない)
- “fglrx” の正式名を修正した