封裝常數變數的規則
封裝規則會決定儲存數據時可以排列得有多緊。 HLSL 會實作 VS 輸出資料的封裝規則、GS 輸入和輸出數據,以及 PS 輸入和輸出數據。 (因為 IA 階段無法解除封裝數據,所以不會封裝 VS 輸入的數據。
HLSL 封裝規則類似於使用 Visual Studio 執行 #pragma 套件 4 ,以將數據封裝成 4 位元組界限。 此外,HLSL 會封裝數據,使其不會跨越16位元組界限。 變數會封裝成指定的四個元件向量,直到變數跨越4向量界限為止:下一個變數將會反彈至下一個四元件向量。
每個結構都會強制下一個變數在下一個四元件向量上啟動。 這有時會產生結構的陣列填補。 任何結構的產生大小一律會依 sizeof(四個元件向量)平均地分割。
數位預設不會封裝在 HLSL 中。 為了避免強制著色器對位移計算產生 ALU 額外負荷,陣列中的每個元素都會儲存在四個元件向量中。 請注意,您可以使用轉換來達成數位的封裝(併產生尋址計算)。
以下是結構及其對應封裝大小的範例(給定: 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;
更緊密的封裝是取捨與地址計算的其他著色器指示的需求。