シェーダー定数 (HLSL)
シェーダー モデル 4 では、シェーダー定数はメモリ内の 1 つまたは複数のバッファー リソースに格納されます。 それは、定数バッファー (cbuffer) とテクスチャ バッファー (tbuffer) の 2 種類のバッファーに編成できます。 定数バッファーは、待機時間の短いアクセスと CPU からの頻度の高い更新が特徴である、定数変数の使用に対して最適化されています。 このため、これらのリソースには、サイズ、レイアウト、アクセスの各制限が追加で適用されます。 テクスチャ バッファーはテクスチャのようにアクセスされ、任意にインデックス付けされたデータに対して優れたパフォーマンスを発揮します。 使用するリソースの種類に関係なく、アプリケーションで作成できる定数バッファーまたはテクスチャ バッファーの数に制限はありません。
定数バッファーまたはテクスチャ バッファーの宣言は、C の構造体の宣言によく似ていますが、手動でレジスタを割り当てたりデータをパックしたりするための register および packoffset キーワードが追加されています。
BufferType Name [: register(b#)] { VariableDeclaration [: packoffset(c#.xyzw)]; ... };
パラメーター
-
BufferType
-
[入力] バッファーの種類。
BufferType 説明 cbuffer 定数バッファー tbuffer テクスチャ バッファー -
名前
-
[入力] 一意のバッファー名を含む ASCII 文字列。
-
register(b#)
-
[入力] 定数データを手動でパックするために使用する省略可能なキーワード。 定数は、定数バッファー内のレジスタにのみパックすることができます。なお、開始レジスタはレジスタ番号 (#) によって指定します。
-
VariableDeclaration
-
[入力] 構造体メンバー宣言に類似した変数宣言。 これは、任意の HLSL 型または効果オブジェクト (テクスチャまたはサンプラー オブジェクトを除く) とすることができます。
-
packoffset(c#.xyzw)
-
[入力] 定数データを手動でパックするために使用する省略可能なキーワード。 定数は、任意の定数バッファー内でパックすることができます。なお、レジスタ番号は (#) によって指定します。 サブコンポーネント パッキング (xyzw スウィズリングを使用) は、定数のサイズが 1 つのレジスタ内に収まる (レジスタ境界を越えない) 場合に使用できます。 たとえば、float4 は y コンポーネントから始まる単一のレジスタにパックすることはできません。この場合、4 コンポーネント レジスタに収まらないからです。
解説
定数バッファーを使用すると、複数のシェーダー定数をグループにまとめて同時にコミットできます。それぞれのシェーダー定数を個別の呼び出しで別々にコミットすることがないので、シェーダー定数の更新に必要な帯域を削減することができます。
定数バッファーは、バッファーのようにアクセスされる特殊なバッファー リソースです。 各定数バッファーには、最大 4096 個のベクターを保持できます。各ベクターには、最大 4 つの 32 ビット値が格納されます。 パイプライン ステージごとに最大 14 個の定数バッファーをバインドできます (追加の 2 つのスロットは内部使用のために予約されています)。
テクスチャ バッファーは、テクスチャのようにアクセスされる特殊なバッファー リソースです。 テクスチャ アクセスは、(バッファー アクセスと比較して)、任意にインデックス付けされたデータに対するパフォーマンスが優れています。 パイプライン ステージごとに最大 128 個のテクスチャ バッファーをバインドできます。
バッファー リソースは、シェーダー定数を設定する際のオーバーヘッドを最小限に抑えるように設計されています。 効果フレームワーク (ID3D10Effect インターフェイスを参照) では、定数およびテクスチャ バッファーの更新を管理します。また、Direct3D API を使用してバッファーを更新することもできます (詳細については、「リソース データのコピーとアクセス (Direct3D 10)」 を参照してください)。 アプリケーションにより、別のバッファー (レンダー ターゲットやストリーム出力ターゲットなど) から定数バッファーにデータをコピーすることもできます。
D3D10 アプリケーションで定数バッファーを使用する方法の詳細については、「リソースの種類 (Direct3D 10)」および「バッファー リソースの作成 (Direct3D 10)」を参照してください。
D3D11 アプリケーションで定数バッファーを使用する方法の詳細については、「Direct3D 11 のバッファーの概要」および「方法: 定数バッファーを作成する」を参照してください。
定数バッファーでは、ビューをパイプラインにバインドする必要はありません。 しかし、テクスチャ バッファーにはビューが必要であり、テクスチャ スロットにバインドする必要があります (または、効果を使用する場合は SetTextureBuffer とバインドする必要があります)。
定数データをパックするには次の 2 つの方法があります: register (DirectX HLSL) キーワードを使用。packoffset (DirectX HLSL) キーワードを使用。
Direct3D 9 と Direct3D 10 および 11 との違いは、次のとおりです。
- Direct3D 9 での定数の自動割り当て (パッキングを実行せず、代わりに各変数を float4 レジスタのセットに割り当てた) とは異なり、HLSL 定数変数は Direct3D 10 と 11 のパッキング規則に従います。
定数バッファーの編成
定数バッファーを使用すると、複数のシェーダー定数をグループにまとめて同時にコミットできます。それぞれのシェーダー定数を個別の呼び出しで別々にコミットすることがないので、シェーダー定数の更新に必要な帯域を削減することができます。
定数バッファーを効率的に使う最良の方法として、更新頻度に応じてシェーダーの変数を定数バッファーにまとめる。 これにより、アプリケーションではシェーダー定数の更新に必要な帯域幅を最小限に抑えることができます。 たとえば、シェーダーで、2 つの定数バッファーを宣言し、更新の頻度に基づいてそれぞれにおけるデータを次のように整理します: オブジェクトごとに更新する必要があるデータ (ワールド マトリックスなど) は、オブジェクトごとに更新できる定数バッファーにグループ化します。 これはシーンを特徴付けるデータとは別のものであるため、更新頻度が大幅に低下する可能性があります (シーンが変更される場合)。
cbuffer myObject
{
float4x4 matWorld;
float3 vObjectPosition;
int arrayIndex;
}
cbuffer myScene
{
float3 vSunPosition;
float4x4 matView;
}
既定の定数バッファー
既定の定数バッファーとして、$Global と $Param の 2 つが用意されています。 グローバル スコープ内に配置された変数は、cbuffer に使用されるのと同じパッキング メソッドを使用して、$Global cbuffer に暗黙的に追加されます。 シェーダーが効果フレームワークの外部でコンパイルされると、関数のパラメーター リスト内の均一なパラメーターが $Param 定数バッファーに表示されます。 効果フレームワーク内でコンパイルする場合、均一なものはすべてグローバル スコープで定義されている変数に解決する必要があります。
例
Skinning10 サンプルからの以下の例は、マトリックスの配列で構成されたテクスチャ バッファーです。
tbuffer tbAnimMatrices
{
matrix g_mTexBoneWorld[MAX_BONE_MATRICES];
};
この宣言例では、特定のレジスタから開始する定数バッファーを手動で割り当てて、さらにサブコンポーネントによって特定の要素をパックしています。
cbuffer MyBuffer : register(b3)
{
float4 Element1 : packoffset(c0);
float1 Element2 : packoffset(c1);
float1 Element3 : packoffset(c1.y);
}