ComputeShaderが呼べるようになるまで

前回から何かこう、とてつもなく初歩レベルでつまづいているような気がするけど、まぁ新しい環境でプログラムを作るときはだいたいこんなモンだよね。って感じで作業内容をさらにメモ。


とりあえず今回は、ComputeShaderで何か適当に動かすところまでやってみた。


結論としては

#include 		// D3DX11CompileFromFile
#define __in_bcount(x)
#include 		// ID3DBlob
#define sqrtf sqrt
#define WINAPI_INLINE inline WINAPI
#include 		// D3DXVECTOR4
#undef sqrtf			// 追加

が必要だった。

[ 2012/08/30 23:45 追記 ]
undefしておかないとfstreamをincludeしたときにエラーが出ることが判明した。他のdefineも不要な箇所に入ったらundefすべきかも。
[ 2012/08/30 23:45 追記 ]


では、1つずつはまりポイントを。


まずD3DX11CompileFromFile()でいきなりはまった。
DirectXのヘルプだと、D3DX10async.hって書いてあるけどコンパイルすると

エラー E2268 bench.cpp 79: 未定義の関数 'D3DX11CompileFromFile' を呼び出した(関数 main() )

とおっしゃる。あるぇ〜?って感じでgrepすると、どうgrepしてもD3DX11async.hです、本当にありがとうございました。って感じ。DirectXヘルプェ…
まぁ、これはそれだけだからよしとして。


次がID3DBlob。これは特にヘルプに書いてなさそうだけど、grepしたらD3Dcompiler.hだったので普通にinclude。定義してなかった__in_bcountがここで使われていたので同じようにダミー定義で回避。


最後がD3DXVECTOR4。ComputeShader呼び出しには関係ないけど、今回のテストプログラムで使ってたので。

エラー E2268 D:\soft\Microsoft DirectX SDK (February 2010)\Include\D3DX10math.inl 1562: 未定義の関数 'sqrtf' を呼び出した(関数 D3DXVec2Length(const D3DXVECTOR2*) )

これはDirectX9のときにもお目にかかったエラー。普通にsqrtにすればOK。

エラー E2139 D:\soft\Microsoft DirectX SDK (February 2010)\Include\d3dx10core.h 345: 宣言に ; がない
エラー E2040 D:\soft\Microsoft DirectX SDK (February 2010)\Include\d3dx10core.h 349: 宣言が正しく終了していない
エラー E2190 D:\soft\Microsoft DirectX SDK (February 2010)\Include\d3dx10core.h 349: 不要な }
エラー E2190 D:\soft\Microsoft DirectX SDK (February 2010)\Include\d3dx10core.h 349: 不要な }

これがちょっとわかりにくい。該当行をみると

HRESULT WINAPI_INLINE GetDesc(D3DX10_FONT_DESCA *pDesc) { return GetDescA(pDesc); }

とか書いてある。1要素ずつ改行で分けてコンパイルエラー行を分けていくと、どうやらWINAPI_INLINEでエラーになっているっぽい。んで、「宣言に ; がない」ってエラーはたいていbccが知らない識別子を見たときに吐くエラーなので、おそらくWINAPI_INLINEがincludeしてるヘッダ群の中になかったのだと思われ。名前からなんとくWINAPIとincludeの複合っぽいのでそれらしく自前でdefineして回避。



とりあえずこれでコンパイルは通るけど、しかしそれにしてもDirectXはどの関数を呼べば所望の動作をさせられるのかがとてもわかりにくい。ヘルプを見ても各巻数群の個別説明はあるけど、○×をしたいときに何を呼ばなきゃダメかは書いてない気がする。基本的にサンプルとかチュートリアルのソースを読めって感じなのかな。


忘れそうなので、ComputeShaderだけ動かすところまでの概要まとめ。(動作内容は推測)
1.D3D11CreateDevice()
ID3D11DeviceとID3D11DeviceContextがもらえる。たぶんGPUごとに別々っぽい。CrossFireとかしてる環境だと複数回呼び出しが必要なんじゃないかと思う。
CreateXXX系はID3D11Deviceが担当で、レンダリングパイプライン系がID3D11DeviceContextっぽい。


2.D3DX11CompileFromFile()
なんとなく、内部でfxc.exeを呼んでる気がする。コンパイル結果(たぶんILのソースコード)がID3DBlobに入って戻ってくる気がする。
[2010/05/08 18:45追記]
訂正。少なくともテキストデータではない。ソースコードと書くと語弊があるかも。


3.CreateComputeShader()
たぶんILのソースコードGPU向けのアセンブリコードか機械語かに変換してるんじゃないかと思う。


4.BufferとかShaderResourceViewとか
これがややこしい。この後別にまとめ


5.CSSetShader()
たぶんGPUにシェーダの機械語とかを転送してるんじゃないかと


6.CSSetConstantBuffers()
cbufferのデータをシェーダとくくりつけてそう。どうなってるのかわからないけど、cbufferのデータはレジスタで参照するっぽいし。cbufferとして作ったBufferのデータをレジスタ経由でシェーダに渡せるようにGPUへ指示を送ってるんじゃないかと。


7.CSSetShaderResources()
テクスチャ、その他のbuffer(StructuredBufferとか)のデータをシェーダとくくりつけてそう。cbufferも別だけど、UAV(Unordered Access Buffer)も別I/Fみたい。てか、なぜそろえない(^^;


8.Dispatch()
これでComputeShaderが動くっぽい。ちなみにサンプルとかだと後始末でシェーダとかcbufferとかShaderResourceとかをNULLに戻してるっぽい。次の呼び出しとかでComputeShaderを使わない場合に悪さをしないようにしてるのかな。



最後にややこしいBufferとかをまとめ。

  • Bufferとテクスチャ

こいつらはID3D11Resourceから派生したクラスになってる。だからデータ類は全部ID3D11Resourceってことらしい。Create関数に初期値を設定できるので、たぶんCreateした時にVRAMに初期値の転送もやってるんじゃないかと思う。

  • ShaderResourceView(SRV)

よくわからないけど、シェーダからBuffer類にアクセスするにはID3D11Resourceを元にしてSRVをCreateする必要があるっぽい。XXXSetShaderResources()系関数にもSRVをわたすI/Fになってる。たぶん、VRAM上のデータとシェーダのくくりつけをやるんじゃないかと思うけど、SRVのCreate時にはまだシェーダを設定してないから何をやっているのか不明。
ちなみにUAVだけはなぜか別でID3D11UnorderedAccessViewってのがある。なぜ分けたし。他のSRVはもしかしたらレジスタでアクセスができるのかな。UAVはランダムアクセスするからレジスタ経由じゃなくてload/store的な命令でアクセス?
さらにちなむと、シェーダ以外の機能部へID3D11Resourceを割り当てるときは別のXXXViewを作るっぽい。RenderTargetViewとか、だからなぜ分けたし。