定数変数のパッキング規則
パッキング規則は、格納時にデータをどの程度緊密に配置できるかを指定します。 HLSL は、VS の出力データ、GS の入出力データ、PS の入出力データに関するパッキング規則を実装します。 (IA ステージではデータをアンパックできないため、データは VS 入力についてはパックされません。)
HLSL パッキング規則は、データを 4 バイト境界にパックする Visual Studio での #pragma pack 4 の実行と似ています。 さらに、HLSL は 16 バイト境界をまたがないようにデータをパックします。 変数は、4 ベクトルの境界をまたがるようになるまで、特定の 4 成分ベクトルにパックされ、次の変数は次の 4 成分ベクトルに移ります。
各構造体により、次の変数は次の 4 成分ベクトルで強制的に開始されます。 これにより、構造体の配列にパディングが生成されることがあります。 結果として、構造体のサイズは、常に sizeof(<4 成分ベクトル>) で割り切れます。
HLSL の配列は、既定ではパックされません。 シェーダーでオフセット計算のための ALU オーバーヘッドが強制的に発生するのを避けるため、配列内のすべての要素は 4 成分ベクトルに格納されます。 キャストを使うと、配列をパックできること (そして、アドレス計算が発生すること) に注意してください。
次に示すのは、構造体とそれに対応するパックされたサイズの例です (前提: float1 は 4 バイトを占めます)。
// 2 x 16byte elements
cbuffer IE
{
float4 Val1;
float2 Val2; // starts a new vector
float2 Val3;
};
// 3 x 16byte elements
cbuffer IE
{
float2 Val1;
float4 Val2; // starts a new vector
float2 Val3; // starts a new vector
};
// 1 x 16byte elements
cbuffer IE
{
float1 Val1;
float1 Val2;
float2 Val3;
};
// 1 x 16byte elements
cbuffer IE
{
float1 Val1;
float2 Val2;
float1 Val3;
};
// 2 x 16byte elements
cbuffer IE
{
float1 Val1;
float1 Val1;
float1 Val1;
float2 Val2; // starts a new vector
};
// 1 x 16byte elements
cbuffer IE
{
float3 Val1;
float1 Val2;
};
// 1 x 16byte elements
cbuffer IE
{
float1 Val1;
float3 Val2;
};
// 2 x 16byte elements
cbuffer IE
{
float1 Val1;
float1 Val1;
float3 Val2; // starts a new vector
};
// 3 x 16byte elements
cbuffer IE
{
float1 Val1;
struct {
float4 SVal1; // starts a new vector
float1 SVal2; // starts a new vector
} Val2;
};
// 3 x 16byte elements
cbuffer IE
{
float1 Val1;
struct {
float1 SVal1; // starts a new vector
float4 SVal2; // starts a new vector
} Val2;
};
// 3 x 16byte elements
cbuffer IE
{
struct {
float4 SVal1;
float1 SVal2; // starts a new vector
} Val1;
float1 Val2;
};
より積極的なパッキング
配列をより積極的にパックすることができます。次に例を示します。 定数バッファーからこのような配列にアクセスするとします。
// Original array: not efficiently packed.
float2 myArray[32];
定数バッファーでは、上記の宣言によって 32 個の 4 成分ベクトルが使われます。 これは、各配列要素がこれらのベクトルの先頭に配置されるためです。 ここで、定数バッファーにこれらの値を緊密に (スペースなしで) パックし、シェーダー内で引き続き float2[32]
配列としてその配列にアクセスしたい場合は、代わりに次のように記述できます。
float4 packedArrayInCBuffer[16];
// shader uses myArray here:
static const float2 myArray[32] = (float2[32])packedArrayInCBuffer;
より緊密にパックすると、それと引き換えに、アドレス計算のための追加のシェーダー命令が必要になります。