日別アーカイブ: 2008年11月8日

Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他

引き続き DirectX SDK Novemver 2008 より、Direct3D 11 の Technical Preview です。
今まで取り上げたことがないもの中心。

●リソースサイズ

仕様として扱えるリソースの上限が拡張されています。
4GByte (32bit) を超えるリソースも扱えるらしく、内部的には 64bit で管理されて
いるようです。すでに VRAM 2GB の製品は存在しているので、4GB を超えるのも
時間の問題と思われます。

リソースサイズが 32bit を超えていても、データアクセス時の index は 32bit までに
制限されます。shader に double は追加されたものの 64bit int 型はまだ無いので
当然かもしれません。

●64bit 整数

HLSL では long/ulong 型が 64bit 整数となるようです。
double 対応前の HLSL コンパイラでは double 型を使うと float 相当の扱いでした。
(D3D10/DX10 シェーダーと64bit浮動少数)
新しい fxc で long/ulong を使うと未サポートエラーになります。

: error X3653: ‘v’: ps_5_0 does not support 64-bit integers
: error X3653: ‘v’: cs_5_0 does not support 64-bit integers

●fxc プロファイル

付属の fxc.exe は単一で、d3d9/d3d10/d3d11 共通になっています。
fxc の対応プロファイルを見ると、ComputeShader は ShaderModel 4.0/4.1 でも
使えそうです。

fx_2_0
fx_4_0
fx_4_1

cs_4_0  // ← ComputeShader 4.0?
cs_4_1  // ← ComputeShader 4.1?
cs_5_0

gs_4_0
gs_4_1 
gs_5_0

ds_5_0  // DomainShader

hs_5_0  // HullShader

ps_2_0
ps_2_a  // NVIDIA拡張
ps_2_b  // ATI拡張
ps_2_sw
ps_3_0
ps_3_sw
ps_4_0 
ps_4_0_level_9_1   // D3D11 の ShaderModel2.0
ps_4_0_level_9_3   // D3D11 の ShaderModel3.0
ps_4_1
ps_5_0

vs_1_1
vs_2_0 
vs_2_a  // NVIDIA拡張
vs_2_sw
vs_3_0
vs_3_sw
vs_4_0
vs_4_0_level_9_1   // D3D11 の ShaderModel2.0
vs_4_0_level_9_3   // D3D11 の ShaderModel3.0
vs_4_1
vs_5_0 

tx_1_0

前前回 FeatureLevel 9.1 と 9.2 の違いがわからないと書いたけど、これを見ると
本当にわかりません。

● RW Buffer

読み書き可能リソースが追加されています。
ついに、シェーダーが直接リソースに書き込みできるようになりました。
ストリーム以外の出力ができます。

RWBuffer
RWByteAddressBuffer
RWStructuredBuffer
RWTexture1D
RWTexture1DArray
RWTexture2D
RWTexture2DArray
RWTexture3D

書き込みできるのは PixelShader と ComputeShader のみ。
Load() メソッドではなく基本的に operator[] を使用します。
例えば Buffer では Load() と両方使えるものの RWBuffer は必ず operator[] を
使う必要があります。

Atomic 命令が使えるのは RWByteAddressBuffer だけで、こちらは逆に operator[] が
使えません。Load()/Store() や Interlocked 命令を使用します。

RWBuffer	a;
RWByteAddressBuffer	b;
Buffer		c;

float4
main() : SV_Target
{
	a[1]= 3;
	b.Store( 0, 4 );
	b.InterlockedAdd( 0, 1 );
	return	a[0]+ c.Load( 0 );
}

下記のようにコンパイルされました。(fxc /Tps_5_0)

ps_5_0
dcl_globalFlags refactoringAllowed 
dcl_resource_buffer (float,float,float,float) t0
dcl_uav_typed_buffer (float,float,float,float) u1
dcl_uav_raw u2
dcl_output o0.xyzw
dcl_temps 2
store_uav_typed u1.xyzw, l(1, 1, 1, 1), l(0x40400000, 0x40400000, 0x40400000, 0x40400000)
store_raw u2.x, l(0), l(4)
atomic_iadd u2, l(0), l(1)
ld_uav_typed r0.xyzw, l(0, 0, 0, 0), u1.xyzw
ld_indexable(buffer)(float,float,float,float) r1.xyzw, l(0, 0, 0, 0), t0.xyzw
add o0.xyzw, r0.xyzw, r1.xyzw
ret 

