OpenGL ES 2.0: поиск советов по производительности и оптимизации VBO для многих подвижных вершин - PullRequest
9 голосов
/ 30 августа 2011

В своей текущей попытке перейти на OpenGL ES 2.0 из ES 1.x я в настоящее время преобразовываю некоторый код для использования объектов буфера вершин ('VBOs') вместо существующих небуферизованных вызовов glDrawArrays.

IЯ настроил VBO и заставил их работать, но столкнулся с дилеммой проектирования и был бы благодарен совету кого-то более опытного с OpenGL ES 2.0.

Я хочу нарисовать кучу многоугольных спрайтов, которые часто перемещаются,Это потому, что это динамические тела Box2D, если вы знакомы с Box2D.Каждое из этих многоугольников генерируется с использованием GL_TRIANGLE_FAN, что несколько критично, поскольку GL_POLYGON недоступен на ES 2.0.

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

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

Здесь есть несколько путей к успеху, я читал Руководство по программированию OpenGL ES 2.0 , чтобы искать столько жеИнформация и советы по оптимизации, как я могу, относящиеся к VBO, и вот что они говорят:

  • Чередующиеся данные предпочтительны, поскольку атрибутные данные "для каждой вершины можно читать последовательноfashion ".

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

  • Рекомендуется" использовать GL_HALF_FLOAT_OES везде, где это возможно«Особенно это касается цветов, поскольку непроецированные местоположения вершин могут превышать это требование к пространству.

  • glMapBufferOES следует использовать только в случае обновления всего буфера, и даже в этом случае операция "все еще может быть дороже по сравнению с glBufferData" .

Вот мои вопросы :

  1. Если использовать GL_TRIANGLE_FAN в качестве режима рисования, это заставляет меня хранить VBO для тела, а не для группы?Или будет ли общее расположение вершин, чтобы «завершить» вентилятор и текущее тело, заставит новый вентилятор быть нарисованным для следующего тела в группе VBO?

  2. Должен ли я чередовать все свои данныенесмотря на то, что местоположения вершин обновляются с высокой частотой, или я должен разделить все это / только местоположения на их собственные VBO?

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

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

  5. Должен ли я пытаться использовать GL_HALF_FLOAT_OES для хранения цветов, то есть в пространстве с 2 числами с плавающей точкой я вместо этого храню 4 числа с половиной числа с плавающей точкой?Если ответ «да», я бы просто использовал любой тип GL, равный половине размера GLfloat для каждого цвета, затем поразрядно ИЛИ их, а затем вставил в соответствующий массив атрибутов?

  6. После того как я создал X много VBO, являются ли единственными вызовами, которые мне нужно сделать для каждого рендера glBindBuffer, glBufferData и glDrawElements / Arrays, или я должен также вызывать glEnableVertexAttribArray и glVertexAttribPointer каждый раз, когда я использую glBufferData?

Буду очень признателен за дополнительные советы по этому вопросу, спасибо.

1 Ответ

14 голосов
/ 30 августа 2011

У меня нет опыта ES, но я думаю, что многие вещи все еще применимы

  1. Частично, это не заставляет вас использовать один VBO на тело, но у вас естьсделать один glDrawArrays на тело.Они могут все еще получать свои данные из того же буфера, но это все еще не рекомендуется.Вместо этого я бы отошел от сложных примитивов, таких как веерные треугольники или полосы, и использовал бы индексированные списки треугольников, таким образом, все можно нарисовать за один вызов.Я сомневаюсь, что ES поддерживает расширение primitve_restart.При этом вы можете указать специальный индекс вершины, который перезапускает примитив.

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

  3. ЕслиВы обновляете их все каждый кадр, тогда полный glBufferData может быть лучше, чем glBufferSubData.Или вы можете также вызвать glBufferData(..., NULL), а затем glMapBuffer(..., GL_WRITE_ONLY), если вы не хотите хранить массив ЦП для ваших данных.Это говорит водителю, что вам больше не нужны предыдущие данные.Таким образом, драйвер может выделить для вас совершенно новый буфер, в то время как предыдущие данные все еще используются для рендеринга.Таким образом, вы можете выложить новые данные, когда старые все еще используются (старый буфер освобождается драйвером, когда он больше не используется).

  4. заполнитель

  5. Для цветов GL_UNSIGNED_BYTE может быть даже лучше, так как они обычно не нуждаются в такой высокой точности.Это также может хорошо работать с оптимизацией выравнивания, когда, например, у вас есть 3 плавающие координаты и 4 байтовых цветовых канала, что дает вершину из 16 байтов, что очень удобно для выравнивания.В этом случае может быть целесообразно сохранить вершины и цвета в одном и том же буфере.

РЕДАКТИРОВАТЬ: Немного прояснить пункт 3: Если у вас есть данныев любом случае, в массиве ЦП вы можете просто вызвать glBufferData со своими данными.Если вы хотите, чтобы драйвер выделил это хранилище для вас, вы можете использовать glMapBuffer, который дает вам указатель на буферную память, отображаемую в адресное пространство ЦП (и, конечно, вы GL_WRITE_ONLY, так как вы не заботитесь о предыдущемданные).Но в этом случае glBufferData с нулевым указателем выделит совершенно новое хранилище для буфера (без копирования каких-либо данных), что говорит драйверу, что мы не заботимся о предыдущем содержимом (даже они могут в настоящее время *)1040 * все еще будет использоваться для рендеринга).Драйвер может оптимизировать этот случай и выделить новое хранилище под капотом, но все равно не освободить предыдущее хранилище, которое затем освобождается, когда предыдущие данные, наконец, больше не используются для рендеринга.Но имейте в виду, что you не создает другого буфера, он просто продолжается под капотом драйвера.Поэтому, когда вы хотите обновить весь буфер, вы можете либо сделать

updateData(dataArray);
glBufferData(GL_ARRAY_BUFFER, size, dataArray, usage);

, если вы все равно получили данные в свой собственный массив ЦП, либо

glBufferData(GL_ARRAY_BUFFER, size, NULL, usage);
dataArray = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
updateData(dataArray);
glUnmapBuffer(GL_ARRAY_BUFFER);

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

...