Функции, переменные и спецификаторы

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

Функциональные спецификаторы

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

kernel

Параллельная данным функция, выполняющаяся по 1-, 2-или 3-мерной сетке.

vertex

Функция вершины, выполняющаяся для каждой вершины в потоке вершины и это генерирует на вершину вывод.

fragment

Функция фрагмента, выполняющаяся для каждого фрагмента (и его связанные данные) в потоке фрагмента и это генерирует на фрагмент вывод.

Функциональный спецификатор используется в начале функции перед ее типом возврата. Следующий пример показывает синтаксис для вычислить функции:

kernel void
foo(...)
{
    ...
}

Для функций, объявленных с kernel спецификатор, тип возврата должен быть void.

Только графическая функция может быть объявлена с одним из vertex или fragment спецификаторы. Для графических функций тип возврата идентифицирует, является ли вывод, сгенерированный функцией, или на вершину или на фрагмент. Тип возврата для графической функции может быть void указание, что функция не генерирует вывод.

Функции то использование a kernel, vertex или fragment функциональный спецификатор не может вызвать функции, также использующие эти спецификаторы, или ошибка компиляции заканчивается.

Спецификаторы адресного пространства для переменных и параметров

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

Все параметры графике (вершина или фрагмент) или вычисляют функцию, которые являются указателем, или ссылка на тип должна быть объявлена со спецификатором адресного пространства. Для графических функций параметр, который является указателем или ссылкой на тип, должен быть объявлен в 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 адресное пространство и сэмплеры, объявленные в объеме программы). Параметрами графике и функциям ядра может быть одно из следующего:

Буферы (устройство и постоянный) указанный как значения аргументов к графике или функции ядра не могут быть alieased — т.е. буфер передал, поскольку значение аргумента не может наложиться, другой буфер передал отдельному параметру той же графики или функции ядра.

Параметры этим функциям часто указываются со спецификаторами атрибута для обеспечения дальнейшего руководства на их использовании. Спецификаторы атрибута используются для указания:

Спецификаторы атрибута для определения местоположения буферов, текстур и сэмплеров

Для каждого параметра спецификатор атрибута должен быть указан для идентификации расположения буфера, текстуры или сэмплера для использования для этого типа аргумента. Металлическая платформа 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-1  спецификаторы атрибута для входных аргументов функции вершины

Спецификатор атрибута

Соответствующие типы данных

[[ vertex_id ]]

ushort или uint

[[ instance_id ]]

ushort или uint

Спецификаторы атрибута для вывода функции вершины

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

Табличные 4-2  спецификаторы атрибута для типов возврата функции вершины

Спецификатор атрибута

Соответствующие типы данных

[[ clip_distance ]]

float или float[n]

n должен быть известен во время компиляции

[[ point_size ]]

float

[[ position ]]

float4

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

Табличные 4-3  спецификаторы атрибута для входных аргументов функции фрагмента

Спецификатор атрибута

Соответствующие типы данных

Описание

[[ color(m) ]]

floatn, halfn, intn, uintn, shortn, или ushortn

m должен быть известен во время компиляции

Входное значение читало из цветного присоединения. Индекс m указывает который цветное присоединение читать из.

[[ front_facing ]]

bool

Булево значение, которое является true если фрагмент принадлежит обращенному к передней стороне примитиву.

[[ point_coord ]]

float2

Двумерные координаты, указывающие, где в точке, примитивной расположен текущий фрагмент. Они колеблются от 0,0 до 1,0 через точку.

[[ position ]]

float4

Относительная координата окна (x, y, z, 1/w) оценивает за фрагмент.

[[ sample_id ]]

uint

Демонстрационное число выборки, в настоящее время обрабатываемой.

[[ sample_mask ]]

uint

Набор выборок, покрытых примитивной генерацией фрагмента во время мультидемонстрационной растеризации.

Переменная, объявленная с [[ position ]] припишите, как введено функции фрагмента, может только быть объявлен с center_no_perspective выборка и спецификатор интерполяции.

Для [[ color(m) ]], m используется для указания цветного присоединяемого индекса при доступе (чтение или запись) к многократным цветным присоединениям в функции фрагмента.

Спецификаторы атрибута для вывода функции фрагмента

Тип возврата функции фрагмента описывает вывод на фрагмент. Функция фрагмента может вывести одно или более значений целевого цвета рендеринга, значение глубины и маску покрытия, которая должна быть идентифицирована при помощи спецификаторов атрибута, перечисленных в Таблице 4-4. Если значение глубины не выводится функцией фрагмента, значение глубины, сгенерированное rasterizer, выводится к присоединению глубины.

Табличные 4-4  спецификаторы атрибута для типов возврата функции фрагмента

Спецификатор атрибута

Соответствующие типы данных

[[ color(m) ]]

floatn, halfn, intn, uintn, shortn, или ushortn

m должен быть известен во время компиляции

[[ depth(depth_qualifier) ]]

float

[[ sample_mask ]]

uint

Цветной присоединяемый индекс 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 перечисляет встроенные спецификаторы атрибута, которые могут быть указаны для параметров вычислить функции и соответствующим типам данных, с которыми они могут использоваться.

Табличные 4-5  спецификаторы атрибута для входных аргументов функции ядра

Спецификатор атрибута

Соответствующие типы данных

[[ thread_position_in_grid ]]

ushort, ushort2, ushort3, uint, uint2, или uint3

[[ thread_position_in_threadgroup ]]

ushort, ushort2, ushort3, uint, uint2, или uint3

[[ thread_index_in_threadgroup ]]

uint, ushort

[[ threadgroup_position_in_grid ]]

ushort, ushort2, ushort3, uint, uint2, или uint3

[[ threads_per_grid ]]

ushort, ushort2, ushort3, uint, uint2, или uint3

[[ threads_per_threadgroup ]]

ushort, ushort2, ushort3, uint, uint2, или uint3

[[ threadgroups_per_grid ]]

ushort, ushort2, ushort3, uint, uint2, или uint3

[[ thread_execution_width ]]

ushort или uint

Примечания по ядру функционируют спецификаторы атрибута:

  • Тип раньше объявлял [[ 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 функция для конфигурирования конвейера рендеринга.

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

Графическая функциональная подпись комбинирует некоторых или все следующие элементы:

Функция вершины и функция фрагмента, как полагают, имеют соответствие подписей и таким образом имеют право быть объединенными в графическом конвейере в любом из двух случаев:

  1. Если функция фрагмента не объявляет входного аргумента с [[ stage_in ]] спецификатор.

  2. Если функция фрагмента объявляет входной аргумент с [[ 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 потому что:

Обратите внимание на то, что функции рассматривают, соответствуя даже при том, что функция фрагмента не использует [[ 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) ]] спецификаторы не соответствуют.

Дополнительные ограничения

Следующие дополнительные ограничения применяются к функциям, переменным и спецификаторам: