Массивы (Arrays)

Однотипные переменные могут быть объединены в массивы путем объявления имени и последующих квадратных скобок ( [] ), которые могут содержать (но не обязательно) размер массива.  Размер указанный при объявлении должен быть целочисленным постоянным выражением (см. раздел 4.3.3 “Constant Expressions”) больше нуля. Исключение составляет последний объявленный член блока памяти шейдера (см. раздел 4.3.9 “Interface Blocks”), размер массива должен быть объявлен (явно задан) до того, как он будет проиндексирован с чем либо, кроме целочисленного постоянного выражения.Размер любого массива должен быть объявлен перед передачей его в качестве аргумента в функцию. Нарушение любого из этих правил приведет к ошибке времени компиляции. Так же можно объявить массив без размера (Unsized, массив без размера,»не-размерный» — для упрощения в дальнейшем) и в дальнейшем переопределяется массив с аналогичным именем и типом определенного размера, либо индексировать его только используя целочисленные константных выражений (неявный размер). Однако блоки не могут быть переопределены, если не указано другого; не-размерному массиву в блоке определенном пользователем не может быть указан размер путем переопределения блока. Объявление массива с размером, а затем (в том же шейдере) индексирование того же массива с целочисленным константным выражением, большим или равным объявленному размеру, приводит к ошибке времени компиляции.  Переопределение не-размерного массива размером равным или меньшим, чем любой индекс, использованный ранее индекс в шейдере для индексации массива приводит к ошибке времени компиляции. Так же к ошибки времени компиляции приводит отрицательное значение индекса. Массивы, объявленные как формальные параметры в определении функции, должны иметь указанный размер. Индексирование массива не-константным выражением, которое больше или равно размеру массива или меньше 0, приводит к неопределенному поведению. Массивы имеют только одно измерение (одна запись внутри «[]«) , однако могут быть объявлены массивы массивов. Все типы (базовые типы, структуры, массивы) могут формировать массивы.

Все массивы однородны; все элементы имеют одинаковый тип и размер за одним исключением. Исключением является блок хранения шейдеров, последним элементом которого выступает не-размерный массив (размер времени выполнения, run-time sized); массив может быть сформирован из такого блока хранения шейдеров,  даже если блоки имеют разные длины для последних членов.

Вот некоторые примеры:

float frequencies[3];
uniform vec4 lightPosition[4];
light lights[];
const int numLights = 2;
light lights[numLights];
            // блок хранения шейдеров, в разделе 4.3.7 “buffer variables”
buffer b {
 float u[]; // ошибка, если u не получает статический размер во время линковки
 vec4 v[];  // okay, размер v будет изменяться динамически, если не будет указан статически
} name[3];  // когда блок разбит на массивы, все u будут одного размера
            // но не обязательно все v, если их размер динамический

Тип массива может быть сформирован путем указанием типа не-массива и последующего спецификатора массива. В данном случае обязательно наличие размера.

float[5] // an array of size [5] of float
float[2][3] // an array of size [2][3] of float, not size [3] of float[2]

Этот тип может быть использован где угодно и использовать любой другой тип, в том числе в качестве возвращаемого значения функции

float[5] foo() { }

как конструктор массива

float[5](3.4, 4.2, 5.0, 5.2, 1.1)

как параметр без имени

void foo(float[5])

и как альтернативный способ объявления переменной или параметра функции.

float[5] a;

Массивы могут быть инициализированы конструкторами массивов:

float a[5] = float[5](3.4, 4.2, 5.0, 5.2, 1.1);
float a[5] = float[](3.4, 4.2, 5.0, 5.2, 1.1); // аналогичный результат

Массивы массивов можно объявить как

vec4 a[3][2]; // массив с размером 3 содержащий массива размера 2 содержащий тип vec4

который объявляет одномерный массив размером в 3 одномерных массива размером в 2  vec4. Следующие примеры делают то же самое:

vec4[2] a[3]; // size-3 array of size-2 array of vec4
vec4[3][2] a; // size-3 array of size-2 array of vec4
              // (перевод аналогичен примеру выше)

