日別アーカイブ: 2007年7月9日

Direct3D Geometry Instancing

同一形状のモデルを同時に大量に描画する場合は、DirectX9 の
ShaderModel3.0 から導入された GeometryInstancing を使うことができます。
Geometry Instancing は同一の頂点バッファの内容を繰り返し描画できる
機能のことで、頂点ストリームの読み取り周期をずらしてスケジューリング
することで実現しています。

同じ形状のモデルを何個も描画する場合は、その数だけ毎回 Draw 呼び出し
を行うことになります。
WindowsPC 上の DirectX のボトルネックとして有名なのがこの Draw 呼び
出し回数の問題です。1フレームに呼び出す回数が増えるとそれだけで CPU
の負荷が高くなります。
CPU Emulation Driver (HEL) のように HAL を通さない場合や、Xbox の
ように API レイヤが薄い場合はここまで顕著な傾向は現れません。

このオーバーヘッドを軽減するアイデアとして登場したのが
Geometry Instancing です。複数のオブジェクトの描画を一度の Draw
呼び出しで済ませるための工夫といえるでしょう。

注意点は、Geometry Instancing によって恩恵を得られるのはあくまで
CPU 側だということです。必ず描画が速くなるわけではないし、pixel
負荷などで GPU がボトルネックの場合はほとんど速度が変わらない
可能性があります。

GPU の負担が軽くなる要素としては、パイプラインを止めないで済む
ということ。また Constant Register 等の更新が不要になる分効率良く
なるかもしれません。その代わり VertexShader で特殊なストリーム入力
のための負担が増えるのと、16本しかない入力パラメータを圧迫します。

まず描画回数を減らす方法として考えられるのは、必要なオブジェクト
を全部マージしてしまうことです。同じモデルであればマテリアルも共有
されるのでいっぺんに描画することができるでしょう。
Draw 回数のオーバーヘッドは激減しますが、各オブジェクトを個別に
動かしたり位置や向きを変更することができませんし、同一であるはずの
データを頂点バッファに展開するのでメモリ消費も多くなります。

Geometry Instancing ではこの考えをさらに進めて、描画すべき位置の
情報 (Geometry, Matrixなど) を頂点と同じようにストリームで渡す
ことにしました。
Geometry 情報をあらかじめ頂点バッファに書き込んでおきます。
これでマージされたオブジェクトながら、個々のオブジェクトを異なる
座標に配置できるようになります。

Geometry 情報を頂点単位に持たせるとメモリも馬鹿にならないし、
座標更新のために頂点数分書き換えなければなりません。
そのため次の2つのちょっとした追加機能が必要になりました。

・描画するモデル側は何度も繰り返し頂点バッファを読めるようにする

  24頂点の Cube を 10個描画するなら、24頂点の同じバッファを
  10回繰り返して読み込めるようにするわけです。
  Index Buffer を必要回数コピーするようなもの。
  いわゆる Maya とかの Instance。

・Geomtery 情報の頂点バッファは、1モデル単位で読み進める

  つまり 24頂点の Cube があれば、24頂点には同じ Geometry
  が渡されるようにします。

特殊な頂点バッファの使い方に見えますが、これが最小の追加で高い
効果を得られる方法だったのだと考えられます。

Geometry だけでなく、マテリアルカラーなども頂点ストリームで
入力できるので、個々に色や属性等も変更することが可能です。
これらが全部マージされたのと同じように一度の Draw 呼び出しで
描画できるわけです。

ShaderModel3.0 は ConstantRegister 容量の制約があるので、
異なるスキニングモーションをしているキャラクタを一度の Draw
で描画するのは少々難しいかと思います。

すべて同じポーズなら簡単です。例えば 30 frame 分のモーション
なら、各フレーム毎に描画をわけて、30回の Draw で済ませることは
できるかもしれません。
Geometry Instance 系の簡単なアニメーションデモなどはこの方法で
十分だと思います。

骨の数が少なければ、ストリームに Bone 用 Constant Register の
オフセットも入れておくことで、数個の異なるポーズのモーションを
一度に描画することはできるかもしれません。

Geometry Instance は ShaderModel3.0 から付いた機能ですが一部
例外があります。ATI(AMD) RADEON 9700~X800系の場合は、
ShaderModel2.0 であるにもかかわらずドライバのエミュレーション
で Geometry Instance を使うことができました。
あらかじめ Video Card のコントロールパネルで有効に設定して
おくことができます。
エミュレーションなのでパフォーマンス的には限界があるものの
Geometry Instance のコードがきちんと動作します。

このとき Direct3D9 の設定が Debug になっているとエラーチェックに
引っかかってしまいます。
「ShaderModel2.0 なのに Geometry Instance を使っている」
という旨のエラーが出て止まってしまうわけです。
エラーチェックの無い Retail モードだと大丈夫です。

Direct3D10 では DrawInstanced / DrawIndexedInstanced によって
最初から Geometry Instancing の描画がサポートされています。