Правила упаковки для постоянных переменных

Правила упаковки определяют, насколько тесно данные могут быть упорядочены при хранении. HLSL реализует правила упаковки для выходных данных VS, входных и выходных данных GS, а также входных и выходных данных PS. (Данные не упакованы для входных данных VS, так как этап IA не может распаковывать данные.)

Правила упаковки HLSL похожи на выполнение пакета #pragma 4 с Visual Studio, который упаковывает данные в границы 4 байтов. Кроме того, HLSL упаковывает данные, чтобы он не пересекал границу 16-байтов. Переменные упаковываются в заданный вектор с четырьмя компонентами, пока переменная не будет привязана к 4-векторной границе; Следующие переменные будут отскакивать до следующего четырехкомпонентного вектора.

Каждая структура заставляет следующую переменную начинать с следующего четырехкомпонентного вектора. Иногда это создает заполнение для массивов структур. Результирующий размер любой структуры всегда будет равномерно разделяться по размеру (четырехкомпонентный вектор).

Массивы по умолчанию не упакованы в HLSL. Чтобы избежать принудительного использования шейдера для вычислений смещения, каждый элемент массива хранится в векторе с четырьмя компонентами. Обратите внимание, что вы можете достичь упаковки для массивов (и нести вычисления адресации) с помощью приведения.

Ниже приведены примеры структур и их соответствующих размеров упаковки (учитывая: 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;

Более жесткая упаковка является компромиссом по сравнению с необходимостью дополнительных инструкций шейдера для вычисления адресов.