Правила упаковки для постоянных переменных
Правила упаковки определяют, насколько тесно данные могут быть упорядочены при хранении. 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;
Более жесткая упаковка является компромиссом по сравнению с необходимостью дополнительных инструкций шейдера для вычисления адресов.