ComputeShader の使い方はドキュメントも少なくまだよくわかっていません。
使用できる機能の共通性などから、PixelShader に近いか PixelShader の改良版
ではないかと予想していました。でも思ったより違いは多そうです。
Technical Preview (Beta) の段階なので、まだこれから仕様が変わる可能性もあります。
●作成と実行
シェーダーの生成はこれまでの PixelShader 等と同じです。
ShaderLinkage を指定できる点も同じ。
ステート設定は CS~ 系の命令を使います。
他のシェーダーとの違いは、任意のアドレスに書き込み可能なリソース UAV を
設定する専用命令 CSSetUnroderdAccessViews() があることです。
UAV (UnorderdAccessView) は、RWBuffer や RWTexture2D 等の書き込み可能な
リソースへのアクセスを意味しています。
ComputeShader はストリーム出力を持たないので Shader は void 宣言されます。
値を返すには必ず出力先として UAV の設定が必要となるようです。
UAV は PixelShader でも使えるはずなのに SetUnorderdAccessView 相当の命令は
ありません。この辺の仕様は要調査です。
将来 10 世代 GPU 対応の ComputeShader 4.0/4.1 が使えるようになった場合も
出力先の扱いがどうなるか気になるところです。
ComputeShader の実行は Draw() ではなく専用命令 Dispatch() を使います。
iContext->Dispatch( 1, 1, 1 );
引数はマニュアルに載ってませんが、走らせるスレッドの数と考えられます。
おそらく CUDA や AMD Stream SDK の thread や block、domain 等に相当する
パラメータでしょう。
●ウィンドウレス実行
ComputeShader は描画とは関係なく実行できるはずです。
そこで Window も DXGI も使わずにシェーダーを走らせてみました。
下記のシェーダーを実行すると結果は「360」。(45 x 2 x 4)
Windows や Present() とか無しにシェーダーが走りました。
Reference では InterlockedAdd でなく直接 a[0]+= tid.x と書いても同じ値に
なってしまいます。要注意です。
実際は Query 等を使って GPU の実行を待つ必要があるかもしれません。
●リソースの受け渡し
GPU が書き込めるリソースは D3D11_USAGE_DEFAULT だけなので、UAV は全部
D3D11_USAGE_DEFAULT になります。D3D11_USAGE_DEFAULT だと CPU で直接
読み出せないため、システム側に D3D11_USAGE_STAGING のミラーバッファを用意し
アクセスの度に CopyResource() しています。
初期化手順も多く、あまり簡単とはいえません。
CPU 側との連携やデータの読み書きの容易さを考えると、CUDA や AMD Stream SDK
を直接使った方が便利だと思います。おそらく CopyResource 相当の転送も自動で
やってくれています。
ComputeShader を使うメリットは、GPU 毎の SDK の違いを吸収できることと
Direct3D との連携の容易さでしょう。
Structured Buffer や Append Buffer も試そうとしましたが、フラグ設定や
組み合わせが複雑でなかなかうまくいきませんでした。
関連エントリ
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
使用できる機能の共通性などから、PixelShader に近いか PixelShader の改良版
ではないかと予想していました。でも思ったより違いは多そうです。
Technical Preview (Beta) の段階なので、まだこれから仕様が変わる可能性もあります。
●作成と実行
シェーダーの生成はこれまでの PixelShader 等と同じです。
ShaderLinkage を指定できる点も同じ。
ID3D11Device* iDevice; ID3D11ComputeShader* iCS; iDevice->CreateComputeShader( blob, blobsize, NULL, &iCS );
ステート設定は CS~ 系の命令を使います。
ID3D11DeviceContext* iContext; iContext->CSSetShaderResources( 0, 1, srvlist ); iContext->CSSetShader( iCS, NULL, 0 ); UINT uavcount= 256; iContext->CSSetUnroderdAccessViews( 0, 1, &iUAV0, &uavcount );
他のシェーダーとの違いは、任意のアドレスに書き込み可能なリソース UAV を
設定する専用命令 CSSetUnroderdAccessViews() があることです。
UAV (UnorderdAccessView) は、RWBuffer や RWTexture2D 等の書き込み可能な
リソースへのアクセスを意味しています。
ComputeShader はストリーム出力を持たないので Shader は void 宣言されます。
値を返すには必ず出力先として UAV の設定が必要となるようです。
UAV は PixelShader でも使えるはずなのに SetUnorderdAccessView 相当の命令は
ありません。この辺の仕様は要調査です。
将来 10 世代 GPU 対応の ComputeShader 4.0/4.1 が使えるようになった場合も
出力先の扱いがどうなるか気になるところです。
ComputeShader の実行は Draw() ではなく専用命令 Dispatch() を使います。
iContext->Dispatch( 1, 1, 1 );
引数はマニュアルに載ってませんが、走らせるスレッドの数と考えられます。
おそらく CUDA や AMD Stream SDK の thread や block、domain 等に相当する
パラメータでしょう。
●ウィンドウレス実行
ComputeShader は描画とは関係なく実行できるはずです。
そこで Window も DXGI も使わずにシェーダーを走らせてみました。
D3D11CreateDevice(
NULL,
//D3D_DRIVER_TYPE_HARDWARE,
//D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
NULL,
D3D11_CREATE_DEVICE_DEBUG,
NULL,
0,
D3D11_SDK_VERSION,
&iDevice,
NULL,
&iContext
);
// hlsl 読み込みは省略 → memory
ID3DBlob* codeblob;
D3DCompile(
memory,
(size_t)size,
fname,
NULL, // macro
NULL, // include
"main",
"cs_5_0",
D3D10_SHADER_ENABLE_STRICTNESS,
0,
&codeblob,
&errblob
);
iDevice->CreateComputeShader(
codeblob->GetBufferPointer(),
codeblob->GetBufferSize(),
NULL, &iCS );
codeblob->Release();
// 出力先
ID3D11Buffer* iBufferC;
D3D11_BUFFER_DESC bdesc;
bdesc.Usage= D3D11_USAGE_DEFAULT;
bdesc.BindFlags= D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS;
bdesc.CPUAccessFlags= 0;
bdesc.MiscFlags= 0;
bdesc.ByteWidth= sizeof(float)*4*256;
bdesc.StructureByteStride= sizeof(float);
iDevice->CreateBuffer( &bdesc, NULL, &iBufferC );
ID3D11UnorderedAccessView* iUAView;
D3D11_UNORDERED_ACCESS_VIEW_DESC uavdesc;
uavdesc.Format= DXGI_FORMAT_R32_UINT;
uavdesc.ViewDimension= D3D11_UAV_DIMENSION_BUFFER;
uavdesc.Buffer.FirstElement= 0;
uavdesc.Buffer.NumElements= 256;
uavdesc.Buffer.Flags= 0;
iDevice->CreateUnorderedAccessView( iBufferC, &uavdesc, &iUAView );
// CPUアクセス用バッファ
ID3D11Buffer* iBufferC2;
D3D11_BUFFER_DESC bdesc;
bdesc.Usage= D3D11_USAGE_STAGING;
bdesc.BindFlags= 0;
bdesc.CPUAccessFlags= D3D11_CPU_ACCESS_WRITE|D3D11_CPU_ACCESS_READ;
bdesc.MiscFlags= 0;
bdesc.ByteWidth= sizeof(float)*4*256;
bdesc.StructureByteStride= sizeof(float);
iDevice->CreateBuffer( &bdesc, NULL, &iBufferC2 );
// 初期データ書き込み
D3D11_MAPPED_SUBRESOURCE mapres;
iContext->Map( iBufferC2, 0, D3D11_MAP_WRITE, 0, &mapres );
unsigned int* ptr= reinterpret_cast<unsigned int*>( mapres.pData );
ptr[0]= 0;
iContext->Unmap( iBufferC2, 0 );
iContext->CopyResource( iBufferC, iBufferC2 );
// 実行
for( int i= 0 ; i< 4 ; i++ ){
iContext->CSSetShader( iCS, NULL, 0 );
UINT uavcount= 256;
iContext->CSSetUnorderedAccessViews( 0, 1, &iUAView, &uavcount );
iContext->Dispatch( 10, 2, 1 );
}
// 結果読み込みと表示
iContext->CopyResource( iBufferC2, iBufferC );
D3D11_MAPPED_SUBRESOURCE mapres;
iContext->Map( iBufferC2, 0, D3D11_MAP_READ, 0, &mapres );
unsigned int* ptr= reinterpret_cast<unsigned int*>( mapres.pData );
printf( "%d\n", ptr[0] );
iContext->Unmap( iBufferC2, 0 );
下記のシェーダーを実行すると結果は「360」。(45 x 2 x 4)
Windows や Present() とか無しにシェーダーが走りました。
// cs.hlsl
RWBuffer<uint> a;
[numthreads(1,1,1)]
void main( uint3 tid : SV_DispatchThreadID )
{
InterlockedAdd( a[0], tid.x );
}
Reference では InterlockedAdd でなく直接 a[0]+= tid.x と書いても同じ値に
なってしまいます。要注意です。
実際は Query 等を使って GPU の実行を待つ必要があるかもしれません。
●リソースの受け渡し
GPU が書き込めるリソースは D3D11_USAGE_DEFAULT だけなので、UAV は全部
D3D11_USAGE_DEFAULT になります。D3D11_USAGE_DEFAULT だと CPU で直接
読み出せないため、システム側に D3D11_USAGE_STAGING のミラーバッファを用意し
アクセスの度に CopyResource() しています。
初期化手順も多く、あまり簡単とはいえません。
CPU 側との連携やデータの読み書きの容易さを考えると、CUDA や AMD Stream SDK
を直接使った方が便利だと思います。おそらく CopyResource 相当の転送も自動で
やってくれています。
ComputeShader を使うメリットは、GPU 毎の SDK の違いを吸収できることと
Direct3D との連携の容易さでしょう。
Structured Buffer や Append Buffer も試そうとしましたが、フラグ設定や
組み合わせが複雑でなかなかうまくいきませんでした。
関連エントリ
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver