Должен ли я вызывать glEnable и glDisable каждый раз, когда я что-то рисую?

StackOverflow https://stackoverflow.com/questions/802079

  •  03-07-2019
  •  | 
  •  

Вопрос

Как часто я должен вызывать функции OpenGL, такие как glEnable() или glEnableClientState() и их соответствующие glDisable аналоги?Предназначены ли они для вызова один раз в начале приложения, или я должен оставить их отключенными и включать только те функции, которые мне немедленно нужны для рисования чего-либо?Есть ли разница в производительности?

Это было полезно?

Решение

"Это зависит от обстоятельств".

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

Большинству реальных приложений требуется смешивание, и тогда вы вынуждены вызывать glEnable() чтобы включить какое-то конкретное состояние (состояния), выполните вызовы draw, затем glDisable() повторите их снова, когда закончите "очищать сцену".

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

Другие советы

Если вы обнаружите, что часто проверяете значение переменных состояния и впоследствии вызываете glEnable / glDisable, возможно, вам удастся немного навести порядок, используя стек атрибутов (glPushAttrib / glPopAttrib).

Стек атрибутов позволяет вам изолировать области вашего кода таким образом, чтобы изменения атрибута в одних разделах не влияли на состояние атрибута в других разделах.

void drawObject1(){
  glPushAttrib(GL_ENABLE_BIT);

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_LIGHTING);    

  /* Isolated Region 1 */

  glPopAttrib();
}        

void drawObject2(){
  glPushAttrib(GL_ENABLE_BIT);

  glEnable(GL_FOG);
  glEnable(GL_GL_POINT_SMOOTH);    

   /* Isolated Region 2 */

  glPopAttrib();
}    

void drawScene(){
  drawObject1();
  drawObject2();
}

Хотя GL_LIGHTING и GL_DEPTH_TEST заданы в drawObject1, их состояние не сохраняется в drawObject2.В отсутствие glPushAttrib этого не было бы.Также - обратите внимание, что нет необходимости вызывать glDisable в конце вызовов функции, glPopAttrib выполняет эту работу.

Что касается производительности, накладные расходы, связанные с вызовами отдельных функций glEnable / glDisable, минимальны.Если вам нужно обрабатывать большое количество состояний, вам, вероятно, потребуется создать свой собственный менеджер состояний или выполнить множество вызовов glGetInteger...а затем действуйте соответственно.Добавленный механизм и поток управления могли бы сделать код менее прозрачным, более сложным для отладки и более трудным в обслуживании.Эти проблемы могут затруднить другие, более плодотворные оптимизации.

Стек атрибуции может помочь в поддержании уровней абстракции и создании областей изоляции.

Справочная страница glPushAttrib

Прежде всего, какую версию OpenGL вы используете?И какое поколение графического оборудования есть у вашей целевой группы?Зная это, было бы легче дать более правильный ответ.Мой ответ предполагает OpenGL 2.1.

OpenGL - это конечный автомат, означающий, что всякий раз, когда состояние изменяется, это состояние становится "текущим" до тех пор, пока программист не изменит его снова явно с помощью нового вызова OpenGL API.Существуют исключения из этого правила, например, вызовы массива состояний клиента, делающие текущий цвет вершины неопределенным.Но это те исключения, которые определяют правило.

"один раз в начале приложения" это не имеет особого смысла, потому что иногда вам нужно уничтожить ваш контекст OpenGL, пока приложение все еще запущено.Я предполагаю, что вы имеете в виду сразу после создания каждого окна.Это работает для состояния, которое вам не нужно менять позже.Пример:Если все ваши вызовы draw используют одни и те же данные массива вершин, вам не нужно впоследствии отключать их с помощью glDisableClientState .

Существует множество состояний включения / выключения, связанных со старым конвейером с фиксированной функцией.Легким искуплением этого является:Используйте шейдеры!Если вы ориентируетесь на поколение карт возрастом не более пяти лет, оно, вероятно, в любом случае имитирует конвейер с фиксированной функцией с шейдерами.Используя шейдеры, вы более или менее полностью контролируете то, что происходит на этапах преобразования и растеризации, и вы можете создавать свои собственные "состояния" с помощью униформ, которые очень дешево менять / обновлять.

Знание того, что OpenGL - это конечный автомат, как я сказал выше, должно прояснить, что следует стремиться свести изменения состояния к минимуму, насколько это возможно.Однако, скорее всего, есть и другие вещи, которые влияют на производительность гораздо больше, чем включение / выключение вызовов состояний.Если вы хотите узнать о них, читайте дальше.


За счет государства нет связанные со старыми вызовами состояний фиксированных функций, которые не являются простым включением / отключением состояний, могут сильно отличаться по стоимости.Примечательно, что связывание шейдеров и имен привязок ("имен" текстур, программ, объектов буфера) обычно довольно дорого обходится.Вот почему многие игры и приложения раньше сортировали порядок прорисовки своих сеток в соответствии с текстурой.Таким образом, им не нужно было дважды связывать одну и ту же текстуру.Однако в настоящее время то же самое относится и к шейдерным программам.Вы же не хотите дважды привязывать одну и ту же шейдерную программу, если в этом нет необходимости.Кроме того, не все функции конкретной версии OpenGL аппаратно ускорены на всех картах, даже если производители этих карт утверждают, что они совместимы с OpenGL.Соответствие требованиям означает, что они следуют спецификации, а не то, что они обязательно эффективно выполняют все функции.В связи с этим следует помнить о некоторых функциях, таких как glHistogram и glMinMax из GL__ ARB __imaging.


Заключение:Если нет очевидной причины не делать этого, используйте шейдеры!Это избавляет вас от множества ненужных вызовов состояния, так как вместо этого вы можете использовать униформу.Вы знаете, шейдеры OpenGL существуют всего около шести лет.Кроме того, изменяются накладные расходы на включение / выключение состояния может это может быть проблемой, но обычно гораздо больше можно выиграть от оптимизации других, более дорогостоящих изменений состояния, таких как glUseProgram, glCompileShader, glLinkProgram, glBindBuffer и glBindTexture.

P.S:OpenGL 3.0 удалил включение / выключение вызовов состояния клиента.Они неявно включены, поскольку рисование массивов - единственный способ рисования в этой версии.Немедленный режим был удален.вызовы gl ..Pointer также были удалены, поскольку на самом деле просто нужен glVertexAttribPointer.

Эмпирическое правило, которому меня научили, гласило, что почти всегда дешевле просто включать / отключать по желанию, а не проверять текущее состояние и изменять только при необходимости.

Тем не менее, ответ Марка - это то, что определенно должно сработать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top