常量变量的打包规则

打包规则决定了在存储数据时的数据排列紧密程度。 HLSL 针对 VS 输出数据、GS 输入和输出数据以及 PS 输入和输出数据强制实施打包规则。 (由于 IA 阶段无法解包数据,因此不会打包 VS 输入数据。)

HLSL 打包规则类似于使用 Visual Studio 执行 #pragma pack 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;

更紧密的打包方式是一种折衷方法,这样就无需为寻址计算添加额外的着色器指令。