HLSL でのルート署名の指定
C++ コードでの指定の代替方法として、HLSL Shader Model 5.1 でルート署名を指定できます。
- HLSL ルート署名の例
- RootFlags
- ルート定数
- 可視性
- ルートレベル CBV
- ルートレベル SRV
- ルートレベル UAV
- 記述子テーブル
- 静的サンプラー
- HLSL ルート署名のコンパイル
- FXC コンパイラでのルート署名の操作
- メモ
- 関連トピック
HLSL ルート署名の例
ルート署名は、HLSL では文字列として指定できます。 この文字列の内容は、ルート署名を構成する要素を記述する句の、コンマ区切りの集合です。 ルート署名は、1 つのパイプライン状態オブジェクト (PSO) に対するすべてのシェーダーで同一であることが必要です。 たとえば次のようになります。
ルート署名バージョン 1.0
#define MyRS1 "RootFlags( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | " \
"DENY_VERTEX_SHADER_ROOT_ACCESS), " \
"CBV(b0, space = 1), " \
"SRV(t0), " \
"UAV(u0, visibility = SHADER_VISIBILITY_GEOMETRY), " \
"DescriptorTable( CBV(b0), " \
"UAV(u1, numDescriptors = 2), " \
"SRV(t1, numDescriptors = unbounded)), " \
"DescriptorTable(Sampler(s0, numDescriptors = 2)), " \
"RootConstants(num32BitConstants=1, b9), " \
"DescriptorTable( UAV(u3), " \
"UAV(u4), " \
"UAV(u5, offset=1)), " \
"StaticSampler(s2)," \
"StaticSampler(s3, " \
"addressU = TEXTURE_ADDRESS_CLAMP, " \
"filter = FILTER_MIN_MAG_MIP_LINEAR )"
この定義では以下のようなルート署名になります。次の点に注目してください。
- 既定のパラメーターの使用。
- b0 と (b0, space=1) が競合しない
- u0 はジオメトリ シェーダーからのみ認識できる
- u4 と u5 はエイリアスであり、ヒープ内の同じ記述子を指している
ルート署名バージョン 1.1
ルート署名バージョン 1.1 では、ルート署名の記述子とデータに対するドライバー最適化が可能です。
#define MyRS1 "RootFlags( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | " \
"DENY_VERTEX_SHADER_ROOT_ACCESS), " \
"CBV(b0, space = 1, flags = DATA_STATIC), " \
"SRV(t0), " \
"UAV(u0), " \
"DescriptorTable( CBV(b1), " \
"SRV(t1, numDescriptors = 8, " \
" flags = DESCRIPTORS_VOLATILE), " \
"UAV(u1, numDescriptors = unbounded, " \
" flags = DESCRIPTORS_VOLATILE)), " \
"DescriptorTable(Sampler(s0, space=1, numDescriptors = 4)), " \
"RootConstants(num32BitConstants=3, b10), " \
"StaticSampler(s1)," \
"StaticSampler(s2, " \
"addressU = TEXTURE_ADDRESS_CLAMP, " \
"filter = FILTER_MIN_MAG_MIP_LINEAR )"
HLSL ルート署名言語は、C++ ルート署名 API に密接に対応しており、同等の表現力があります。 ルート署名は、一連の句をコンマで区切って指定します。 句の順序は重要です。解析の順序でルート署名内のスロット位置が決まるためです。 それぞれの句は 1 つまたは複数の名前付きパラメーターを受け取ります。 ただし、パラメーターの順序は重要ではありません。
RootFlags
省略可能な RootFlags 句は、0 (フラグがないことを示す既定値)、または OR '|' 演算子を介して接続された 1 つまたは複数の定義済みのルート フラグ値を受け取ります。 許可されるルート フラグの値は、 D3D12_ROOT_SIGNATURE_FLAGSによって定義されます。
次に例を示します。
RootFlags(0) // default value – no flags
RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)
RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | DENY_VERTEX_SHADER_ROOT_ACCESS)
ルート定数
RootConstants 句では、ルート署名の中のルート定数を指定します。 2 つの必須パラメーターは、cbuffer の num32BitConstants と bReg (C++ API の BaseShaderRegister に対応するレジスタ) です。 スペース (C++ API の RegisterSpace) と可視性 (C++ の ShaderVisibility) のパラメーターは省略可能であり、既定値は次のとおりです。
RootConstants(num32BitConstants=N, bReg [, space=0,
visibility=SHADER_VISIBILITY_ALL ])
次に例を示します。
RootConstants(num32BitConstants=3, b3)
視程
可視性は省略可能なパラメーターであり、 D3D12_SHADER_VISIBILITYの値のいずれかを持つことができます。
SHADER_VISIBILITY_ALLは、ルート引数をすべてのシェーダーにブロードキャストします。 ハードウェアによっては、これにコストはかかりませんが、その他のハードウェアではデータをすべてのシェーダー ステージにフォークするためのコストがかかります。 SHADER_VISIBILITY_VERTEXなどのオプションの 1 つを設定すると、ルート引数は 1 つのシェーダー ステージに制限されます。
ルート引数を単一のシェーダー ステージに設定すると、同じバインド名をさまざまなステージで使用できます。 たとえば、t0,SHADER_VISIBILITY_VERTEX
の SRV バインドと t0,SHADER_VISIBILITY_PIXEL
の SRV バインドが有効になります。 しかし、可視性の設定がこれらのバインドのいずれかに対して t0,SHADER_VISIBILITY_ALL
の場合は、このルート署名は無効になります。
ルートレベル CBV
CBV
(定数バッファー ビュー) 句では、ルートレベル定数バッファーの b レジスタ Reg エントリを指定します。 これはスカラー エントリであることに注意してください。つまり、ルート レベルに対する範囲を指定することはできません。
CBV(bReg [, space=0, visibility=SHADER_VISIBILITY_ALL ]) // Version 1.0
CBV(bReg [, space=0, visibility=SHADER_VISIBILITY_ALL, // Version 1.1
flags=DATA_STATIC_WHILE_SET_AT_EXECUTE ])
ルートレベル SRV
SRV
(シェーダー リソース ビュー) 句では、ルートレベル SRV の t レジスタ Reg エントリを指定します。 これはスカラー エントリであることに注意してください。つまり、ルート レベルに対する範囲を指定することはできません。
SRV(tReg [, space=0, visibility=SHADER_VISIBILITY_ALL ]) // Version 1.0
SRV(tReg [, space=0, visibility=SHADER_VISIBILITY_ALL, // Version 1.1
flags=DATA_STATIC_WHILE_SET_AT_EXECUTE ])
ルートレベル UAV
UAV
(順序指定されていないアクセス ビュー) 句では、ルートレベル UAV の u レジスタ Reg エントリを指定します。 これはスカラー エントリであることに注意してください。つまり、ルート レベルに対する範囲を指定することはできません。
UAV(uReg [, space=0, visibility=SHADER_VISIBILITY_ALL ]) // Version 1.0
UAV(uReg [, space=0, visibility=SHADER_VISIBILITY_ALL, // Version 1.1
flags=DATA_VOLATILE ])
次に例を示します。
UAV(u3)
記述子テーブル
DescriptorTable
句はそれ自体が、記述子テーブル句をコンマで区切って連結したリストであり、必要に応じて可視性パラメーターも指定できます。
DescriptorTable 句には、CBV、SRV、UAV、サンプラーが含まれています。 これらのパラメーターは、ルートレベル句のものとは異なることに注意してください。
DescriptorTable( DTClause1, [ DTClause2, … DTClauseN,
visibility=SHADER_VISIBILITY_ALL ] )
記述子テーブル CBV
の構文は次のとおりです。
CBV(bReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND ]) // Version 1.0
CBV(bReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND // Version 1.1
, flags=DATA_STATIC_WHILE_SET_AT_EXECUTE ])
次に例を示します。
DescriptorTable(CBV(b0),SRV(t3, numDescriptors=unbounded))
必須パラメーター bReg では、cbuffer 範囲の開始 Reg を指定します。
numDescriptors パラメーターでは、連続する cbuffer 範囲内の記述子の数を指定します。既定値は 1 となります。
numDescriptors が数値のときは、このエントリによって cbuffer の範囲 [Reg, Reg + numDescriptors - 1]
が宣言されます。
numDescriptors が "unbounded" と等しい場合は、範囲は [Reg, UINT_MAX]
となります。つまり、アプリは範囲外の領域を参照していないことを保証する必要があります。
offset フィールドは、C++ API の OffsetInDescriptorsFromTableStart パラメーター、つまり、テーブルの先頭からの (記述子内の) オフセットを表します。 オフセットが DESCRIPTOR_RANGE_OFFSET_APPEND (既定値) に設定されている場合は、範囲が直前の範囲の直後であることを意味します。 しかし、特定のオフセットを入力すると、複数の範囲を重ね合わせることができ、レジスタのエイリアシングが可能になります。
記述子テーブル SRV
の構文は次のとおりです。
SRV(tReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND ]) // Version 1.0
SRV(tReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND, // Version 1.1
flags=DATA_STATIC_WHILE_SET_AT_EXECUTE ])
これは記述子テーブルの CBV
エントリと似ていますが、指定された範囲がシェーダー リソース ビューに対するものである点が異なります。
記述子テーブル UAV
の構文は次のとおりです。
UAV(uReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND ]) // Version 1.0
UAV(uReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND, // Version 1.1
flags=DATA_VOLATILE ])
これは記述子テーブル CBV
のエントリと似ていますが、指定された範囲が順序指定されていないアクセス ビューに対するものである点が異なります。
記述子テーブル Sampler
の構文は次のとおりです。
Sampler(sReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND ]) // Version 1.0
Sampler(sReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND, // Version 1.1
flags=0 ])
これは記述子テーブル CBV
のエントリと似ていますが、指定された範囲がシェーダー サンプラーに対するものである点が異なります。 サンプラーと他の種類の記述子とを同じ記述子テーブル内で混在させることはできないことに注意してください (これらは別の記述子ヒープに存在するため)。
静的サンプラー
静的サンプラーは 、D3D12_STATIC_SAMPLER_DESC 構造体を表します。 StaticSampler の必須パラメーターはスカラー型の、サンプラーの s レジスタ Reg です。その他のパラメーターは省略可能であり、既定値は以下のとおりです。 ほとんどのフィールドで、一連の定義済みの列挙型が受け入れられます。
StaticSampler( sReg,
[ filter = FILTER_ANISOTROPIC,
addressU = TEXTURE_ADDRESS_WRAP,
addressV = TEXTURE_ADDRESS_WRAP,
addressW = TEXTURE_ADDRESS_WRAP,
mipLODBias = 0.f,
maxAnisotropy = 16,
comparisonFunc = COMPARISON_LESS_EQUAL,
borderColor = STATIC_BORDER_COLOR_OPAQUE_WHITE,
minLOD = 0.f,
maxLOD = 3.402823466e+38f,
space = 0,
visibility = SHADER_VISIBILITY_ALL ])
次に例を示します。
StaticSampler(s4, filter=FILTER_MIN_MAG_MIP_LINEAR)
パラメーターのオプションは、C++ API 呼び出しの場合とよく似ていますが、borderColor は例外であり、HLSL では列挙型 1 個だけとなります。
フィルター フィールドには、 D3D12_FILTERのいずれかを指定できます。
アドレス フィールドには、それぞれ D3D12_TEXTURE_ADDRESS_MODEのいずれかを指定できます。
比較関数には、 D3D12_COMPARISON_FUNCのいずれかを指定できます。
罫線の色フィールドには 、D3D12_STATIC_BORDER_COLORのいずれかを指定できます。
可視性は 、D3D12_SHADER_VISIBILITYのいずれかになります。
HLSL ルート署名のコンパイル
HLSL ルート署名をコンパイルするメカニズムは 2 つあります。 1 つは、ルート署名文字列を特定のシェーダーに、RootSignature 属性を介してアタッチするというものです (下記の例では MyRS1 エントリ ポイントを使用しています)。
[RootSignature(MyRS1)]
float4 main(float4 coord : COORD) : SV_Target
{
…
}
コンパイラによってシェーダーのルート署名 BLOB が作成されて検証され、シェーダー バイト コードと共にシェーダー BLOB に埋め込まれます。 コンパイラでは、シェーダー モデル 5.0 以上のルート署名構文がサポートされます。 ルート署名がシェーダー モデル 5.0 のシェーダーに埋め込まれており、そのシェーダーが D3D12 ではなく、D3D11 ランタイムに送信された場合は、ルート署名部分は D3D11 によって無視されます。
もう 1 つのメカニズムは、スタンドアロンのルート署名 BLOB を作成するというものです。これは、多数のシェーダーのセットでの再利用を目的とするものであり、スペースを節約できます。 エフェクト コンパイラ ツール (FXC) は、rootsig_1_0シェーダー モデルとrootsig_1_1 シェーダー モデルの両方をサポートしています。 定義文字列の名前は、通常の /E 引数を介して指定されます。 次に例を示します。
fxc.exe /T rootsig_1_1 MyRS1.hlsl /E MyRS1 /Fo MyRS1.fxo
ルート署名の定義文字列は、コマンド ラインで渡すこともできます (例: /D MyRS1=”…”)。
FXC コンパイラでのルート署名の操作
FXC コンパイラは、HLSL ソース ファイルからシェーダー バイト コードを作成します。 このコンパイラには多数の省略可能パラメーターがあります。「Effect-Compiler Tool」を参照してください。
HLSL で作成されたルート署名の管理に関する FXC の使用例を次の表に示します。
折れ線 | コマンド ライン | 説明 |
---|---|---|
1 | fxc /T ps_5_1 shaderWithRootSig.hlsl /Fo rs1.fxo |
ピクセル シェーダー 5.1 ターゲットのシェーダーをコンパイルします。シェーダー ソースは shaderWithRootSig.hlsl ファイル内にあり、この中にルート署名があります。 シェーダーとルート署名は、rs1.fxo バイナリ ファイル内でそれぞれ別の BLOB としてコンパイルされます。 |
2 | fxc /dumpbin rs1.fxo /extractrootsignature /Fo rs1.rs.fxo |
行 1 で作成されたファイルからルート署名を抽出します。したがって、rs1.rs.fxo ファイルの内容はルート署名だけとなります。 |
3 | fxc /dumpbin rs1.fxo /Qstrip_rootsignature /Fo rs1.stripped.fxo |
行 1 で作成されたファイルからルート署名を削除します。したがって、rs1.stripped.fxo ファイルの内容はルート署名のないシェーダーとなります。 |
4 | fxc /dumpbin rs1.stripped.fxo /setrootsignature rs1.rs.fxo /Fo rs1.new.fxo |
それぞれ別のファイルにあるシェーダーとルート署名を結合して、両方の BLOB が含まれているバイナリ ファイルを作成します。 この例の rs1.new.fx0 は行 1 の rs1.fx0 と同一になります。 |
5 | fxc /T rootsig_1_0 rootSigAndMaybeShaderInHereToo.hlsl /E RS1 /Fo rs2.fxo |
スタンドアロンのルート署名バイナリ ファイルを作成します。このソースの内容は、ルート署名 1 つだけではない可能性があります。 rootsig_1_0ターゲットに注意してください。また、RS1 は HLSL ファイル内のルート署名 (#define) マクロ文字列の名前であることに注意してください。 |
FXC を介して使用可能な機能は、プログラムでも D3DCompile 関数を通して利用できます。 この呼び出しは、ルート署名またはスタンドアロン ルート署名 (rootsig_1_0 ターゲットの設定) を使用してシェーダーをコンパイルします。 D3DGetBlobPart と D3DSetBlobPart ではルート署名を抽出して既存の BLOB にアタッチできます。 D3D_BLOB_ROOT_SIGNATUREは、ルート署名 BLOB パーツの種類を指定するために使用されます。 D3DStripShader は、(D3DCOMPILER_STRIP_ROOT_SIGNATURE フラグを使用して) ルート署名を BLOB から削除します。
メモ
注意
シェーダーのオフラインでのコンパイルを強くお勧めしますが、シェーダーを実行時にコンパイルする必要がある場合は、D3DCompile2 の注釈を参照してください。
Note
既存の HLSL 資産とともに使用するルート署名を処理できるようにするために、その資産に変更を加える必要はありません。
関連トピック