DBE(3) ダブルバッファ拡張

書式

ダブルバッファ拡張(Double Buffer Extension, DBE)は、X ウィンドウシステ ムの枠組の中でダブルバッファリングを利用する標準的な方法を提供する。 ダブルバッファリングでは、前面バッファと背面バッファと呼ばれる2つのバッ ファを使用する。この両者ともイメージを保持する。 前面バッファはユーザに見え、背面バッファはユーザからは見えない。アニメー ションの連続するフレームは、前にレンダリングされたフレームが前面バッファ に表示されている間に、背面バッファにレンダリングされる。

新しいフレームの準備ができると、背面バッファと前面バッファの役割は交替 し、新しいフレームが見えるようになる。理想的にはこの交換は、ユーザには 視覚的に不自然なことがない一瞬の出来事に見える。このようにして、完全に レンダリングされたイメージだけがユーザに見せられ、新しいフレームをレン ダリングしている時間もずっとイメージは見える状態にある。結果として、 ちらつきのないアニメーションが得られる。

説明

Concepts
通常のウィンドウは XCreateWindow()XCreateSimpleWindow() を用いて生成される。 これにより、ウィンドウ属性のセットが割り当てられ、InputOutput のウィ ンドウに対しては、イメージを描画する前面バッファが割り当てられる。こ のバッファの内容はウィンドウが可視状態になったときに表示される。

この拡張により、アプリケーションはウィンドウでダブルバッファを使用でき るようになる。 これは、背面バッファと呼ばれる2番目のバッファを生成し、ウィンドウの背 面バッファを参照したときに使うため、1つ以上の背面バッファの名前 (XID) をウィンドウに関連づけることを含む。 背面バッファの名前は、 XdbeBackBuffer 型のドロウアブルである。

DBE は相対的なダブルバッファリングのモデルを提供する。1つの XID, つま りウィンドウは常に前面バッファを参照する。1つ以上の他の XID, つまり 背面バッファ名は、常に背面バッファを参照する。バッファ交換の後、ウィン ドウは(新しい)前面バッファを参照し続け、背面バッファ名は、(新しい)背面 バッファを参照し続ける。したがって、背面バッファをレンダリングしようと するアプリケーションやツールキットは、ウィンドウに対する全ての描画リク エストについて背面バッファ名を使用する。前面バッファをレンダリングしよ うとするアプリケーションの一部は、ウィンドウへの全ての描画リクエストに ついて、ウィンドウ XID を使う。

複数のクライアントやツールキットは、同一ウィンドウ上でダブルバッファリ ングを使用することができる。 DBE はウィンドウがダブルバッファをサポートしているか問い合わせるリクエ ストと、サポートしている場合に背面バッファ名を問い合わせるリクエストを 提供しない。X ウィンドウシステムの非同期性により、これは競合状態を起こ すからである。代わりに、DBE は同じウィンドウに対して複数の背面バッファ 名が存在することを許す。これらの全ては同じ物理的背面バッファを参照する。 ウィンドウに対して初めて背面バッファ名が割り当てられた時、そのウィン ドウはダブルバッファを持つようになり、背面バッファ名がそのウィンドウに 対応づけられる。

続いて、新しい背面バッファが割り当てられた時、その新しい背面バッファ 名がウィンドウに関連している場合を除き、ウィンドウは既にダブルバッファ ウィンドウであり、そのウィンドウは何も変化しない。 ウィンドウが破棄されるか、ウィンドウに対する全ての背面バッファ名が解放 されるまで、そのウィンドウはダブルバッファを持つ。

一般に、前面バッファと背面バッファは同じように扱われる。特に重要な特性 がいくつかある:

ウィンドウ1つに対しては、1つのバッファだけが同時に可視状態になれる(前 面バッファ)。 ウィンドウに対応づけられているどちらのバッファも、同じビジュアル型、 深さ、幅、高さ、ウィンドウ形状を持つ。 ウィンドウに対応づけられているどちらのバッファも、同じように「可視状態」 (あるいは「隠された状態」)になる。ウィンドウに対して Expose イベントが 生成されたとき、このイベントは両方のバッファに同じように適用されるもの と考えられる。ダブルバッファを持つウィンドウが露出したとき、どちらのバッ ファもウィンドウの背景色で塗りつぶされる。背面バッファが不可視であって も、隠されるなどの条件は前面バッファと同様に背面バッファにも適用される。

