Direct3D11/DirectX11 (7) テセレータの流れの基本部分

● ComputeShader 用の Append Buffer 作成

何度も DebugLayer に怒られつつテストしてみました。

Buffer
 D3D11_USAGE_DEFAULT
 D3D11_BIND_UNORDERED_ACCESS
 D3D11_RESOURCE_MISC_BUFFER_STRUCTURED
 StructureByteStride= 4
UAV
 DXGI_FORMAT_UNKNOWN
 D3D11_BUFFER_UAV_FLAG_APPEND

AppendStructuredBuffer	a;
[numthreads(1,1,1)]
void main( uint3 tid : SV_DispatchThreadID )
{
	a.Append( tid.x );
}

バッファ内容の変化が観測できず、まだ動作がつかめていません。
うまく動いていないところや使い方の疑問点もあります。

・ID3D11DeviceContext::CSSetUnorderedAccessViews() に 2つ以上の UAV を渡すと
 シェーダーが落ちる。サンプル HDRToneMappingCS11 では 1つしか渡していない。
・CSSetUnorderedAccessViews() の 3 番目の引数の意味がわからない。
 サンプルでは 2番目のリストのアドレスを UINT* で渡しているが、これが正しい
 とは思えない。

●テセレータ

これも D3D11 での大きな追加機能の一つです。

HullShader と DomainShader を設定することで、その間を繋ぐ Tessellator は
勝手に呼び出されるようになります。
VertexShader と PixelShader の間に、暗黙のうちに Rasterizer が呼ばれるのと
よく似ています。機能も似ています。

GS と HS → DS はオプションなので、パイプラインは下記の 4 通り (+2) です。

  VS → (Rasterizer) PS
  VS → GS → (Rasterizer) PS
  VS → HS → (Tessellator) DS → (Rasterizer) PS
  VS → HS → (Tessellator) DS → GS → (Rasterizer) PS

StreamOutput:
  VS → GS → SO
  VS → HS → (Tessellator) DS → GS → SO

Tessellator を Enable にした場合使用できるプリミティブは Patch のみとなります。
PATCH は新しく追加された Primitive Topology で、1~32 までの頂点を入力可能です。

・Point     List
・Line      List, Strip, ListAdj, StripAdj
・Triangle  List, Strip, ListAdj, StripAdj
・Patch     1~32

Patch の入力頂点は、主に分割時のカーブを求めるためのコントロールポイントとして
使われます。
従来 TriangleListAdj でも最大 6頂点までだったので、入力頂点数は大幅に増えました。
試していませんが DebugLayer のメッセージを見ると GeometryShader で
Patch を直接受け取れる可能性があります。もし使えるなら汎用の多入力頂点としても
応用できそうです。

HullShader は Tessellator に渡すパラメータの生成を行います。
入力された頂点 (コントロールポイント) 毎に走る関数と、パッチ単位で一回だけ走る
関数の 2つエントリポイントがあります。

VertexShader と GeometryShader がセットになったような感じですが、どちらも
1プリミティブ(patch) 分の全頂点に自由にアクセスすることが可能です。

(1) VertexShader が頂点単位に走る
(2) Patchを構成する全頂点(ControlPoint)がキャッシュされたら HullShader を
  呼び出す。
(3) HullShader は 2つの関数が存在する
  - Patch 単位で走る × 1
  - 頂点(ControlPoint) 単位で走る × 頂点数

HullShader が出力すべき必須パラメータは SV_TessFactor と SV_InsideTessFactor
です。これは Path 単位で走る “patchconstantfunc” が設定するようです。

Rasterizer の前に SV_Position が確定していなければならないのと同じように、
Tessellator の前に TessFactor も必須です。

(4) Tessellator が頂点を生成する。
(5) Tessellator の出力頂点毎に DomainShader が走る

DomainShader は、Tessellator の出力である SV_DomainLocation と、
HullShader の出力パラメータをそのまま受け取ります。
DomainShader の役割は、HullShader の出力と Tessellator の出力を元に、正しい
頂点を生成することのようです。

HullShader と違って、Tessellator の実行が完了してから走るわけではありません。
平行して生成される毎にストリームとして呼び出されるようです。
その点 VertexShader に近いといえます。

(6) 1プリミティブ分頂点が生成されたら GeometryShader を呼び出す。

まだ詳しく使い込んでない&調べてませんが、HullShader とテセレータは若干
並列動作しているのかもしれません。
ただし DomainShader が走る前には、依存している HullShader は完了している
必要があります。

─┬ per control point HS ────────→ DomainShader
 └ PatchConstantFunc HS → Tessellator → DomainShader

全コントロールポイントの情報が集まってから各シェーダーに渡されるなど、
シェーダー間で受け渡すデータ量が大幅に増えていることがわかります。
ComputeShader で追加された Append / Consume Buffer の仕組みは、このように
シェーダー間で複雑なデータを受け渡す必然性から設けられたものかもしれません。

単体で見たときはいまいち機能や用途が思い浮かばなかったのですが、HS, DS, GS の
流れを見ていると Append / Consume Buffer の存在意義が見えてきた気がします。

パイプラインが複雑で扱うデータも増えました。
それでも GPU / シェーダーで実行するメリットは、メインメモリを介さず、
CPU を介さずに頂点の増減をストリーム内で閉じたまま扱えることです。

関連エントリ
Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
Direct3D11 Technical Preview D3D11の互換性、WARP Driver