Почему этот геометрический шейдер так сильно тормозит мою программу?

27

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

До того, как я добавил геометрический шейдер, когда я просто программировал этапы фрагмента и затенения вершин конвейера, я получал частоту кадров около 30+. Достаточно того, что я не мог заметить никакой изменчивости. После добавления геометрического шейдера я получаю около 5 кадров в секунду. Зачем? Это весь геометрический шейдер:

#version 420

layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

void main()
{
    for (int i = 0; i < gl_in.length(); i++)
    {
        gl_Position = gl_in[i].gl_Position;
        EmitVertex();
    }
    EndPrimitive();
}

Разве это не то, что OpenGL делал без геометрического шейдера?

Avi
источник

Ответы:

40

Разве это не то, что OpenGL делал без геометрического шейдера?

Нет, это не так. GS - необязательный шаг, а не шаг по умолчанию.

Чтобы OpenGL мог выполнить геометрический шейдер , он должен выполнить так называемую « примитивную сборку ». Когда вы визуализируете серию треугольников через GL_TRIANGLE_STRIP, OpenGL будет выполнять внутреннюю работу для преобразования каждых 3 смежных вершин в отдельный треугольник, соответственно изменяя порядок намотки.

Обычно, когда GS не используется, этот процесс выполняется один раз. Однако, когда вы используете GS, это должно быть выполнено до того, как GS выполнится. Но это также должно быть выполнено после GS, потому что GS может выводить совершенно другой тип примитива (например, четырехугольники).

Так что теперь вы заставляете систему делать кучу дополнительной работы даром. В конце концов, OpenGL не может предположить, что ваш GS ничего не делает (это неразрешимая проблема).

Кроме того, ряд оптимизаций больше не работает в присутствии GS. Рассмотрим индексированный рендеринг.

Каждый индекс из буфера массива элементов будет генерировать одинаковые выходные данные вершинного шейдера. Таким образом , ГПУ будет часто кэшировать эти выходы в кэш пост-T & L . Если он видит индекс, который уже находится в кэше, VS больше не запускается; он просто выбирает данные из кеша.

Что это такое"? «Это» - это ... примитивная сборочная единица . Да, это то, что запускается дважды, когда вы используете GS. Кэширование индекса? Это работает только для входов GS.

Так что же происходит с выходами GS? Ну, это зависит от оборудования. Но это должно войти в какой-то буфер памяти. И в этом заключается проблема: этот буфер вообще не индексируется. Это похоже на ситуацию с glDrawArrays.

Таким образом, если вы отправите индексный буфер в размере 0, 1, 2, 0, 2, 3, это будет преобразовано в 4 вершины в кэше после T & L. Но буфер вершин после GS теперь имеет 6 вершин. Буфер post-GS занимает больше места. Так что, если вы столкнетесь с проблемой составления должным образом оптимизированных списков или полос треугольников после T & L и включите сквозной GS, такой как ваш, вы в основном убили около половины прироста производительности от этой оптимизации.

Это не было бесполезно, но это больно.

К этому следует добавить тот факт, что многие графические процессоры класса GL 3.x (также известные как DX10) имели довольно небольшие буферы после GS. Чем меньше буфер, тем меньше вызовов GS вы можете иметь одновременно. Таким образом, ваше оборудование эффективно ограничивает возможности GS. Поскольку тесселяция является большой характеристикой оборудования класса 4.x, большинство такого оборудования имеет буферы, достаточные для обеспечения более интенсивного использования GS.

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

Для получения дополнительной информации о замедлении, вызванном GS, прочитайте эту статью .

Вот основное правило о GS: никогда не используйте GS, потому что вы думаете, что это сделает рендеринг быстрее . Вы должны использовать это, когда это делает возможным то, что вы пытаетесь сделать . Если вы пытаетесь оптимизировать, используйте что-то другое.

Общие исключения из этого:

Николь Болас
источник
Я пытаюсь вычислить крутизну каждого многоугольника, взяв его наибольшую высоту и вычтя его самую низкую высоту. Однако, если геометрический шейдер обязательно замедлит меня на эту величину, я думаю, что я мог бы сделать это творчески в вершинном шейдере.
Ави
1
@Avi обратите внимание, что самые высокие и самые низкие точки в треугольнике не дадут вам крутизны; вам нужны все три очка.
Сэм Хоцевар
2
Лично я всегда находил экземпляры более полезными для точечных спрайтов, чем GS.
Максимус Минимус
1
Распространяется ли исключение для точечных спрайтов на шейдеры layout(points) in;? Или это фиксированный размер вывода? Или, возможно, оба?
Филипп