2004-07-20

近況

頭痛を抱え社内の保健室(のようなところ)を訪問. 偏頭痛がするんです. 偏頭痛ですか頭痛ですか. 偏頭痛と頭痛は違うんですか. 偏頭痛は締めつけられるような痛みがするの. 痛みに偏りがあるということではないんですね. 違います. じゃあ頭痛です. こうして処方されたバファリンは良く効いた. バファリンを風邪薬だと信じていた私の誤解は, 頭痛の種などない長閑な暮しの象徴に思える. やさしさを噛み締め, 顔をしかめた. 空腹時を避けて服用すること.

ハードウェア化を目指すデスクトップ・グラフィクス

デスクトップ・アプリケーションで使われる平面グラフィクスのレンダリング・エンジン(レンダラ)について少し調べた. 停滞しているように見えたこの分野も少しずつ進歩している. Win32-GDI や Xlib といった古典の時代はとうに去って, 今は PDF 並の描画能力を持つ API が利用できるようになっている. またこうした描画品質の向上とは別に, ハードウェアによる高速化も実現されつつある. ハードウェアによる高速化と描画機能の向上という相容れないベクトルの両立は一見の価値あり. 一方で相変わらずな部分も残る. プログラマの多くがウィンドウシステムの Graphics 機能を活用しないのは, その描画品質の貧弱さ以上に描くべきコンテンツの不在が原因であった. この問題は相変わらず解決されていないが, 進歩の兆しはある. Web の普及につれて進化が止まったように見えたデスクトップ・プログラミングも, Web Application の高機能化によりいずれまた復権するだろう. そんな日に向けてちょっと予習しておく.

レンダリング・エンジンの進化

そもそもこの話題に興味を持ったのは, Quartz, Avalon, freedesktop といった新しいデスクトップ環境について調べていたからだ, これらに登載されたレンダラがどのような位置付けにあるのか, まずはざっと眺めてみる.

素朴な実装: GDI, Xlib, AWT, etc.

矩形の塗り潰しやコピー, 直線, 円, 多角形, 文字列の塗り潰しと描画,ビットマップ画像の転送... 大抵のウィンドウ・システムが備えているグラフィクス機能はだいたいこんなところだろう. こうしたよくあるレンダリング機能を 素朴な実装 と呼ぶことにする. Java の java.awt.Graphics の API は典型的でわかりやすい. こうした 素朴な実装 を使う機会は少い. なにしろ機能が貧弱すぎる. こうした環境で現実的なアプリケーションを作るには文字列の描画と画像の転送だけを使ってがんばるか, あるいはプログラマが自力でレンダラを用意する必要がある. 大抵のデスクトップ・ソフトウェアはそうして作られているはずだ.

ソフトウェア・レンダラ: libart, java2D, quartz, GDI+

先に述べたように, 一昔前の OS やウィンドウ・システムの素朴な実装で用意されていない高度な機能, たとえば曲線の描画, グラデーションによる塗り潰し, サブピクセル単位の位置指定など..., を補うには, ソフトウェア自身がピクセル単位で描画を行う必要がある. 内部で確保したビットマップのメモリ領域を直接書換え, そのビットマップを画面に転送して期待した描画結果を得る. つまり抽象化されたグラフィクス・コンテクストに頼らない. そんなアプローチをとり高度な描画機能を提供するレンダラを, ここでは ソフトウェア・レンダラと呼ぶ. libart は典型的. レンダラ・オブジェクトの引数にピクセルマップを渡し, 描画はそこに行われる. ハードウェアの手を借りようという姿勢はまったく見せない. JDK の Java2D, Apple の Quartz, Microsoft の GDI+ などはそこまで露骨ではなく, 一応抽象化層がある. ただ, 現状ではこれがハードウェアによる支援を受けている様子はない.

なお, こうした高度なレンダラは描画モデルとして PostScript や PDF 相当のものを採用しているものが多い. それらの機能を調べる時には, PDF の仕様書 が助けになる.

ハードウェアを利用するレンダラ: cairo, Agile2d, Java2d(Tiger), Evas, (たぶん)Avalon

レンダリングエンジンがグラフィクス・コンテクストのような抽象化層を用意する理由の一つはハードウェア支援を期待しているからだ. 直線, ポリゴンの描画やビットマップの転送などは確かにハードウェアの支援を期待できる. 一方で曲線の描画やグラデーションなどの機能に対する支援をこれまでのハードウェアに期待するのは無理があった. 多くのソフトウェアが独自のソフトウェア・レンダラを利用したのはこうした判断による.

