1.はじめに...そろそろ触っておかないと不味い感じがしてきましたので,触ってみます。右も左もわからない状況なので,DirectXのサンプルにあるBasicCompute11を,少しいじってみるところから始めてみます。今回は,地味なコンソールアプリです。2.コンピュートシェーダって?コンピュートシェーダは,描画パイプラインとは切り離された独立されたシェーダです。個人的にはグラフィックスの描画とは,関係が薄いので「シェーダ」って名前をつけるのはどうなのかなぁ~なんて思ったりするするのですが...。3Dプログラミング」という本にはこんなことが書いてあります。・出力リソースの任意の位置に書き込み可能。・「データ共有」や「スレッド間同期」のメカニズム。・指定した数の「スレッド」を明示的に起動でき,パフォーマンスを最適化できる。・描画パイプラインと無関係なので,コードの保守が簡単。個人的には,「描画パイプラインと無関係」という所と「出力リソースの任意の位置に書き込み可能」という所が一番大きい所です。今までは,頂点シェーダとピクセルシェーダの間にラスタライズの処理が入ってしまうので,ちょっとごにょごにょしないといけないとか制限があったりしました。Mapsなんかの実装では,対数ラスタライズしたいんだけどGPUは線形ラスタライズしてしまうので,相殺されるようにごにょごにょしていたりします。ピクセルシェーダは各ピクセルごとの処理になるので,自分が今書き込もうとしているピクセルの隣がどうなっているかの情報を取り出すことができないので,一度テクスチャに書き出して2パス目でごにょごにょするとか,ちと面倒くさい処理になったりします。いった,今までちょっとやっかいだったものがコンピュートシェーダを使うことにより緩和できる可能性があり,もしかしたら高速化できるかもしれない可能性があります。いった高速化など実践的な使い方に入る前に,基本な使い方をを抑えていきます。演算の準備コンピュートシェーダはその名通り,演算を行うシェーダです。演算するからには,演算の入力元と出力先が必要となります。そこで,まず演算の入力用のデータと出力先データを整えることから始めていきます。コンピュートシェーダの入出力用のデータですが,シェーダではこんな感じで書きます。);ちなみに他の書き方も色々とできますが,とりあえず構造化バッファを使うコードにしてみました。StructuredBufferとRWStructuredBufferの違いですが,RWなしのほうは入力のみに使えます。RWがあるほうはReadWriteの略なので読み書き,つまり入出力に使うことができます。上のコードでは,StructuredBufferを入力データ,RWStructuredBufferを出力先として使用しています。入力はテクスチャとして用意しています。Viewとなっています。日本語に無理やり訳すとすれば非順序アクセスビューといった感じでしょうか,その名の通りすきな場所にアクセスができます。これが上述した「出力リソースの任意の位置に書き込み可能」ということになります。それでは,このInputBufferとOutBufferにあたる部分の作成処理について見ていきましょう。);まず使い方の流れですが,入力用のデータをバッファに格納して,構造化バッファを作成します。出力用のデータは初期データがいらないので,nllptrを引数で渡しています。次にシェーダ内で使うためには,シェーダリソースビューとしてやる必要があるので,CreateBufferSRV()でシェーダリソースビューを作ります。シェーダリソースビューを作る...という流れになっています。構造化バッファの実際の初期化処理は次のようになります。頂点バッファやインデックスバッファとしては使用不可。}00391:続いて,シェーダリソースビューの生成処理です。}構造化バッファの場合は,構造体の中身は当然ながらユーザーが決めるので,DXGI_FORMATにはないフォーマットになったりすると思います。構造化バッファの場合はDXGI_FORMAT_UNKNOWNをしてしておきましょう。これで,入力用データはそろいました。あとは,出力用データを作成します。ここで注意してほしいのは,いつも使ってきたShaderResourceViewは読み込み専用の用途になります。シェーダで計算したデータを書き込むことはできません。作り方は以下のような感じです。}見て分かるように,ほとんどシェーダリソースビューの生成と変わらない感じです。データの設定ができたので,あとはコンピュートシェーダを実行します。);RunComputerShader()メソッドの実装ですが,下記のようになっています。}いつもはコマンドの発行ににID3DDeviceContext::Draw()メソッドとかID3DDeviceContext::DrawIndexed()メソッドなどを使っているのですが,コンピュートシェーダではDraw()メソッドなどの代わりにDispatch()メソッドあるいはDispatchIndirect()メソッドを使用します。メソッドについての詳細な説明はMSDNライブラリを参照して下さい。引数は実行スレッドの数を指定する数値になります。今回のサンプルでは適当な数値にしました。この指定する数値ですが,シェーダファイル側の処理にも関係するので,実装者が『ええ感じ』に調整した方がよいかと思います。ちなみにどのように関するかというと下記のようになります。}このスレッド数ですが,まだコンピュートシェーダを使いはじめた状態なので,正直どういう場合にはどういう数値がいいかなどのノウハウがまだないです(だから誰か教えて!w)。今回のサンプルは超いい加減なので,サイズ1次元しか使っていないです。これで実行までできました。あとはコンピュートシェーダから演算結果を取得しましょう。そのままさくっと取りたいのですが,GPUで演算するために作っているデータなので,簡単にはいきません。一度,CPUアクセスできるバッファを用意して,そのバッファにGPUで演算した結果をコピーして,コピーした結果をCPUでアクセスします。Unmap()を使って地味にアクセスしていきます。今回のサンプルではCPUでGPUで計算しているのと同じ処理を行って,CPUの演算結果とGPUの演算結果を比較します。当然ながら,浮動小数点計算は「==」とか「!=」で比較せず,誤差範囲内に入っているかどうかでチェックすべきなのですが,面倒なので「==」とか「!=」使っちゃっています。した実装をするときには,この実装のままだと絶対に一致しないので気を付けましょう。}今回は,コンピュートシェーダを軽く触ってみました。今回の内容では,実用できるレベルに至らないと思うのであと数回かけて基本を押さえていくことにします。Licenseに準じます。Editionを用いています。