シェーダー定数 (DirectX HLSL)
シェーダー モデル 4 では、シェーダー定数はメモリー内の 1 つまたは複数のバッファー リソースに格納されます。シェーダー定数は、定数バッファー (cbuffer) とテクスチャ バッファー (tbuffer) の 2 種類のバッファーに整理できます。定数バッファーは定数変数で使用するために最適化されていて、CPU からのアクセスでの遅延が少なく、頻繁に更新されるという特徴があります。このため、これらのリソースのサイズ、レイアウトおよびアクセスには制限が追加されます。テクスチャー バッファーは、テクスチャーのようにアクセスされ、任意にインデックス化されたデータに対するパフォーマンスが優れています。使用するリソースの種類に関係なく、アプリケーションが作成できる定数バッファーまたはテクスチャー バッファーの数に制限はありません。
定数バッファーまたはテクスチャー バッファーの宣言は、C での構造体の宣言に非常に似ています。register および packoffset の 2 つのキーワードが、レジスタ データまたはパッキング データを手動で割り当てるために追加されています。
BufferType [Name] [: register(b#)] { VariableDeclaration [: packoffset(c#.xyzw)]; ... }; |
---|
パラメーター
BufferType
[in] バッファーのタイプ。バッファーのタイプ 説明 cbuffer 定数バッファー tbuffer テクスチャー バッファー Name
[in] 一意のバッファー名を含む ASCII 文字列 (省略可能)。register (b#)
[in] 定数データを手動でパッキングするためのキーワード (省略可能)。定数バッファーでのみレジスタの中で定数をパックできます。開始レジスタは、レジスタ番号 (#) で指定します。VariableDeclaration
[in] 変数宣言。構造体メンバー宣言に似ています。HLSL 型またはエフェクト オブジェクトのいずれでも指定できます (テクスチャーまたはサンプラ オブジェクトを除く)。packoffset(c#.xyzw)
[in] 定数データを手動でパッキングするためのキーワード (省略可能)。任意の定数バッファーの中で定数をパックできます。レジスタ番号は (#) で指定します。xyzw スィズルを使用したサブコンポーネントのパッキングは、単一レジスタに格納可能な (レジスタ境界を越えない) サイズの定数で利用可能です。たとえば、float4 は、y 成分で始まる単一レジスタにパックすることはできません。それは、4 成分レジスタに格納できないためです。
解説
定数バッファーを使用すると、複数のシェーダー定数をまとめて同時にコミットできます。それぞれのシェーダー定数を個別の呼び出しで別々にコミットすることがないので、シェーダー定数の更新に必要な帯域を少なくすることができます。
定数バッファーは、バッファーのようにアクセスできる特殊なバッファー リソースです。各定数バッファーには、最大 4096 のベクトルを設定でき、各ベクトルには最大 4 つの 32 ビット値を指定できます。パイプライン ステージごとに最大で 14 個の定数バッファーをバインドできます (2 つのスロットが内部用に別途予約されています)。
テクスチャー バッファーは、テクスチャーのようにアクセスできる特殊なバッファー リソースです。テクスチャー アクセスは (バッファー アクセスと比較して)、任意にインデックス化されたデータに対するパフォーマンスが優れています。パイプライン ステージごとに最大で 128 個のテクスチャー バッファーをバインドできます。
バッファー リソースは、シェーダー定数の設定時のオーバーヘッドを最小化するために設計されています。定数バッファーとテクスチャー バッファーの更新はエフェクト フレームワーク (「ID3D10Effect インターフェイス」を参照) で管理されていますが、Direct3D API を使用してバッファーを更新することもできます (「リソース データのコピーとアクセス (Direct3D 10)」を参照)。アプリケーションは、別のバッファー (たとえば、レンダー ターゲットまたはストリーム出力ターゲット) から定数バッファーにデータをコピーすることもできます。
D3D10 アプリケーションで定数バッファーを使用する方法の詳細については、「リソース タイプ (Direct3D 10)」および「バッファー リソースの作成 (Direct3D 10)」を参照してください。
D3D11 アプリケーションで定数バッファーを使用する方法の詳細については、「Direct3D 11 のバッファーの概要」および「方法: 定数バッファーの作成」を参照してください。
定数バッファーをパイプラインにバインドするうえで、ビューは不要です。しかし、テクスチャー バッファーはビューを必要とし、テクスチャー スロットにバインドする必要があります (またはエフェクトの使用時に SetTextureBuffer を使用してバインドする必要があります)。
定数データをパックするには、register (DirectX HLSL) または packoffset (DirectX HLSL) のいずれかのキーワードを使用します。
Direct3D 9 と Direct3D 10/11 の違い Direct3D 9 でサポートされていた定数の自動割り当てでは、パッキングは実行されず、代わりに各変数を float4 レジスタのセットに割り当てていましたが、Direct3D 10 および Direct3D 11 では HLSL 定数変数はパッキング規則に従っています。 |
定数バッファーの編成
定数バッファーを使用すると、複数のシェーダー定数をまとめて同時にコミットできます。それぞれのシェーダー定数を個別の呼び出しで別々にコミットすることがないので、シェーダー定数の更新に必要な帯域を少なくすることができます。
定数バッファーを効率的に使用する最も良い方法は、それらの更新頻度に基づいて、シェーダー変数を定数バッファーに編成することです。これにより、アプリケーションにおいて、シェーダー定数の更新に必要な帯域幅を最小限に抑えることができます。たとえば、シェーダーで 2 つの定数バッファーを宣言し、それぞれのバッファーにあるデータをその更新頻度に基づいて分類し、整理します。オブジェクト単位の更新を必要とするデータ (ワールド行列など) は、オブジェクト別に更新できる定数バッファーにグループ化します。これにより、1 つのシーンを記述する、更新頻度が非常に低いと考えられるデータから分離されます。
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); }