一方で, 3D グラフィクスはハードウェア化が進んだ. その3D機能を平面グラフィクスに転用しようというアプローチがある. 要するに曲線や多角形を全て三角形に分割してハードウェアに描かせようということ. こうした方針のレンダラをハードウェア・レンダラと呼ぶことにしよう. 平面グラフィクスの描画モデルは3Dグラフィクスのそれと大きく隔たりがあるため, そのギャップを埋めるにはコストがかかる. そのコストを払っても割にあうほどハードウェアが高速ならハードウェア・レンダラは有効だ. 後述するように, このギャップをどうやって埋めるかが各レンダラを特徴づける.

ウィンドウ・システムでの 3D ハードウェア支援: Quartz Extreme, (たぶん)Avalon

レンダリング・エンジンのハードウェア化と直接の関係はないものの, 3D ハードウェアを使ったウィンドウ・システムには注目したい. 基本的なアイデアはこんなかんじ: 全てのウィンドウにテクスチャマップを割当て, アプリケーションはそのテクスチャマップに描画を行う. ウィンドウ・システムは各ウィンドウをあらわす矩形状のメッシュに該当テクスチャをつけて描画する. Apple の Quatz Extereme はまさにそんなことをしているという. Microsoft がそれに追従しない理由はない. Avalon も似たような構造を持つことになるだろう.

この方法は MacOS X のような華々しい UI の基盤となるだけでなく, プログラマにとっても有難い. なにしろ今まで自分で管理していたオフスクリーンを OS が管理してくれるため, ウィンドウサイズの変更や再描画などウィンドウがらみの面倒はだいぶ片付く.

ハードウェア・レンダラと相性が良いのも特徴. ウィンドウの中身がテクスチャなら, レンダラの描画先はそのテクスチャになる, これは単純でわかりやすい. 逆にソフトウェア・レンダラとの相性はあまりよくない. テクスチャは VRAM 上に置く必要があるのに, レンダラはシステムメモリとしてのアクセスを要求するからだ. ウィンドウシステムは描画の度にテクスチャをコピーするか, ハードウェアと連携してメモリ空間を共有する必要がある. Quartz はそうした機構を実現しているが, いずれにせよオーバーヘッドはあるはずだ. (将来は Quartz に対してハードウェア支援が実現されるのかもしれない.)

ハードウェア・レンダラの難しさ

このように, 平面グラフィクスも 3D と同様にハードウェア化を推し進めようとしている. ただ, 平面グラフィクスの技術はソフトウェア・レンダラを前提に進歩してきた. そのためハードウェア化には幾つかの困難がある. 主な困難はふたつ: 一つは描画品質への要求, もう一つは描画モデルの複雑さだ.

描画品質

デスクトップ・グラフィクスは当然のようにフルシーン・アンチエイリアシングを要求する. アルファブレンディングも同様. アルファブレンディングはともかく, フルシーン・アンチエイリアシングなどは最近のハードウェアでようやく実現された機能だ. 現状デスクトップ・システムのレンダラがそんなハードウェアを要求するのは難しいため, レンダラの提供者は高性能ハードウェアの普及を待つほかない. 実際, Longhorn はそんなハードウェアが一般化する(であろう)時期にリリースを予定している.

複雑な描画モデル

たとえばフォントの描画を想像しよう. TrueType フォントはスプライン曲線で定義されている. フォントの描画品質は妥協が許されないから, レンダラは描画の度にそれをテセレートしなければならない. それだけでなく, フォントのヒント情報を解釈する必要もある. これらを 3D レンダリングのパイプラインに統合するのは一仕事だというのがわかるだろう.

平面グラフィクスの中心にある "パス" のモデルも複雑だ. パスは単なる多角形と曲線ではなく様々な属性をもつ. 例えば点線のパターン(Line Dash Pattern)や多角形の頂点の描画方法(Line Join Style, Miter Limit)がそれ. こうした属性を考慮したテセレーションをハードウェア化する困難は想像できる.

この他にもグラデーションや任意形状のクリッピングなど, ハードウェアの支援を受けるのが難しい機能を平面グラフィクスの描画モデルは要求する.

平面グラフィクスで支配的な PDF/PostScript 描画モデルは, そもそも 3D ハードウェアを意識して設計されていない. だからハードウェア化に困難を伴うのは当然だと言える. 一方でハードウェアは劇的なまでに高速化している. このハードウェアといくつかのトリックを組み合わせればこの困難を克服できる; ハードウェア・レンダラはそんな確信のもとに設計, 実装されている(気がする).