store_ 、atomic_ 命令が存在しておりやっと実感がわきます。
シェーダーが本当に書き込んでいます。

dcl_uav や ld_uav など、uav と付くのが読み書き可能な RW リソースを指している
ようです。t レジスタではなく u レジスタが使われています。
ちなみに 4.0 をターゲットにすると UAV をサポートしていないとのエラーが出ます。

// Resource Bindings:
//
// Name                   Type  Format         Dim Slot Elements
// ---------------- ---------- ------- ----------- ---- --------
// c                   texture  float4         buf    0        1
// a                       UAV  float4         buf    1        1
// b                       UAV    byte          rw    2        1

UAV は Unordered Access View (ID3D11UnorderedAccessView) の略だそうです。

● Append/Consume Buffer

RW Buffer や Atomic 命令だけでなく、もう少し便利な同期機能も追加されています。

AppendStructuredBuffer
AppendByteAddressBuffer
ConsumeStructuredBuffer
ConsumeByteAddressBuffer

Append がバッファの最後に追加、Consume がバッファの最後から取り出します。
LIFO として機能するようです。

RW Buffer と違い、読み込み専用、書き込み専用とそれぞれ個別に定義します。
送信側と受け側を明確にし、同期を単純化しているものと思われます。

使用可能なのはやはり PixelShader と ComputeShader のみ。
マニュアルでは一部全シェーダーで使えるかのような表記もありますがおそらく間違いでしょう。

使い方がよくわからなかったのでコンパイルのみ試したところ、PixelShader だとたまに
Internal Compiler Error が出るようです。
やはり主な用途は ComputeShader だと思われます。

ConsumeStructuredBuffer	c;
AppendStructuredBuffer	a;

[numthreads(1,1,1)]
void main()
{
	uint	t= c.Consume();
	a.Append( t );
}

コンパイルした結果は下記の通りです。(fxc /Tcs_5_0)

cs_5_0
dcl_globalFlags refactoringAllowed 
dcl_uav_structured u0, 4
dcl_uav_structured u1, 4
dcl_temps 1
dcl_thread_group 1, 1, 1
imm_atomic_consume r0.x, u0
imm_atomic_alloc r0.y, u1
ld_structured r0.x, r0.x, l(0), u0.x
store_structured u1.x, r0.y, l(0), r0.x
ret 

imm_atomic_consume / imm_atomic_alloc は、このペアを使って load や store を
行うと、atomic な操作でかつバッファサイズも増減することを意味しているようです。
例えば uint を float4 に変更すると次の通り。

ConsumeStructuredBuffer	c;
AppendStructuredBuffer	a;

[numthreads(1,1,1)]
void main()
{
	float4	t= c.Consume();
	a.Append( t );
}


cs_5_0
dcl_globalFlags refactoringAllowed 
dcl_uav_structured u0, 16
dcl_uav_structured u1, 16
dcl_temps 2
dcl_thread_group 1, 1, 1
imm_atomic_consume r0.x, u0
ld_structured r0.y, r0.x, l(0), u0.x
ld_structured r0.z, r0.x, l(4), u0.x
ld_structured r0.w, r0.x, l(8), u0.x
ld_structured r0.x, r0.x, l(12), u0.x
imm_atomic_alloc r1.x, u1
store_structured u1.x, r1.x, l(0), r0.y
store_structured u1.x, r1.x, l(4), r0.z
store_structured u1.x, r1.x, l(8), r0.w
store_structured u1.x, r1.x, l(12), r0.x
ret 

Append / Consume 操作は必ずスカラー単位で行われているようです。
これだけだと、float4 の間に他の Append/Consume が割り込んでしまいそうな
気がしますが、offset も同時に指定しているし、まだよくわかりません。

// Name                   Type  Format         Dim Slot Elements
// ---------------- ---------- ------- ----------- ---- --------
// c                       UAV  struct     consume    0        1
// a                       UAV  struct      append    1        1

バッファとしては UAV の一種で、Dim が consume 及び append となっています。

関連エントリ
Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
Direct3D11 Technical Preview D3D11の互換性、WARP Driver

Direct3D11 シェーダーの動的リンクやテクスチャ
Direct3D11 マルチスレッドのための機能
Direct3D11 テセレータとシェーダー
Direct3D11 のパイプライン
Direct3D11 Compute Shader など
Gamefest2008 と Direct3D 11