ドロウアブルを引き数に取る関数には、いつ XdbeBackBuffer を渡してもかまわない。 これによりアプリケーションは、他のドロウアブルに描画するのと同じように XdbeBackBuffer に直接描画することが可能となる。

Window 型の値を引き数に取る関数に XdbeBackBuffer を渡すとエラーになる。

XdbeBackBuffer は、Window 型が指定されている返り値、イベント、エラーとして送られてく ることはない。

バッキングストアやセーブアンダーがダブルバッファのウィンドウに適用され た場合、これは両方のバッファに同じように適用される。

XClearArea()XClearWindow() がダブルバッファのウィンドウで実行された場合、前面バッファと背面バッファ の同じ領域がクリアされる。

ドロウアブルを受け取る関数にウィンドウを渡したときの動作は、この拡張で も変わらない。ウィンドウと前面バッファは互いに同義である。これは、グラ フィックスコンテクストが関係していれば XGetImage()XGetSubImage() の意味や、サブウィンドウモードの意味に従うことを含む。ウィンドウが明示 的に XGetImage()XGetSubImage() に渡されているかどうか、あるいは明示的に参照されているかどうか(つまり、 祖先ウィンドウが関数に渡された)にかかわらず、前面(つまり可視の)バッファ が常に参照される。 このため、 DBE ネイティブのスクリーンをダンプするクライアントは、常に 前面バッファを取得する。 背面バッファに対する XGetImage()XGetSubImage() は、イメージの範囲内にある、背景バッファの隠された領域に対する未定義の イメージ内容を返す。

背景バッファへの描画は、GC の subwindow モードが ClipByChildren である 前面バッファへの描画で使われるはずのクリップ領域を使用する。 ダブルバッファのウィンドウの祖先ウィンドウが subwindow モードが IncludeInferiors である GC を使って描画される場合、ダブルバッファウィ ンドウの背景バッファに対する影響は、ダブルバッファのウィンドウと祖先ウィ ンドウの深さに依存する。深さが同じであれば、ダブルバッファのウィンドウ の背景バッファの内容は変化しない。深さが異なる場合は、ダブルバッファの ウィンドウの背景バッファの内容は、IncludeInferiors の描画が行われたピ クセルについて未定義である。

DBE で追加される新しいイベントはない。DBE は XdbeBackBuffer と呼ばれる新しいドロウアブルのタイプを追加する以外には、現在のイベント の意味も拡張しない。

リクエストへの応答として、ドロウアブルを含む(例えば GraphicsExpose)イ ベント、返り値、エラーが生成された場合、このドロウアブルはリクエストで 指定されたものになる。

DBE はダブルバッファリングをサポートしているビジュアルを知らせる。

DBE はタイミング調整や同期の機能は持っていない。このような機能が必要な (例えば、フレームレートの管理をする)アプリケーションは、X コンソーシア ムの標準規約である Synchronization Extension を参照すること。

ウィンドウ管理操作

DBE の基本的な考え方は、どちらのバッファも X ウィンドウの管理操作と同 じ方法で扱うことである。

ダブルバッファのウィンドウが破棄されたとき、ウィンドウに関係する両方の バッファは破棄され、ウィンドウに関係する全ての背面バッファ名は解放され る。

ダブルバッファのウィンドウのサイズが変更された場合、どちらのバッファも 新しいサイズとなる。ウィンドウのサイズが大きくなった場合、バッファへの 影響は実装がバッファの gravity ビット値を尊重するかどうかに依存する。 gravity ビット値が実装されていれば、両方のバッファの内容はウィンドウの gravity ビット値に従って移動し、残りの領域はウィンドウの背景色で塗りつ ぶされる。どちらの場合も、ウィンドウの背景色で塗りつぶされた領域に対し て Expose イベントが生成される。 関数 XGetGeometry()XdbeBackBuffer に対して実行された場合、返される x, y 座標、境界幅は 0 になる。