ケース・スタディ

実際にハードウェア・レンダラの実装を覗いて, 一体何が行なわれているのかを見てみたい. 一番知りたい Avalon の秘密はレッドモンドの奥深くに隠されている. そこでオープンソースの寛容さを頼り, OpenGL を使った Java2D の実装 Agile2Dfreedesktop.org のレンダリング・エンジン cairo を眺めてみることにした.

個々の機能をどう実現しているかを眺める他に, ハードウェアに要求する機能と実現している機能の内容もレンダラ比較のポイントになる. エンジンの設計者はどんなハードウェアを前提とし, どんな機能が要求されると考えているのか. ハードウェア・レンダラはスケーラビリティを確保するのが難しいため, そうした点は気になるところ. たとえば OpenGL1.2 の API だけで実現されているレンダラと, ピクセルシェーダを必須とするレンダラではその実装戦略は大きく異なるはずだ. そうした設計思想がレンダラの性格をあらわすといっていい.

典型的な実装: Agile2D

Agile2D は, jdk の java2d API を OpenGL の Java-binding である GL4Java 上に実装したレンダラ. java.awt.Graphics2D クラスをサブクラス化し, 描画メソッドを差し替えている.

Agile2D の実装は直感的だ. Path の多角形やベジェ曲線を三角形に分割し, それを OpenGL で描画する. テセレートには GLU の関数を, アンチエイリアシングには OpenGLのレンダリング・ヒントを使う. 標準的な OpenGL の関数を用いるため, ハードウェアへの要求は軽い. かわりに実装されている機能は少い. Java2D API のサブセットだけが実装されている. たとえばグラデーションやダッシュは実装されていない. その他の細かな挙動も Java2D 本来のものとは異る. Agile2D は高機能なレンダラではない. 機能を捨ててハードウェアへの間口を広げた実装だと言える.

Agile2D はその抽象度の低さに特徴がある. この API は実装を完全には隠蔽しない. プログラマは OpenGL の機能を意識して利用することができる. たとえば描画用のプリミティブが拡張され, VertexArray をアプリケーションが作成, 保持することができる. こうしたキャッシングの仕組みは高速化に寄与するだろう.

高機能ハードウェアの利用: cairo graphics

cairo は正確にはハードウェア・レンダラではない. cairo でラスタライズを担当するバックエンドは差し替え可能になっており, OpenGL 上に実装されたバックエンドである glitz を組み合わせることでハードウェア・レンダラになる. glitz はプログラマブル・シェーダを利用しており, 高機能なハードウェアを要求する. cairo/glitz の実装には注記すべき部分がある. そのいくつかを以下に示す.

描画プリミティブ

cairo はパスや多角形を分割後, バックエンドに描画を委譲する. 分割の単位は台形だ. 三角形でも四角形でもなく台形. 底辺と上辺は画面の水平方向と平行になっている. このモデルは "Render" という X の新しいレンダリングモデル に基いている. 分割の単位にこのような台形を採用することは, VertexArray のようなキャッシングの仕組みを捨てることを意味する. 座標変換を行うと, スキャンラインに辺が揃うという台形の性質が失なわれるからだ. 別の言い方をすれば, cairo ではピクセルマップ以外を VRAM に置こうとしていない. 3D グラフィクスに慣れた身からすると, これは大胆な決断に見える. しかしそもそも大抵の 平面グラフィクス用レンダラはそうした機構を持っていなかったのだから, これを自然な判断と考えることもできる.

ピクセル・シェーダの利用

上記のアプローチだと, ハードウェアの支援はピクセル単位の操作が中心になる. glitz はそこにピクセルシェーダを使うことで, 柔軟かつ高速なラスタライズを実現している. 各種のアルファ値演算は当然として, グラデーションの実装をシェーダで行っているのには少し驚いた. ソフトウェア・レンダラでボトルネックになっていたグラデーションを, glitz ではあっさりと実装している. これは 3D グラフィクスのハードウェアが 平面グラフィクスで有効に使われている良い例と言えるだろう.

Flash などのコンテンツを見ていると, cairo/glitz の決断は妥当なものに思える. 平面グラフィクスのリッチ化は, もっぱらアルファ操作やグラデーションの多用がもたらしている. 使われる図形の複雑さに大きな変化はない. したがってテセレータはソフトウェアに留めておきピクセルレベルの操作だけをバックエンドとしてハードウェア化するこのアプローチは, レンダラの高速化とバックエンドの単純さを両立する見事なアーキテクチャだと言える.

