Функции, переменные и спецификаторы
В этой главе описываются, как объявляются функции, параметры и переменные. Это также детализирует, как спецификаторы часто используются с функциями, параметрами и переменными для указания ограничений.
Функциональные спецификаторы
Металлический язык штриховки поддерживает следующие спецификаторы, ограничивающие, как может использоваться функция:
kernel
Параллельная данным функция, выполняющаяся по 1-, 2-или 3-мерной сетке.
vertex
Функция вершины, выполняющаяся для каждой вершины в потоке вершины и это генерирует на вершину вывод.
fragment
Функция фрагмента, выполняющаяся для каждого фрагмента (и его связанные данные) в потоке фрагмента и это генерирует на фрагмент вывод.
Функциональный спецификатор используется в начале функции перед ее типом возврата. Следующий пример показывает синтаксис для вычислить функции:
kernel void |
foo(...) |
{ |
... |
} |
Для функций, объявленных с kernel
спецификатор, тип возврата должен быть void
.
Только графическая функция может быть объявлена с одним из vertex
или fragment
спецификаторы. Для графических функций тип возврата идентифицирует, является ли вывод, сгенерированный функцией, или на вершину или на фрагмент. Тип возврата для графической функции может быть void
указание, что функция не генерирует вывод.
Функции то использование a kernel
, vertex
или fragment
функциональный спецификатор не может вызвать функции, также использующие эти спецификаторы, или ошибка компиляции заканчивается.
Спецификаторы адресного пространства для переменных и параметров
Металлический язык штриховки реализует спецификаторы адресного пространства для указания области памяти, где выделяются функциональная переменная или параметр. Эти спецификаторы описывают непересекающиеся адресные пространства для переменных:
device
(для большего количества подробных данных посмотрите Пространство адреса устройства),threadgroup
(см. threadgroup Адресное пространство),constant
(см. Пространство базового адреса),thread
(см. Адресное пространство потока),
Все параметры графике (вершина или фрагмент) или вычисляют функцию, которые являются указателем, или ссылка на тип должна быть объявлена со спецификатором адресного пространства. Для графических функций параметр, который является указателем или ссылкой на тип, должен быть объявлен в device
или constant
адресное пространство. Для функций ядра параметр, который является указателем или ссылкой на тип, должен быть объявлен в device
, threadgroup
, или constant
адресное пространство. Следующий пример представляет использование нескольких спецификаторов адресного пространства. ( threadgroup
спецификатор поддерживается здесь для указателя l_data
только если foo
вызывается функцией ядра, как детализировано в threadgroup Адресном пространстве).
void foo(device int *g_data, |
threadgroup int *l_data, |
constant float *c_data) |
{ |
... |
} |
Адресное пространство для переменной в объеме программы должно быть constant
.
Любая переменная, которая является указателем или ссылкой, должна быть объявлена с одним из спецификаторов адресного пространства, обсужденных в этом разделе. Если спецификатор адресного пространства отсутствует на объявлении типа указателя или ссылочного типа, ошибка компиляции происходит.
Пространство адреса устройства
device
имя адресного пространства относится к объектам буферной памяти, выделенным от пула памяти устройства, которые и читаемы и writeable.
Объект буферной памяти может быть объявлен как указатель или ссылка на скаляр, вектор или определяемую пользователем структуру. Когда объект памяти выделяется с помощью надлежащих Металлических вызовов API платформы в коде узла, фактический размер объекта буферной памяти определяется. Например:
// an array of a float vector with 4 components |
device float4 *color; |
struct Foo { |
float a[3]; |
int b[2]; |
}; |
// an array of Foo elements |
device Foo *my_info; |
Так как Металл всегда выделяет объекты текстуры от пространства адреса устройства, Вам не нужен a device
спецификатор адреса для доступа к типам текстуры. Вы не можете непосредственно получить доступ к элементам объекта текстуры; однако, можно читать из или записать для текстурирования объектов с помощью функций от Металлической стандартной библиотеки. Для получения дополнительной информации посмотрите Функции Текстуры.
Адресное пространство threadgroup
threadgroup
имя адресного пространства используется для выделения переменных, используемых функцией ядра, которые совместно используются всеми потоками threadgroup. Переменные, объявленные в threadgroup
адресное пространство не может использоваться в графических функциях.
Переменные, выделенные в threadgroup
адресное пространство в функции ядра выделяется для каждого threadgroup выполнение ядра. Эти переменные существуют только для времени жизни threadgroup, выполняющего ядро.
Пример ниже шоу, как переменные выделили в threadgroup
адресное пространство может быть передано или как параметры или может быть объявлено в функции ядра. Спецификатор [[ threadgroup(0) ]]
в коде ниже объяснен в Спецификаторах Атрибута для Определения местоположения Ресурсов.
kernel void my_func(threadgroup float *a [[ threadgroup(0) ]], ...) { // A float allocated in threadgroup address space threadgroup float x; // An array of 10 floats allocated in // threadgroup address space threadgroup float b[10]; ... } |
Пространство базового адреса
constant
имя адресного пространства относится к объектам буферной памяти только для чтения, выделяющимся от пула памяти устройства. Переменные в объеме программы должны быть объявлены в constant
адресное пространство и инициализированный во время оператора объявления. Значения, используемые для инициализации их, должны быть постоянным временем компиляции. Переменные в объеме программы имеют то же время жизни как программа, и их значения сохраняются между вызовами к любому вычислению или графическим функциям в программе.
constant float samples[] = { 1.0f, 2.0f, 3.0f, 4.0f }; |
Указатели или ссылки на constant
адресное пространство позволяется как параметры функциям.
Запись в переменные, объявленные в constant
адресное пространство приводит к ошибке времени компиляции. Объявление такой переменной без инициализации является также ошибкой времени компиляции.
распараллельте Адресное пространство
thread
адресное пространство относится к пространству адреса памяти на поток. Переменные, выделенные в этом адресном пространстве, не видимы к другим потокам. Переменные, объявленные в графике или функции ядра, выделяются в thread
адресное пространство.
kernel void my_func(...) |
{ |
// A float allocated in the per-thread address space |
float x; |
// A pointer to variable x in per-thread address space |
thread float p = &x; |
... |
} |
Аргументы функции и переменные
Все вводы и выводы к графике или функциям ядра передаются как параметры (за исключением инициализированных переменных в constant
адресное пространство и сэмплеры, объявленные в объеме программы). Параметрами графике и функциям ядра может быть одно из следующего:
буфер устройства — указатель или ссылка на любой тип данных в
device
адресное пространство (см. Буферы),постоянный буфер — указатель или ссылка на любой тип данных в
constant
адресное пространство (см. Буферы),a
texture
объект (см. Текстуры),a
sampler
объект (см. Сэмплеры),буфер, совместно использованный потоками в threadgroup — указатель на тип в
threadgroup
адресное пространство. (Этот буфер может только использоваться в качестве параметра с функциями ядра.)
Буферы (устройство и постоянный) указанный как значения аргументов к графике или функции ядра не могут быть alieased — т.е. буфер передал, поскольку значение аргумента не может наложиться, другой буфер передал отдельному параметру той же графики или функции ядра.
Параметры этим функциям часто указываются со спецификаторами атрибута для обеспечения дальнейшего руководства на их использовании. Спецификаторы атрибута используются для указания:
расположение ресурса для параметра (см. Спецификаторы Атрибута для Определения местоположения Ресурсов),
встроенные переменные, поддерживающие передающие данные между этапами стандартной функции и программируемого конвейера (см. Спецификаторы Атрибута для Определения местоположения Вводов На вершину),
какие данные отправляются вниз конвейер от функции вершины для фрагментации функции (см. stage_in Спецификатор).
Спецификаторы атрибута для определения местоположения буферов, текстур и сэмплеров
Для каждого параметра спецификатор атрибута должен быть указан для идентификации расположения буфера, текстуры или сэмплера для использования для этого типа аргумента. Металлическая платформа API использует этот атрибут для идентификации расположения для этих типов аргумента:
устройство и постоянные буферы —
[[ buffer(index) ]]
текстура —
[[ texture (index) ]]
сэмплер —
[[ sampler (index) ]]
буфер threadgroup —
[[ threadgroup (index) ]]
index
значение является целым без знака, идентифицирующим расположение присваивающегося буфера, текстуры или параметра сэмплера. Правильный синтаксис для спецификатора атрибута для следования за параметром/именем переменной.
Примером ниже является простая функция ядра, add_vectors
, это добавляет массив двух буферов в пространстве адреса устройства, inA
и inB
, и возвращает результат в буфере out
. Спецификаторы атрибута (buffer(index)
) укажите буферные расположения для аргументов функции.
kernel void add_vectors(const device float4 *inA [[ buffer(0) ]], const device float4 *inB [[ buffer(1) ]], device float4 *out [[ buffer(2) ]], uint id [[ thread_position_in_grid ]]) { out[id] = inA[id] + inB[id]; } |
Пример ниже шоу приписывает спецификаторы, используемые для аргументов функции нескольких различных типов (буфер, текстура и сэмплер).
kernel void |
my_kernel(device float4 *p [[ buffer(0) ]], |
texture2d<float> img [[ texture(0) ]], |
sampler sam [[ sampler(1) ]]) |
{ |
... |
} |
Пример Функции вершины с помощью Спецификаторов Атрибута
Следующим примером является функция вершины, render_vertex
, какие выводы к памяти устройства в массиве xform_pos_output
, который является аргументом функции, указанным с device
спецификатор (представленный в Аргументах функции и Переменных). Весь render_vertex
аргументы функции указаны со спецификаторами (buffer(0)
, buffer(1)
, buffer(2)
, и buffer(3)
), как представлено в Спецификаторах Атрибута для Определения местоположения Ресурсов. ( position
спецификатор, показанный в этом примере, обсужден в Спецификаторах Атрибута для Определения местоположения Вводов На вершину.)
#include <metal_stdlib> using namespace metal; struct VertexOutput { float4 position [[ position ]]; float4 color; float2 texcoord; }; struct VertexInput { float4 position; float3 normal; float2 texcoord; }; constexpr constant uint MAX_LIGHTS = 4; struct LightDesc { uint num_lights; float4 light_position[MAX_LIGHTS]; float4 light_color[MAX_LIGHTS]; float4 light_attenuation_factors[MAX_LIGHTS]; }; vertex VertexOutput render_vertex(const device VertexInput* v_in [[ buffer(0) ]], constant float4x4& mvp_matrix [[ buffer(1) ]], constant LightDesc& light_desc [[ buffer(2) ]], device float4* xform_pos_output [[ buffer(3) ]], uint v_id [[ vertex_id ]] ) { VertexOutput v_out; v_out.position = v_in[v_id].position * mvp_matrix; v_out.color = do_lighting(v_in[v_id].position, v_in[v_id].normal, light_desc); v_out.texcoord = v_in[v_id].texcoord; // output position to a buffer xform_pos_output[v_id] = v_out.position; return v_out; } |
Спецификаторы атрибута для определения местоположения вводов на вершину
Функция вершины может читать, вводы на вершину путем индексации в один или несколько буферов передали как параметры функции вершины использование вершины и экземпляра IDs. Вводы на вершину могут также быть переданы как параметр функции вершины путем объявления их с [[ stage_in ]]
спецификатор атрибута. Для вводов на вершину, переданных как параметр, объявленный с stage_in
спецификатор, каждый элемент ввода на вершину должен указать расположение атрибута вершины как [[ attribute(index) ]]
.
Индексное значение является целым без знака, идентифицирующим присваивающееся входное расположение вершины. Правильный синтаксис для спецификатора атрибута для следования за параметром/именем переменной. Металл использует этот атрибут, чтобы идентифицировать расположение буфера вершины и описать данные вершины, такие как буфер для выборки данных на вершину от, его формат данных и его шаг.
Пример ниже шоу, как атрибуты вершины могут быть присвоены элементам входной структуры вершины, передал функции вершины использование stage_in
спецификатор.
#include <metal_stdlib> using namespace metal; struct VertexInput { float4 position [[ attribute(0) ]]; float3 normal [[ attribute(1) ]]; half4 color [[ attribute(2) ]]; half2 texcoord [[ attribute(3) ]]; }; constexpr constant uint MAX_LIGHTS = 4; struct LightDesc { uint num_lights; float4 light_position[MAX_LIGHTS]; float4 light_color[MAX_LIGHTS]; float4 light_attenuation_factors[MAX_LIGHTS]; }; constexpr sampler s = sampler(coord::normalized, address::clamp_to_zero, filter::linear); vertex VertexOutput render_vertex(VertexInput v_in [[ stage_in ]], constant float4x4& mvp_matrix [[ buffer(1) ]], constant LightDesc& lights [[ buffer(2) ]], uint v_id [[ vertex_id ]]) { VertexOutput v_out; ... return v_out; } |
Пример ниже шоу, как оба буфера и stage_in
спецификатор может использоваться для выборки вводов на вершину в функции вершины.
#include <metal_stdlib> using namespace metal; struct VertexInput { float4 position [[ attribute(0) ]]; float3 normal [[ attribute(1) ]]; }; struct VertexInput2 { half4 color; half2 texcoord[4]; }; constexpr constant uint MAX_LIGHTS = 4; struct LightDesc { uint num_lights; float4 light_position[MAX_LIGHTS]; float4 light_color[MAX_LIGHTS]; float4 light_attenuation_factors[MAX_LIGHTS]; }; constexpr sampler s = sampler(coord::normalized, address::clamp_to_zero, filter::linear); vertex VertexOutput render_vertex(VertexInput v_in [[ stage_in ]], VertexInput2 v_in2 [[ buffer(0) ]], constant float4x4& mvp_matrix [[ buffer(1) ]], constant LightDesc& lights [[ buffer(2) ]], uint v_id [[ vertex_id ]]) { VertexOutput vOut; ... return vOut; } |
Спецификаторы атрибута для встроенных переменных
Некоторые графические операции происходят на этапах конвейера стандартных функций и должны обеспечить значения для или получить значения из, графические функции. Встроенные переменные ввода и вывода используются для передачи значений между графикой (вершина и фрагментом) функции и настройки канала связи графики стандартной функции. Спецификаторы атрибута используются с параметрами и типами возврата графических функций для идентификации этих встроенных переменных.
Спецификаторы атрибута для ввода функции вершины
Таблица 4-1 перечисляет встроенные спецификаторы атрибута, которые можно указать для параметров функции вершины и соответствующим типам данных, с которыми они могут использоваться.
Спецификатор атрибута | Соответствующие типы данных |
---|---|
|
|
|
|
Спецификаторы атрибута для вывода функции вершины
Таблица 4-2 перечисляет встроенные спецификаторы атрибута, которые можно указать для типа возврата функции вершины или для элементов структуры, возвращающихся функцией вершины (и соответствующие типы данных, с которыми они могут использоваться).
Спецификатор атрибута | Соответствующие типы данных |
---|---|
|
|
|
|
|
|
Пример ниже описывает вызванную функцию вершины process_vertex
. Вместо того, чтобы представлять позицию вершины с возвращаемым значением функции, функция возвращает определяемую пользователем структуру, вызванную VertexOutput
. Поскольку функция вершины обеспечивает позицию вершины в своем возвращаемом значении, необходимо использовать [[ position ]]
спецификатор для указания, какой элемент структуры представляет позицию вершины.
struct VertexOutput { float4 position [[ position ]]; float4 color; float2 texcoord; }; vertex VertexOutput process_vertex(...) { VertexOutput v_out; // compute per-vertex output ... return v_out; } |
Спецификаторы атрибута для ввода функции фрагмента
Таблица 4-3 перечисляет встроенные спецификаторы атрибута, которые могут быть указаны для параметров функции фрагмента (и их соответствующие типы данных).
Спецификатор атрибута | Соответствующие типы данных | Описание |
---|---|---|
|
| Входное значение читало из цветного присоединения. Индекс |
|
| Булево значение, которое является |
|
| Двумерные координаты, указывающие, где в точке, примитивной расположен текущий фрагмент. Они колеблются от 0,0 до 1,0 через точку. |
|
| Относительная координата окна (x, y, z, 1/w) оценивает за фрагмент. |
|
| Демонстрационное число выборки, в настоящее время обрабатываемой. |
|
| Набор выборок, покрытых примитивной генерацией фрагмента во время мультидемонстрационной растеризации. |
Переменная, объявленная с [[ position ]]
припишите, как введено функции фрагмента, может только быть объявлен с center_no_perspective
выборка и спецификатор интерполяции.
Для [[ color(m) ]]
, m
используется для указания цветного присоединяемого индекса при доступе (чтение или запись) к многократным цветным присоединениям в функции фрагмента.
Спецификаторы атрибута для вывода функции фрагмента
Тип возврата функции фрагмента описывает вывод на фрагмент. Функция фрагмента может вывести одно или более значений целевого цвета рендеринга, значение глубины и маску покрытия, которая должна быть идентифицирована при помощи спецификаторов атрибута, перечисленных в Таблице 4-4. Если значение глубины не выводится функцией фрагмента, значение глубины, сгенерированное rasterizer, выводится к присоединению глубины.
Спецификатор атрибута | Соответствующие типы данных |
---|---|
|
|
|
|
|
|
Цветной присоединяемый индекс m
для фрагмента вывод указан таким же образом, как это для [[ color(m) ]]
для ввода фрагмента (см. обсуждение для Таблицы 4-3).
Если существует только единственное цветное присоединение в функции фрагмента, [[ color(m) ]]
является дополнительным. Если [[ color(m) ]]
не указан, присоединяемый индекс будет 0. Если многократные цветные присоединения указаны, [[ color(m) ]]
должен быть указан для всех значений цвета. Посмотрите примеры указания цветного присоединения в Функции На фрагмент по сравнению с Функцией На выборку и Программируемым Смешиванием.
Если функция фрагмента пишет значение глубины, depth_qualifier
должен быть указан с одним из следующих значений: any
, greater
, или less
.
Следующий пример показывает, как могут быть указаны цветные присоединяемые индексы. Значения цвета, записанные в clr_f
запишите для окраски присоединяемого индекса 0, значения цвета записанный в clr_i
запишите для окраски присоединяемого индекса 1 и значений цвета записанными в clr_ui
запишите для окраски присоединяемого индекса 2.
struct MyFragmentOutput { // color attachment 0 float4 clr_f [[ color(0) ]]; // color attachment 1 int4 clr_i [[ color(1) ]]; // color attachment 2 uint4 clr_ui [[ color(2) ]]; }; fragment MyFragmentOutput my_frag_shader( ... ) { MyFragmentOutput f; .... f.clr_f = ...; ... return f; } |
Спецификаторы атрибута для ввода функции ядра
Когда ядро представлено для выполнения, оно выполняется по n-мерной сетке потоков, где n равняется 1, 2, или 3. Экземпляр ядра выполняется для каждой точки в этой сетке. Поток является экземпляром ядра, выполняющегося для каждой точки в этой сетке, и thread_position_in_grid
идентифицирует его позицию в сетке.
Потоки организованы в threadgroups. Потоки в threadgroup сотрудничают путем совместного использования данных через threadgroup
память и путем синхронизации их выполнения для координирования доступов памяти к обоим device
и threadgroup
память. Потоки в данном threadgroup выполняются одновременно на сингле, вычисляют модуль на GPU. (GPU может иметь многократный, вычисляют модули. Многократный threadgroups может выполниться одновременно через кратное число, вычисляют модули.) В вычислить модуле threadgroup делится в многократные меньшие группы для выполнения. Ширина выполнения вычислить модуля, называемого thread_execution_width
, определяет рекомендуемый размер этой меньшей группы. Для лучшей производительности общее количество потоков в threadgroup должно быть кратным числом thread_execution_width
.
Threadgroups присваиваются уникальная позиция в сетке (называемый как threadgroup_position_in_grid
) с той же размерностью, поскольку индексное пространство используется для потоков. Потоки присваиваются уникальная позиция в threadgroup (называемый как thread_position_in_threadgroup
). Уникальным скалярным индексом потока в threadgroup дают thread_index_in_threadgroup
.
Позиция каждого потока в сетке и позиция в threadgroup являются n-мерными кортежами. Threadgroups присваиваются позиция с помощью аналогичного подхода к используемому для потоков. Потоки присвоены threadgroup и даны позицию в threadgroup с компонентами в диапазоне от нуля до размера threadgroup в той размерности минус одна.
Когда ядро представлено для выполнения, число threadgroups и threadgroup размера указано. Например, считайте ядро представленным для выполнения, использующего 2-мерную сетку, где число указанного threadgroups (Wx, Вайоминг) и где threadgroup размер (Sx, Си).
Позвольте (wx, wy) быть позицией каждого threadgroup в сетке (т.е. threadgroup_position_in_grid
), и (lx, ly) быть позицией каждого потока в threadgroup (т.е. thread_position_in_threadgroup
).
Позиция потока в сетке (т.е. thread_position_in_grid
):
(gx, gy) = (wx * Sx + lx, wy * Си + ly)
Размер сетки (т.е. threads_per_grid
):
(Gx, Gy) = (Wx * Sx, Вайоминг * Си)
Список веток в threadgroup (т.е. thread_index_in_threadgroup
):
ly * Sx + lx
Таблица 4-5 перечисляет встроенные спецификаторы атрибута, которые могут быть указаны для параметров вычислить функции и соответствующим типам данных, с которыми они могут использоваться.
Спецификатор атрибута | Соответствующие типы данных |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Примечания по ядру функционируют спецификаторы атрибута:
Тип раньше объявлял
[[ thread_position_in_grid ]]
,[[ threads_per_grid ]]
,[[ thread_position_in_threadgroup ]]
,[[ threads_per_threadgroup ]]
,[[ threadgroup_position_in_grid ]]
и[[ threadgroups_per_grid ]]
должен быть скалярный тип или тип вектора. Если это - тип вектора, число компонентов для типов векторов, используемых, чтобы объявить, что должны соответствовать эти параметры.Типы данных раньше объявляли
[[ thread_position_in_grid ]]
и[[ threads_per_grid ]]
должен соответствовать.Типы данных раньше объявляли
[[ thread_position_in_threadgroup ]]
и[[ threads_per_threadgroup ]]
должен соответствовать.Если
thread_position_in_threadgroup
как объявляют, имеет типuint
,uint2
, илиuint3
, тогда[[ thread_index_in_threadgroup ]]
как должны объявлять, имеет типuint
.
Спецификатор stage_in
Металл генерирует вводы на фрагмент к функции фрагмента использование вывода от функции вершины и фрагментов, сгенерированных rasterizer. Вводы на фрагмент идентифицируются с [[ stage_in ]]
спецификатор атрибута.
Функция вершины может читать, вводы на вершину путем индексации в один или несколько буферов передали как параметры функции вершины использование вершины и экземпляра IDs. Вводы на вершину могут также быть переданы как параметры функции вершины путем объявления их с [[ stage_in ]]
спецификатор атрибута.
Только один параметр фрагмента или функции вершины может быть объявлен с stage_in спецификатором. Для определяемой пользователем структуры, объявленной с stage_in
спецификатор, элементы структуры могут быть:
скалярное целочисленное или значение с плавающей точкой или
вектор целого числа или значений с плавающей точкой.
Пример Функции вершины Используя stage_in Спецификатор
Следующий пример показывает, как передать вводы на вершину с помощью stage_in
спецификатор:
#include <metal_stdlib> using namespace metal; struct VertexOutput { float4 position [[ position ]]; float4 color; float2 texcoord[4]; }; struct VertexInput { float4 position [[ attribute(0) ]]; float3 normal [[ attribute(1) ]]; half4 color [[ attribute(2) ]]; half2 texcoord [[ attribute(3) ]]; }; constexpr constant uint MAX_LIGHTS = 4; struct LightDesc { uint num_lights; float4 light_position[MAX_LIGHTS]; float4 light_color[MAX_LIGHTS]; float4 light_attenuation_factors[MAX_LIGHTS]; }; constexpr sampler s = sampler(coord::normalized, address::clamp_to_zero, filter::linear); vertex VertexOutput render_vertex(VertexInput v_in [[ stage_in ]], constant float4x4& mvp_matrix [[ buffer(1) ]], constant LightDesc& lights [[ buffer(2) ]], uint v_id [[ vertex_id ]]) { VertexOutput v_out; v_out.position = v_in.position * mvp_matrix; v_out.color = do_lighting(v_in.position, v_in.normal, lights); ... return v_out; } |
Пример Функции фрагмента Используя stage_in Спецификатор
Пример в Спецификаторах Атрибута для Определения местоположения Вводов На вершину представил process_vertex
функция вершины, возвращающая a VertexOutput
структура на вершину. В следующем примере, выводе от process_vertex
является конвейерным для становления введенным для вызванной функции фрагмента render_pixel
, в котором первый параметр функции фрагмента использует [[ stage_in ]]
спецификатор и поступление VertexOutput
ввести. (В render_pixel
, imgA
и imgB
2D текстуры вызывают встроенную функцию sample
, который представлен в 2D Текстуре.)
#include <metal_stdlib> using namespace metal; struct VertexOutput { float4 position [[ position ]]; float4 color; float2 texcoord; }; struct VertexInput { float4 position; float3 normal; float2 texcoord; }; constexpr constant uint MAX_LIGHTS = 4; struct LightDesc { uint num_lights; float4 light_position[MAX_LIGHTS]; float4 light_color[MAX_LIGHTS]; float4 light_attenuation_factors[MAX_LIGHTS]; }; constexpr sampler s = sampler(coord::normalized, address::clamp_to_border, filter::linear); vertex VertexOutput render_vertex(const device VertexInput* v_in [[ buffer(0) ]], constant float4x4& mvp_matrix [[ buffer(1) ]], constant LightDesc& lights [[ buffer(2) ]], uint v_id [[ vertex_id ]]) { VertexOutput v_out; v_out.position = v_in[v_id].position * mvp_matrix; v_out.color = do_lighting(v_in[v_id].position, v_in[v_id].normal, lights); v_out.texcoord = v_in[v_id].texcoord; return v_out; } fragment float4 render_pixel(VertexOutput input [[ stage_in ]], texture2d<float> imgA [[ texture(0) ]], texture2d<float> imgB [[ texture(1) ]]) { float4 tex_clr0 = imgA.sample(s, input.texcoord); float4 tex_clr1 = imgB.sample(s, input.texcoord); // compute color float4 clr = compute_color(tex_clr0, tex_clr1, ...); return clr; } |
Спецификаторы класса памяти
Металлический язык штриховки поддерживает extern
и static
спецификаторы класса памяти. Металл не поддерживает thread_local
спецификаторы класса памяти.
Используйте extern
спецификатор класса памяти только для функций и переменных, объявленных в объеме программы или для переменных, объявленных в функции. Используйте static
спецификатор класса памяти только для переменных, объявленных в объеме программы (см. Пространство базового адреса), а не для переменных, объявленных в графике или функции ядра. В следующем примере, static
спецификатор неправильно используется переменными b
и c
объявленный в функции ядра:
#include <metal_stdlib> using namespace metal; extern constant float4 noise_table[256]; static constant float4 color_table[256] = { ... }; // static is okay extern void my_foo(texture2d<float> img); extern void my_bar(device float *a); kernel void my_func(texture2d<float> img [[ texture(0) ]], device float *ptr [[ buffer(0) ]]) { extern constant float4 a; static constant float4 b; // static is an error. static float c; // static is an error. ... my_foo(img); ... my_bar(ptr); ... } |
Выборка и спецификаторы интерполяции
Выборка и спецификаторы интерполяции используется с вводами для фрагментации функций, объявленных с stage_in
спецификатор. Спецификатор определяет то, что, выбирая метод фрагмент функционирует использование и как интерполяция выполняется, включая то, использовать ли корректную перспективой интерполяцию, линейную интерполяцию или никакую интерполяцию.
Выборка и спецификатор интерполяции могут быть указаны на любом элементе структуры, объявленном с stage_in
спецификатор. Выборка и поддерживаемые спецификаторы интерполяции:
center_perspective |
center_no_perspective |
centroid_perspective |
centroid_no_perspective |
sample_perspective |
sample_no_perspective |
flat |
center_perspective
выборка по умолчанию и спецификатор интерполяции для всех спецификаторов атрибута для типов возврата функций вершины и параметров функциям фрагмента, за исключением [[ position ]]
, который может быть объявлен только с center_no_perspective
.
Следующим примером является определяемая пользователем структура, указывающая, как интерполированы данные в определенных элементах:
struct FragmentInput { float4 pos [[ center_no_perspective ]]; float4 color [[ center_perspective ]]; float2 texcoord; int index [[ flat ]]; float f [[ sample_perspective ]]; }; |
Для целых типов единственный допустимый спецификатор интерполяции flat
.
Варианты спецификатора выборки (sample_perspective
и sample_no_perspective
) интерполируйте в демонстрационном расположении, а не в пиксельном центре. Когда Вы маркируете одну или более переменных a sample
спецификатор вместо a center
или centroid
спецификатор, функции фрагмента (или блоки кода в функции фрагмента) использующий эти переменные выполняется на выборку, а не на фрагмент.
Функция на фрагмент по сравнению с функцией на выборку
Функция фрагмента обычно выполняется на фрагмент. Если какой-либо ввод фрагмента должен быть интерполирован в на выборку по сравнению с на фрагмент, спецификатор выборки идентифицирует. Точно так же [[ sample_id ]]
атрибут используется для идентификации текущего демонстрационного индекса и [[ color(m) ]]
атрибут используется для идентификации целевого цвета фрагмента или демонстрационного цвета (для присоединения мультивыбранного цвета) значение. Если какой-либо из этих спецификаторов используется с параметрами функции фрагмента, функция фрагмента может выполниться на выборку вместо на пиксель. (Реализация может решить только выполнить код на выборку, зависящую от на демонстрационные значения, и остальная часть функции фрагмента может выполниться на фрагмент.)
Только вводы с указанной выборкой (или объявленный с [[ sample_id ]]
или [[ color(m) ]]
спецификатор), отличаются между вызовами на фрагмент или на выборку, тогда как другие вводы все еще интерполируют в пиксельном центре.
Следующий пример использует [[ color(m) ]]
атрибут, чтобы указать, что эта функция фрагмента должна быть выполнена на основе на выборку:
#include <metal_stdlib> using namespace metal; fragment float4 my_frag_shader(float2 tex_coord [[ stage_in ]], texture2d<float> img [[ texture(0) ]], sampler s [[ sampler(0) ]], float4 framebuffer [[ color(0) ]]) { return c = mix(img.sample(s, tex_coord), framebuffer, mix_factor); } |
Программируемое смешивание
Функция фрагмента может использоваться для выполнения программируемого смешивания на выборку или на фрагмент. Цветной присоединяемый индекс, идентифицированный [[ color(m) ]]
спецификатор атрибута может быть указан как параметр функции фрагмента.
Пример ниже демонстрирует программируемый смешивающий OpenGL ES, с помощью метода, детализированного в Данных Кадрового буфера Выборки для Программируемого Смешивания для применения полутонового эффекта преобразования к тому, что представляется, прежде чем программа построения теней выполняется. Перечисление 4-1 показывает программу построения теней GLSL 1.0 фрагмента (для использования с OpenGL ES 2.0), и Перечисление 4-2 показывает эквивалентную Металлическую функцию фрагмента.
Перечисление 4-1 , программируемое смешивающий OpenGL ES 2.0
#extension GL_APPLE_shader_framebuffer_fetch : require void main() { // RGB to grayscale mediump float lum = dot(gl_LastFragData[0].rgb, vec3(0.30,0.59,0.11)); gl_FragColor = vec4(lum, lum, lum, 1.0); } |
Перечисление 4-2 , программируемое смешивающий металл
#include <metal_stdlib> using namespace metal; fragment half4 paint_grayscale(half4 dst_color [[ color(0) ]]) { // RGB to grayscale half lum = dot(dst_color.rgb, half3(0.30h, 0.59h, 0.11h)); return half4(lum, lum, lum, 1.0h); } |
Объединение функций вершины и фрагмента в графическом конвейере
Типичный Металлический графический конвейер комбинирует функцию вершины и функцию фрагмента. В самом простом случае Вы определяете функцию вершины и функцию фрагмента вместе, такой, что выходной тип функции вершины является входным типом функции фрагмента. Перечисление 4-3 иллюстрирует этот сценарий.
Перечисление 4-3 простая комбинация функций вершины и фрагмента
struct VertexOutput { float4 position [[ position ]]; float3 normal; float2 texcoord; }; vertex VertexOutput my_vertex_shader(...) { VertexOutput v; ... return v; } fragment float4 my_fragment_shader(VertexOutput f [[ stage_in ]], ...) { float4 clr; ... return clr; } fragment float4 my_fragment_shader2(VertexOutput f [[ stage_in ]], bool is_front_face [[ front_facing ]], ...) { float4 clr; ... return clr; } |
В этом примере можно соединиться my_vertex_shader
функция с любым my_fragment_shader
функционируйте или my_fragment_shader2
функция для конфигурирования конвейера рендеринга.
Однако в более сложном приложении может быть полезно объединить вершину и функции фрагмента, не требуя, чтобы те функции использовали тождественно определенные типы данных. Системное определение, которое могут соединить функции вершины, с которым функционирует фрагмент, вызывают графическим соответствием функциональной подписи, где графическая функциональная подпись является списком параметров, или вводящихся к или выводящихся от функции фрагмента или вершины.
Графическая функциональная подпись комбинирует некоторых или все следующие элементы:
Ввод на фрагмент к функции фрагмента, объявленной с
[[ stage_in ]]
спецификатор. Они выводятся связанной функцией вершины.Встроенные переменные, объявленные с одним из спецификаторов атрибута, определенных в Спецификаторах Атрибута для Определения местоположения Вводов На вершину. Они сгенерированы любой функцией вершины (такой как
[[ position ]]
,[[ point_size ]]
,[[ clip_distance ]]
) или rasterizer (такой как[[ point_coord ]]
,[[ front_facing ]]
,[[ sample_id ]]
,[[ sample_mask ]]
) Или они могут обратиться к значению цвета кадрового буфера (такой как[[ color ]]
) переданный как ввод функции фрагмента.Определяемые пользователем переменные, объявленные с синтаксисом спецификатора атрибута
[[ user(name) ]]
.
Функция вершины и функция фрагмента, как полагают, имеют соответствие подписей и таким образом имеют право быть объединенными в графическом конвейере в любом из двух случаев:
Если функция фрагмента не объявляет входного аргумента с
[[ stage_in ]]
спецификатор.Если функция фрагмента объявляет входной аргумент с
[[ stage_in ]]
спецификатор и соблюдающие правила применяются:Каждый элемент во входном аргументе является встроенной переменной, сгенерированной rasterizer, значение цвета кадрового буфера передало как ввод функции фрагмента или сгенерированный пользователями вывод от функции вершины.
Каждая пользовательская сгенерированная переменная во входном аргументе указана с a
[[ user(name) ]]
спецификатор, где имя соответствует a[[ user(name) ]]
спецификатор в типе возврата функции вершины. Типы данных элементов с соответствием[[ user(name) ]]
спецификаторы должны также соответствовать.Также, если входной аргумент содержит определяемые пользователем переменные, не указанные с
[[ user(name) ]]
спецификатор, и имена и типы переменных должен соответствовать.
Перечисление 4-4 показывает пример совместимых подписей.
Перечисление 4-4 совместимые функциональные подписи со спецификаторами для пользовательских сгенерированных переменных
struct VertexOutput { float4 position [[ position ]]; float3 vertex_normal [[ user(normal) ]]; float2 texcoord [[ user(texturecoord) ]]; }; struct FragInput { float3 frag_normal [[ user(normal) ]]; float4 position [[ position ]]; float4 framebuffer_color [[ color(0) ]]; bool is_front_face [[ front_facing ]]; }; vertex VertexOutput my_vertex_shader(...) { VertexOutput v; ... return v; } fragment float4 my_fragment_shader(FragInput f [[ stage_in ]], ...) { float4 clr; ... return clr; } |
В этом примере, функции вершины my_vertex_shader
совместимо с функцией фрагмента my_fragment_shader
потому что:
Функция фрагмента использует
[[ position ]]
и[[ user(normal) ]]
атрибуты выводятся от вершинного шейдера.Другие атрибуты, используемые в функции фрагмента, сгенерированы rasterizer или считаны из кадрового буфера.
Обратите внимание на то, что функции рассматривают, соответствуя даже при том, что функция фрагмента не использует [[ user(texturecoord) ]]
атрибут выводится от функции вершины.
Перечисление 4-5 показывает другой пример совместимых подписей.
Перечисление 4-5 совместимые функциональные подписи с соответствием имен элементов
struct VertexOutput { float4 position [[ position ]]; float3 normal; float2 texcoord; }; struct FragInput { float4 position [[ position ]]; float2 texcoord; }; vertex VertexOutput my_vertex_shader(...) { VertexOutput v; ... return v; } fragment float4 my_fragment_shader(FragInput f [[ stage_in ]], ...) { float4 clr; ... return clr; } |
В этом примере и вершина и программы построения теней фрагмента используют элемент типа float2
именованный texcoord
. Поскольку имя и тип этих элементов соответствие, Металл может использовать texcoord
значение, выведенное вершиной, функционирует для создания texcoord
оцените ввод функции фрагмента.
Перечисление 4-6 показывает третий пример совместимых подписей.
Перечисление 4-6 Совместимые Подписи Без stage_in Спецификатора
struct VertexOutput { float4 position [[ position ]]; float3 normal; float2 texcoord; }; vertex VertexOutput my_vertex_shader(...) { VertexOutput v; ... return v; } fragment float4 my_fragment_shader(float4 p [[ position ]], ...) { float4 clr; ... return clr; } |
В этом примере, функции фрагмента my_fragment_shader
не имеет никакого ввода, объявленного с [[ stage_in ]]
спецификатор, но соответствия, вершина функционирует my_vertex_shader, потому что оба используют [[ position ]]
атрибут.
Перечисление 4-7 и Перечисление 4-8 иллюстрируют несовместимые подписи:
Перечисление 4-7 несовместимое использование типов
struct VertexOutput { float4 position [[ position ]]; float3 normal; float2 texcoord; }; struct FragInput { float4 position [[ position ]]; half3 normal; }; vertex VertexOutput my_vertex_shader(...) { VertexOutput v; ... return v; } fragment float4 my_fragment_shader(FragInput f [[ stage_in ]], ...) { float4 clr; ... return clr; } |
Перечисление 4-8 несовместимое использование определяемых пользователем переменных
struct VertexOutput { float4 position [[ position ]]; float3 normal [[ user(normal) ]]; float2 texcoord [[ user(texturecoord) ]]; }; struct FragInput { float3 normal [[ user(foo) ]]; float4 position [[ position ]]; }; vertex VertexOutput my_vertex_shader(...) { VertexOutput v; ... return v; } fragment float4 my_fragment_shader(FragInput f [[ stage_in ]], ...) { float4 clr; ... return clr; } |
Оба примера пытаются соответствовать названный элемент normal
между VertexOutput
и FragInput
структуры. Однако в Перечислении 4-7, типы этих элементов не соответствуют. В Перечислении 4-8, [[ user(name) ]]
спецификаторы не соответствуют.
Дополнительные ограничения
Следующие дополнительные ограничения применяются к функциям, переменным и спецификаторам:
Записи к буферу или текстуре запрещены от функции фрагмента.
Записи к текстуре запрещены от вершинного шейдера.
Записи к буферу от функции вершины, как гарантируют, не будут видимы к чтениям от связанной функции фрагмента данного примитива.
Тип возврата вершины или функции фрагмента не может включать элемент, который является упакованным типом вектора, матричным типом, типом массива, или ссылкой или указателем на тип.
Максимум 128 скаляров может использоваться в качестве вводов к функции фрагмента, объявленной с
stage_in
спецификатор. Если ввод к функции фрагмента является вектором, то входной вектор рассчитывает как n скаляры, где n является числом компонентов в векторе.Это ограничение не включает встроенные переменные, объявленные с одним из следующих атрибутов:
[[ color(m) ]]
,[[ front_facing ]]
,[[ sample_id ]]
, и[[ sample_mask ]]
.