Вот очень умозрительная возможность, в зависимости от того, что именно должен делать ваш геометрический шейдер.
Я думаю, вы можете сделать это как бы «назад» с помощью только вершинного шейдера и без отдельного вычислительного шейдера, встоимость избыточной работы на графическом процессоре.Вы бы нарисовали , как если бы имел буфер всех выходных вершин выходных примитивов геометрического шейдера.Вы бы на самом деле не имели этого под рукой.Вы бы построили вершинный шейдер, который вычислял бы их в полете.
Итак, в коде приложения вычислите количество выходных примитивов и, следовательно, количество выходных вершин, которые будут созданы для заданного количества входных примитивов.,Сделайте отрисовку типа выходного примитива с таким количеством вершин.
Вы бы не предоставили бы буфер с данными выходных вершин в качестве входных данных для этого отрисовки.
Вы бы сделалипредоставить исходный индексный буфер и исходный буфер вершин в качестве входных данных для вершинного шейдера для этого отрисовки.Шейдер вычисляет по идентификатору вершины, для какого выходного примитива он предназначен, и для какой вершины этого примитива (например, для треугольника vid / 3
и vid % 3
соответственно).Из идентификатора выходного примитива он вычислит, какой входной примитив сгенерировал бы его в исходном геометрическом шейдере.
Шейдер будет искать индексы для этого входного примитива из буфера индексов, а затем данные вершин избуфер вершин.(Это может быть чувствительным к различию между списком треугольников и полосой треугольника, например.) Это применило бы к этим данным любое затенение вершин предгеометрического шейдера.Затем он выполняет ту часть вычисления геометрии, которая вносит вклад в идентифицированную вершину идентифицированного выходного примитива.Как только он вычислил выходные данные вершин, вы можете применить любое затенение вершин пост-геометрического шейдера (?), Которое вы хотите.Результатом будет то, что он вернет.
Если геометрический шейдер может создавать переменное количество выходных примитивов на входной примитив, ну, по крайней мере, у вас есть максимальное число.Таким образом, вы можете нарисовать максимальное потенциальное количество вершин для максимального потенциального числа выходных примитивов.Вершинный шейдер может выполнять вычисления, необходимые для того, чтобы выяснить, действительно ли геометрический шейдер создал этот примитив.Если нет, вершинный шейдер может организовать отсечение всего примитива, либо расположив его вне усеченного конуса, либо используя свойство [[clip_distance]]
выходных данных вершины.
Это позволяет избежать сохранения сгенерированногопримитивы в буфере.Однако из-за этого графический процессор выполняет несколько вычислений вершинного шейдера и геометрического шейдера до геометрического шейдера.Конечно, оно будет распараллелено, но все же может быть медленнее, чем то, что вы делаете сейчас.Кроме того, он может не справиться с некоторыми оптимизациями при извлечении индексов и данных о вершинах, что возможно при использовании более обычных вершинных шейдеров.
Вот пример преобразования вашего геометрического шейдера:
#include <metal_stdlib>
using namespace metal;
struct VertexIn {
// maybe need packed types here depending on your vertex buffer layout
// can't use [[attribute(n)]] for these because Metal isn't doing the vertex lookup for us
float3 position;
float3 normal;
float2 uv;
};
struct VertexOut {
float3 position;
float3 normal;
float2 uv;
float4 new_position [[position]];
};
vertex VertexOut foo(uint vid [[vertex_id]],
device const uint *indexes [[buffer(0)]],
device const VertexIn *vertexes [[buffer(1)]])
{
VertexOut out;
const uint triangle_id = vid / 3;
const uint vertex_of_triangle = vid % 3;
// indexes is for a triangle strip even though this shader is invoked for a triangle list.
const uint index[3] = { indexes[triangle_id], index[triangle_id + 1], index[triangle_id + 2] };
const VertexIn v[3] = { vertexes[index[0]], vertexes[index[1]], vertexes[index[2]] };
float3 p = abs(cross(v[1].position - v[0].position, v[2].position - v[0].position));
out.position = v[vertex_of_triangle].position;
out.normal = v[vertex_of_triangle].normal;
out.uv = v[vertex_of_triangle].uv;
if (p.z > p.x && p.z > p.y)
{
out.new_position = float4(out.position.x, out.position.y, 0, 1);
}
else if (p.x > p.y && p.x > p.z)
{
out.new_position = float4(out.position.y, out.position.z, 0, 1);
}
else
{
out.new_position = float4(out.position.x, out.position.z, 0, 1);
}
return out;
}