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

D3D10 Geometry Instancing の問題と

「Direct3D10 の Geometry Instancing の問題と機能の解説」
Direct3D9 の ShaderModel3.0 から Geometry Instancing に対応し、
Direct3D10 では必須機能となりました。Geometry Instancing がなぜ重要
なのか、また D3D9 での機能については前回こちらで触れました。
Direct3D Geometry Instancing
今回は D3D10 での Geometry Instancing についてわかったことなどを
書いてみます。

D3D9 までの DrawPrimitive 命令は無くなり、D3D10 ではただの Draw 命令
となりました。これでいちいちプリミティブ数を計算して求める必要が無くなり
ました。頂点数やインデックス数だけで描画命令を発行できます。

●頂点指定の描画命令
 Draw()
 DrawInstanced()

●インデックス指定の描画命令
 DrawIndexed()
 DrawIndexedInstanced()

~Instanced() 命令は、それぞれの描画命令に繰り返し機能が付いたバージョンです。
繰り返す回数を指定することで、InputLayout の D3D10_INPUT_PER_VERTEX_DATA
指定した頂点バッファが、繰り返し数分だけ何度も読み込まれます。

例えば 24頂点の Cube の描画は

 iDevice->Draw( 24, 0 );

ですが、これを繰り返し回数 (InstanceCount) 8 回 で描画すると

 iDevice->DrawInstanced( 24, 8, 0, 0 );

となります。このとき同じ場所に同じモデルを 8回上書きするだけなので、
描画負荷が上がる以外に特に意味はありません。

この 8回の描画位置を何らかの方法で指定してあげる必要があります。
さまざまな方法が考えられますが、例えば Shader 内でも System Value
Semantic の SV_InstanceID を使って何個目の描画なのか取得することが
できます。Constant Buffer で Geometry を渡して Index に使えば
任意の位置に描画することが可能です。

Constant Buffer は容量的な上限があります。またアドレッシングしなくても
効率よくデータを読み込めるように、専用 Stream を使うことができます。
この Stream には区別のために D3D10_INPUT_PER_INSTANCE_DATA を指定します。

D3D10_INPUT_PER_INSTANCE_DATA 指定された Stream は、読み進めるための
ポインタを Instance 毎にしか更新しません。同一 Instance の頂点には
すべて同じ値が渡されるわけです。

これがまさに Direct3D9 の Geometry Instancing と同じもので、本来は
この使い方を想定して API の設計が行われているようです。
対して最初の Stream を使わない Direct3D10 独自の手法を
Stream Less Instancing と名付けておきます。

Per Instance Stream も単なる Buffer であって基本的には頂点と変わりません。
Draw() に DrawIndexed() があるように、Per Instance Stream でももっと
凝ったことをやろうとすると Index が欲しくなるでしょう。

Index とまでは行きませんが、D3D10 では通常の stream だけでも多少凝った
指定ができるようになっています。D3D10_INPUT_PER_INSTANCE_DATA にも同じ
ストリームを多重読み込みできるように、個別に読み込み回数を指定可能です。
ローカルなループを構成できるわけです。

このローカルな回数は InputLayout を作るときに設定します。
InstanceDataStepRate です。

この機能を使えば、繰り返し回数 (InstanceCount) = 8回のデータでも、
2個の Per Instance Data をそれぞれ 4回ずつ描画する
(InstanceDataStepRate = 4)、といった柔軟な設定が可能になります。

このとき D3D10_INPUT_PER_INSTANCE_DATA の Stream は 2個のジオメトリ
分の大きさですが、DrawInstanced()/DrawIndexedInstanced() には
InstanceCount = 8 を設定します。

ここで今の DirectX Runtime (June2007) には若干問題があって、
Debug Build だと Instance 用の VertexBuffer が描画回数に比べて
少なすぎるという WARNING が毎フレーム発生してしまいます。

[EXECUTION WARNING #356: DEVICE_DRAW_VERTEX_BUFFER_TOO_SMALL]

どうやら判定時に InstanceDataStepRate を見ていないようです。
当初自分のアプリケーションの問題かと思っていたのですが、
Sample の Instancing10 でも発生していました。

とりあえず、Direct3D10 の Geometry Instancing は、無理やり拡張したような
D3D9 と比べてかなりシェーダーで自由に扱うことができます。
情報が少ないのがネックですがかなり思い通りに使うことができるようです。

InstanceDataStepRate はいわゆる通常の Geometry Instancing に
さらに Stream Less Instancing を混在利用しているわけですし、
Texture/Buffer 読み込みを使えば Instance Data の Index 化もおそらく
可能でしょう。