Shepe 拡張 ShapeRectangles, ShapeMask, ShapeCombine, ShapeOffset がダブルバッファのウィンドウに対して実行された場合、両方のバッファは新 しいウィンドウ形状に一致するように変形される。形状の差分 (D = 新形状 - 旧形状) は、どちらのバッファでもウィンドウの背景色で塗り つぶされ、D に対して Expose イベントが生成される。

複雑なスワップ動作

DBE は補助バッファ(例えば、深さのバッファやアルファバッファ)の明示的な 情報は持たず、既定のスワップ動作の制限されたセットしか持たない。アプリ ケーションによっては、DBE が提供するよりも高機能のスワップ動作のセット が必要かもしれない。DBE の実装によっては、補助バッファの情報を持ち、高 機能のスワップ動作のセットを提供できる。スワップ動作のセットを増やして 連続的に DBE を拡張する代わりに、DBE は柔軟な「イディオム」の機構を提 供する。アプリケーションの需要が既定のスワップ動作で満たされた場合は、 それを利用すべきである。そうでない場合は、以下に述べる、複雑なスワップ 動作をイディオムとして表現する方法を使うべきである。この方針に従うこと によって、幅広い実装に渡って最も有効な性能を保証することができる。

「イディオム(慣用句)」の語が示すように、複雑なスワップ動作はリクエスト のグループ/列として表される。このリクエストのグループをまとめ、性能を 最大化するため、実装による atomic な動作に組み込むことができる。実際に は最適化のために認識されるこのイディオムのセットは、実装依存である。イ ディオムの表現と解釈を補助するため、イディオムは次の2つの関数呼び出し で挟まなければならない: XdbeBeginIdiom(), XdbeEndIdiom(). 開始-終了の組でイディオムを挟んでいない場合は、実装による認識は行えず、 実行性能は悪くなる。

例えば、アプリケーションが2つのウィンドウのバッファを交換し、背面バッ ファの特定のプレーンだけクリアしようとした場合、アプリケーションは以下 の呼び出しを以下の順序でグループとする:

XdbeBeginIdiom().

2つのウィンドウに対する XID を用いた XdbeSwapBuffers(). どちらのウィンドウもスワップ動作 Untouched を使用する。

片方のウィンドウの背面バッファに対する XFillRectangle().

もう片方のウィンドウの背面バッファに対する XFillRectangle().

XdbeEndIdiom().

関数 XdbeBeginIdiom()XdbeEndIdiom() はそれ自体では動作は行わない。特定のリクエストのグループ/列をイディオ ムとして結合することができる実装にはマーカーとして扱われ、そうでない実 装や認識できないリクエストのグループ/列の場合は無視される。これらの関 数呼び出しの順序がおかしい場合や対応が取れていない場合には、エラーは送 られず関数は通常通り実行されるが、性能は悪くなってしまう。

XdbeSwapBuffers() をイディオムに含める必要はない。例えばスワップ動作 Copied が必要だが一 部のプレーンしかコピーすべきでない場合には、 XdbeSwapBuffers() ではなく XCopyArea() が使われる。 XdbeSwapBuffers() がイディオムに含まれる場合、これは XdbeBeginIdiom() の呼び出しの直後でなければならない。また、 XdbeSwapBuffers() がイディオムに含まれる場合、そのリクエストのスワップ動作は正しいが、ス ワップ動作が他のリクエストと重なっていれば、イディオムの最後のリクエス トは、別々のリクエストが連続して実行されるように行われなければならない。 例えば、指定したスワップ動作が Untouched であり、 XdbeSwapBuffers() の呼び出しの後にウィンドウの背面バッファに対してクライアントのクリップ 長方形を用いた XFillRectangle() が行われた場合、新しい背面バッファ(イディオムの後)は、イディオムが実装 に認識されなかったかのようになる。

クライアントアプリケーションが共通のイディオムをカプセル化した1つの手 続きを呼び出せるようになる「簡易」関数を、API 提供者が定義し、アプリケー ション開発者が利用することが強く求められる。これらの関数は、 XdbeBeginIdiom(), イディオム、 XdbeEndIdiom() の呼び出しを生成する。これらの関数の使用は、広範囲の実装に渡っての最善 の性能を保証する。