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

D3D10/DX10 Effect Interface内情報

D3D10 では D3D9 と違い、Effect (fx) API は D3DX ではなく D3D10 に
含まれています。インターフェースは ID3D10Effect で、シェーダーの
コンパイルや fx に含まれる各種変数やステート情報へのアクセスなどが
できるようになっています。

従来どおり個別の VertexShader / GeometryShader / PixelShader も
扱うことができ、こちらは Shader インターフェースとしてまとめられています。
・ID3D10GeometryShader
・ID3D10PixelShader
・ID3D10VertexShader

D3D10Effect から各種シェーダーを直接取り出す手順はこんな感じになりました。

// vertex shader
D3D10Effect* ieffect; // input
D3D10VertexShader* ivsh= NULL; // output
D3D10EffectTechnique* itechnique= ieffect->GetTechniqueByName( “Main” );
D3D10EffectPass* ipass= itechnique->GetPassByIndex( 0 );
if( ipass->IsValid() ){
 D3D10_PASS_SHADER_DESC shdesc;
 ipass->GetVertexShaderDesc( &shdesc );
 ID3D10EffectShaderVariable* ishadervar= shdesc.pShaderVariable;
 if( ishadervar->IsValid() ){
  ishadervar->GetVertexShader( shdesc.ShaderIndex, &ivsh );
 }
}

これは technique Main の Pass0 の VertexShader を参照しています。
このとき、得られた D3D10VertexShader の参照カウンタが増加するので、
不要になったら ivsh には Release() が必要です。

それに対して D3D10EffectShaderVariable 等、Reflection 系のアクセス
インターフェースは COM ではないので参照カウンタがありません。
ID3D10ShaderReflectionVariable 等も全く同じです。
D3D10 では最初はこの辺の区別がつきづらく、慣れるまで少々分かりにくく
なりました。

ここでちょっとはまった注意点として GeometryShader があります。
というのは、Effect(fx) 内部で GeometryShader に NULL を設定していても
ipass->GetGeometryShaderDesc() が成功して D3D10EffectShaderVariable* を
参照できてしまうからです。IsValid() も通過します。
どうやら内部で static の NULL object を用意しているらしく、
GeometryShader に NULL を設定した場合はどの D3D10Effect でも全く同じ
D3D10EffectShaderVariable のアドレスが返ってきました。
D3D10EffectShaderVariable::GetGeometryShader() では NULL が返るので、
このことが分かっていれば全く問題はありません。

shader からさらに D3D10ShaderReflection を取得するには次のようになります。

ID3D10EffectShaderVariable* ishadervar; // input
ID3D10ShaderReflection* iref= NULL; // output
D3D10_EFFECT_SHADER_DESC efsdesc;
ishadervar->GetShaderDesc( &efsdesc );
D3D10ReflectionShader( efsdesc.pBytecode, efsdesc.BytecodeLength, &iref );

Reflection 等から変数の要素1つ1つ調べるには ~Variable から ~Type を
取得して調べていきます。
例えば Effect なら D3D10EffectType があり、Shader なら
D3D10ShaderReflectionType を使います。
Variable が構造体なら、さらに GetMemberTypeByIndex() 等で内部の
メンバー情報を参照していく必要があるようです。

もちろん変数名等を使って Effect が管理するバッファに値を格納するだけなら
Reflection とか考えずに
 GetVariableByName() → AsVector() → SetFloatVector()
とか使えば良いので簡単です。
だけど D3D9 と違って StateManager とか使わずに API だけでもきちんと
全情報にアクセスできるみたいなので、Effect 内の管理に頼らないで
パラメータ管理自前で乗っ取っても大丈夫そうです。
そういえば、変数名でなく取得したハンドルでのアクセス方法は
なくなっているみたいですね。