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

Direct3D10 StreamOutput

D3D10 で StreamOutput を使う場合、ID3D10GeometryShader の作成に
ID3D10Device::CreateGeometryShader() ではなく
ID3D10Device::CreateGeometryShaderWithStreamOutput() を使います。

Effect(fx) を使用している場合も、Effect(fx) ファイルの中で
 CompileShader() → ConstructGSWithSO()
の流れで、StreamOutput 対応 GeometryShader を作成します。
この辺はほとんどサンプルの記述どおりです。

出力フォーマットの記述など、扱いは Effect ファイルの方が楽なので、

・Effect(fx) ファイルに ConstructGSWithSO() で記述
・ID3D10Effect 作成
・GeometryShader のインターフェース取り出し

の手順で、WithStreamOutput の GeometryShader を簡単に取り出す
ことができます。

普段 GeometryShader を使わない場合は NULL 省略できますが、
StreamOutput では上記方法で GeometryShader を作らなければなりません。

少々不思議なのは D3D10 のサンプルでは、GeometryShader が不要な場合に
ConstructGSWithSO() にコンパイルした VertexShader を渡していること。

例えばこんな感じで使われており、実際にコンパイルしてみても
GeometryShader に VertexShader が入っていました。

VertexShader vs_transform= CompileShader( vs_4_0, VS_Transform() );
GeometryShader gs_transform= ConstructGSWithSO( vs_transform,
  ”POSITION.xyzw;NORMAL.xyz;TEXCOORD.xy” );

StreamOutput 先のバッファを指定するには ID3D10Device::SOSetTargets()
を使います。また同時に現在 SO Stage が有効かどうかの切り替えもこの
SOSetTargets() で行われています。

D3D10_BIND_STREAM_OUTPUT で作った ID3D10Buffer を渡すか NULL を渡すか、
これだけです。

StreamOutput を使う場合は PixelShader を通らないので、PixelShader には
NULL を設定しなければなりません。
さらに Depth/Stencil Buffer が有効になっていると、Pixel Pipeline が
有効とみなされるようです。

StreamOutput 時は Depth/Stencil test を Disable しなければなりません。
この切り替えはもちろん ID3D10DepthStencilState です。

・Shaderやバッファ作成
 CreateGeometryShaderWithStreamOutput()
 CreateDepthStencilState()
 CreateBuffer( D3D10_BIND_STREAM_OUTPUT )

・描画時
 OMSetDepthStencilState( ZENABLE= D3DZB_FALSE , ZWRITEENABLE= FALSE )
 PSSetShader( NULL )
 SOSetTargets( iBuffer )
 Draw( … )
 SOSetTargets( NULL )

バッファ内の StreamOutput 位置は SOSetTargets() の Offset で
指定することができます。

位置が重ならないようにリソースを作ったり Stream に入れたりしても
やはり入出力に同じ Buffer を与えることはできませんでした。

ちなみに GeometryShader で複数頂点を入力して Triangle 出力すると
Index が展開されるので、DrawIndexed() 用の mesh データでも
Draw() での描画になります。
Triangle の増減も可能なのだから仕方ないですが、頂点 Cache の効率を
考えると少々もったいないですね。

GeometryShader が不要で頂点単位の事前変換だけ行う場合は、
StreamOutput 時に POINTLIST を使います。
この場合 Triangle の増減はできないものの、出力頂点数は変化ないので
事前変換しつつ DrawIndexed() を使うことができます。