OpenGL ES 2.0: самая эффективная настройка для VBO с GL_STREAM_DRAW? - PullRequest
6 голосов
/ 05 сентября 2011

Я использую объект буфера вершин (VBO) в OpenGL ES 2.0.

У меня есть набор данных вершин, которые постоянно хранятся в обычном ОЗУ. Причина в том, что вычисление позиций вершин с нуля является дорогостоящим, но дельта может быть добавлена ​​к последней позиции, чтобы дешево ее обновить.

Фактическое количество отображаемых вершин быстро меняется со временем. В одном кадре у меня может быть 1000, а в следующем 2500. Следуя совету, полученному здесь ранее, я теперь задаю целое число UPPER в качестве верхней границы для числа вершин, которые когда-либо будут нарисованы. Я malloc мои массивы данных вершин и индексов только один раз при запуске на основе этого значения.

Я передаю подсказку GL_STREAM_DRAW на каждый вызов glBindBuffer, чтобы указать, что данные меняются в каждом кадре.

Пытаясь быть максимально эффективным, я создал следующую настройку:

// SETUP: Called only once.
glBindBuffer(GL_ARRAY_BUFFER,...);
glBufferData(GL_ARRAY_BUFFER,...); // Pass vertex data for UPPER vertices.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,...);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,...); // Pass index values 0 - (UPPER-1).
glEnableVertexAttribArray(...); // Setup vertex attributes.
glVertexAttribPointer(...);
glUseProgram(...); // Use program with custom shaders.
glUniformMatrix4fv(...); // Identify shader uniforms.

// UPDATE: Called when vertex data changes (on each frame).
glBindBuffer(GL_ARRAY_BUFFER,...);
glBufferSubData(GL_ARRAY_BUFFER,...); // Update VBO data.

// RENDER: Called on each frame.
glDrawElements(GL_TRIANGLES, numberOfVerticesThisFrame, ...); // Number of vertices and indices to be used is inherently specified in here.

Однако это нарушается с EXC_BAD_ACCESS на glDrawElements, и я знаю, что это из-за моего порядка команд gl.

У меня ранее была похожая настройка, которая работала:

// UPDATE: Called when vertex data changes (on each frame).
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,...);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,...); // Pass index values 0 - (actual number of vertices to draw - 1)

// RENDER: Called on each frame.
glBindBuffer(GL_ARRAY_BUFFER,...);
glBufferData(GL_ARRAY_BUFFER,...); // Pass vertex data for actual number of vertices (not UPPER).
glEnableVertexAttribArray(...); // Setup vertex attributes.
glVertexAttribPointer(...);
glUseProgram(...); // Use program with custom shaders.
glUniformMatrix4fv(...); // Identify shader uniforms.
glDrawElements(GL_TRIANGLES, numberOfVerticesThisFrame, ...);

Однако эта настройка требует гораздо больше работы для каждого кадра, и, как вы можете видеть, включает в себя изменение размеров VBO (так как он использует фактический размер, а не UPPER), что, как мне сказали, является большой потерей производительности. 1025 *

Может, кто-нибудь объяснит мне какие-либо очевидные проблемы с моей новой установкой и, что наиболее важно, какие команды мне приходится вызывать каждый кадр до glDrawElements? Мое предположение, что я могу подготовить все возможные индексы заранее и затем передать фактическое число вершин в glDrawElements, явно неверно.

1 Ответ

9 голосов
/ 05 сентября 2011

Чтобы ответить на вопрос, который вы задали в заголовке вопроса, не существует «наиболее эффективной» настройки объекта буфера для потоковой передачи данных вершин.Особенно не в ES 2.0, который охватывает широкий диапазон различного оборудования, у каждого из которых есть свои особенности.

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

Например, glUseProgram заставляет данный программный объект стать программным объектом, который при любых последующих вызовах glDraw* будет использовать , пока вы не вызовете glUseProgram снова. Думайте о большинстве функций OpenGL как о глобальном состоянии, потому что именно так оно и работает.glUseProgram устанавливает глобальную переменную, которую glDraw* читает, чтобы узнать, какой шейдер использовать.

Поэтому, если вы хотите убедиться, что конкретный вызов отрисовки использует определенный шейдер, вы должны glUseProgramтот шейдер сразу же заранее.Или, по крайней мере, в последнее время, что вы знаете, что вы не изменили его где-то еще.Как правило, рендеринг для объекта выглядит следующим образом:

  • Установите атрибуты вершины для рендеринга объекта.
  • Установите текущую программу и измените любые формы для каждого объекта (матрицы и т. Д.).).
  • Привязка текстур для программы, если есть.
  • Привязка другого состояния для программы, если таковые имеются.
  • Визуализация.
  • Деактивация атрибутов

На первом шаге используются glEnableVertexAttribArray, glBindBuffer и glVertexAttribPointer.Эти функции, как и glUseProgram устанавливают глобальное состояние.Вы должны использовать glDisableVertexAttribArray после рендеринга с объектом, и вы должны отменить привязку любых буферов, которые вы, возможно, использовали.

...