ただ, この方法だと任意形状のクリッピングのようなテセレータ中にあるボトルネックは解決しない. たとえばステンシルバッファとの組合せなど, こうした機能を解決するための方法はある. ただ cairo のアプローチだとそれは難しい. バックエンド中立という cairo の構造から来る制限だろう. ハードウェア・アクセラレータだけをターゲットにしたレンダラがあるなら, そこには更なる高速化の余地が残されている.

3D API との共存

Agile2D, cairo はともに OpenGL 上で実装されている. そのため, 同じコンテクストの中で OpenGL を利用することができる. こうした環境では通常のアプリケーションの中で "ちょっとだけ 3D っぽい効果を使う" とか, 3D アプリケーションのシーン中に "ウィンドウを浮かせる" といったことが容易になる. そんなアプリケーションを見せてくれる Longhorn の各種デモも, それほど無茶な話ではないのかもしれない.

技術的な課題

このように, ハードウェア・レンダラは現実のものになりつつある. 基本的なグラフィクス機能はおおよそ実現可能だ. 一方で課題は残されている. そのうちいくつかを以下に示す.

文字の描画

単なるベクターグラフィクスではないフォントの描画には複雑な事情がある. まず, フォントのデータにはヒント情報が含まれている. この解釈は一般的なレンダラの責務を大きく越えるものであり, 大抵はフォント専用のラスタライザがそれを行う. フォントの描画でハードウェアの支援を受けようと思ったら, そのフォント・ラスタライザとハードウェアの連携が必要になる. もう一つの問題はキャッシング. 文字列のグリフは描画が複雑な上に表示量が多いため, 通常はラスタライズの情報をキャッシュして再利用する. キャッシュの中身がラスタライズされたフォントのビットマップなら, 効率的な描画のためにはそれを VRAM 上に置かなければならない. VRAM はサイズが限られている上に仮想記憶のような効率の良いアロケーション機能が乏しい. こうした問題を解決して VRAM を活用するフォントキャッシュの実装には, ソフトウェアによるそれとは別の難しさがあるだろう. もちろんこうした問題を諦め, キャッシュはシステムメモリに置くという決断もありうる. あるいはビットマップ形式以外のキャッシュを使う別の戦略があるかもしれない.

ポストエフェクト/フィルタ効果

ぼかしやモノトーン化, ノイズのようなフィルタ機能の実現も現状のレンダラには統合されていない. 実装自体はピクセルシェーダによって行うことができるだろうが, それをどのように抽象化してユーザに示すかは自明でない. たとえば, ユーザはスクリーンに対してではなくオブジェクト単位でエフェクトをかけたいかもしれない. (あるオブジェクトにだけドロップシャドウをつける, など.) また, フィルタの記述にピクセルシェーダを使えと言われても, 多くのプログラマには無茶な話だろう. より宣言的な記述をしたいはずだ. こうした要求をスマートにまとめあげるには, PDF の描画モデルを踏み越えた新しいモデルを定義する必要がある.

ウィンドウ・システムとの統合

ウィンドウ・システムがハードウェア・レンダラに統合されると, そのウィンドウシステムは複雑さを増すだろう. たとえば, ウィンドウに任意の座標変換を適用したり, ウィンドウ全体をフィルタをかけたりといったことが可能なウィンドウシステムはどのように設計されるのか. MacOS X のような派手なウィンドウ・エフェクトを汎用的に記述しようとすると, こうした枠組みが必要になる.

ウィンドウがテクスチャ付きのメッシュになる世界では, これまでとウィンドウの意味が変わってくる. レンダラがテクスチャへのアクセス手段だとしたら, ウィンドウ・システムはメッシュへに対する操作の集合ということになるだろうか. わからない. ただ, こうしたウィンドウ・システムの構築が困難な作業であるのは確かだと言える.

レンダラにはできないこと

このようにハードウェア・レンダラの現状には課題も多い. とはいえハードウェア化のトレンドが進む上で, これらの問題は解決されていくはずだ.

さて, こうした高性能/高機能 のレンダラが手に入ったとして, どれだけの開発者がこれを使うだろうか. そのレンダラで何を描くのだろうか. 私なら, そんなレンダラだけを渡されても途方に暮れてしまうだろう. ハードウェア化というシステムレベルの課題とは別に, 描くべきもの : コンテンツを巡る問題がそこにはある. これはレンダラの問題ではなく, システムとして解決すべき問題であるように私には思える. (気がむいたらいつもの話につづく)

参考