Когда в открытом участке памяти (как в постоянном (uniform) блоке) макет будет таков, что наиболее внутренняя (наиболее правая в определении) размерность повторяется чаще, чем внешняя размерность. То есть для вышесказанного порядок в памяти будет таков:

Low address : a[0][0] : a[0][1] : a[1][0] : a[1][1] : a[2][0] : a[2][1] : High address 

Тип a необходимый для обоих конструкторов и безымянных параметров — «vec4 [3] [2]»:

vec4 b[2] = vec4[2](vec4(0.0), vec4(0.1));
vec4[3][2] a = vec4[3][2](b, b, b); // конструктор
void foo(vec4[3][2]); // прототип с безымянным параметром

В качестве альтернативы синтаксис списка инициализаторов можно использовать для инициализации массива массивов:

vec4 a[3][2] = { vec4[2](vec4(0.0), vec4(1.0)),
                 vec4[2](vec4(0.0), vec4(1.0)),
                 vec4[2](vec4(0.0), vec4(1.0)) };

Не-размерные массивы могут быть явно заданы инициализатором во время объявления:

float a[5];
...
float b[] = a; // b имеет явный размер 5
float b[5] = a; // означает то же самое
float b[] = float[](1,2,3,4,5); // так же явно задает размер 5

Тем не менее присваивание к не-размерному массиву приведет к ошибке времени компиляции. Обратите внимание: это редкий случай, когда инициализаторы и присваивания имеют разную семантику. Для массивов массивов любое не-размерное измерение явно задается инициализатором:

vec4 a[][] = { vec4[2](vec4(0.0), vec4(1.0)), // okay, size to a[3][2]
               vec4[2](vec4(0.0), vec4(1.0)),
               vec4[2](vec4(0.0), vec4(1.0)) };

Массивы знают количество элементов, которые они содержат. Его можно получить, используя метод length:

float a[5];
a.length(); // возвращается 5 

Он возвращает тип int. Если массив был определен явно, возвращаемое методом значение  будет константным. Если массив не имел явного размера и это не последний объявленный элемент блока хранения шейдеров, значение, возвращаемое методом, не является константным и будет определено во время линковки. Если массив не имел явного размера и это последний объявленный элемент блока хранения шейдеров, значение, возвращаемое методом, не является константным и будет определено во время выполнения на основе размера объекта-буфера, обеспечивающего хранение блока. Для таких массивов возвращаемое значение будет неопределенным, если массив содержится в массиве блоков хранения шейдеров, который индексируется не-константным значением большим или равным количеству блоков в массиве, или меньше нуля.

Метод length не может быть вызван в массиве, который не был еще определен явно, это приведет к ошибке времени компиляции.

Метод length одинаково хорошо работает для массивов массивов:

vec4 a[3][2];
a.length() // this is 3
a[x].length() // this is 2

Когда метод length возвращает константу времени компиляции, выражение в скобках (x выше)  будет оценивать и подстраиваться под правила, необходимые для индексов массива, но массив не будет разыменован (не будут нарушены ссылки). Таким  образом, поведение хорошо отлажено даже для случаев, даже если значение  времени выполнения выходит за пределы.

Когда метод length возвращает значение времени выполнения, массив будет разыменован значением x. Если x не является константой времени компиляции и выходит за пределы диапазона, получится неопределенное значение.

// for an array b containing a member array a:
b[++x].a.length(); // b is never dereferenced, but “++x” is evaluated
// for an array s of a shader storage object containing a member array a:
s[x].a.length(); // s is dereferenced; x needs to be a valid index

 Для массивов неявного размера или размера времени выполнения, только внешний измерение может не иметь размера. Тип, который содержит неизвестной размер массива не может быть сформирован в массив до тех пор, пока он не получит явный размер, за исключением блока хранения шейдеров, где единственным не-размерным элементом является последний член блока.

В блоке хранения шейдеров, последний член может быть объявлен без явного размера. В таком случае, эффективный размер массива вычисляется  во время выполнения из размера хранилища данных относящегося к интерфейсному блоку. Такие массивы размера времени выполнения могут быть проиндексированы при помощи целочисленных выражений общего характера. Однако, попытка передачи их в качестве аргумента в функцию или индексирование отрицательным константным выражением приведет к ошибке времени компиляции.

Main Admin

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *