opengl: glFlush () против glFinish()

у меня возникли проблемы с различением практической разницы между вызовом glFlush() и glFinish().

врачи говорят, что glFlush() и glFinish() подтолкнет все буферизованные операции к OpenGL, чтобы можно было быть уверенным, что все они будут выполнены, разница в том, что glFlush() немедленно возвращает, где as glFinish() блоки до всех операций.

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

поэтому мне интересно, есть ли большая разница между двумя вызовами, или мой код заставляет их работать одинаково. Или где одно должно быть использовано против другого. Я также подумал, что OpenGL будет иметь какой-то вызов, как glIsDone() чтобы проверить, все ли буферизованные команды для glFlush() завершены или нет (поэтому операции в OpenGL не отправляются быстрее, чем они могут быть выполнены), но я не смог найти такой функции.

мой код является типичным игровым циклом:

while (running) {
    process_stuff();
    render_stuff();
}

8 ответов


имейте в виду, что эти команды существуют с первых дней OpenGL. glFlush гарантирует, что предыдущие команды OpenGL должен пройти в конечное время (поддержка OpenGL 2.1 спецификации, 245 стр.). Если вы рисуете непосредственно в передний буфер, это должно гарантировать, что драйверы OpenGL начнут рисовать без слишком большой задержки. Вы можете подумать о сложной сцене, которая появляется объект за объектом на экране, когда вы вызываете glFlush после каждого объекта. Однако, при использовании двойной буферизации, glFlush практически не влияет вообще, так как изменения не будут видны, пока вы не поменяете буферы.

glFinish не возвращается до тех пор, пока все эффекты от ранее выданных команд [...] полностью реализованы. Это означает, что выполнение вашей программы ждет здесь, пока каждый последний пиксель не будет нарисован, и OpenGL больше не имеет ничего общего. Если вы визуализируете непосредственно в передний буфер, glFinish-это вызов перед использованием вызовов операционной системы для создания скриншотов. Это гораздо менее полезно для двойной буферизации, потому что вы не видите изменений, которые вы вынуждены завершить.

поэтому, если вы используете двойную буферизацию, вам, вероятно, не понадобится ни glFlush, ни glFinish. SwapBuffers неявно направляет вызовы OpenGL в правильный буфер,нет необходимости сначала вызывать glFlush. И не стесняйтесь подчеркивать драйвер OpenGL: glFlush не будет подавляться слишком многими командами. Не гарантируется, что этот вызов возвращает тут (что бы это ни значило), поэтому для обработки ваших команд может потребоваться любое время.


как намекали другие ответы, действительно нет хорошего ответа в соответствии со спецификацией. Общее намерение glFlush() Это то, что после его вызова у хост-процессора не будет работы, связанной с OpenGL, - команды будут перенесены на графическое оборудование. Общее намерение glFinish() это после того, как он возвращается,нет оставшаяся работа остается, и результаты должны быть доступны также все соответствующие API, отличные от OpenGL (например, чтение из фреймбуфера, скриншотов и т. д...). Действительно ли это происходит, зависит от водителя. Спецификация позволяет тонну широты относительно того, что является законным.


Я всегда был смущен этими двумя командами, но этот образ сделал все это ясным для меня: Flush vs Finish По-видимому, некоторые драйверы GPU не отправляют выданные команды на оборудование, если не накоплено определенное количество команд. В этом примере это число 5.
На изображении показаны различные команды OpenGL (A, B, C, D, E...), которые были выпущены. Как мы видим вверху, команды еще не выданы, потому что очередь еще не заполнена.

В середине мы видим, как glFlush() влияет на команды в очереди. Он говорит водителю отправить все команды в очередь на оборудование (даже если очередь еще не заполнена). Это не блокирует вызывающий поток. Он просто сигнализирует водителю, что мы, возможно, не посылаем никаких дополнительных команд. Поэтому ждать, пока очередь заполнится, было бы пустой тратой времени.

внизу мы видим пример использования glFinish(). Это почти то же самое, что glFlush(), за исключением это заставляет вызывающий поток ждать, пока все команды не будут обработаны оборудованием.

Изображение взято из книги "Advanced Graphics Programming Using OpenGL".


Если вы не видите никакой разницы в производительности, это означает, что вы делаете что-то неправильно. Как уже упоминалось, вам не нужно звонить, но если вы вызовете glFinish, вы автоматически потеряете параллелизм, которого могут достичь GPU и CPU. Позвольте мне нырнуть глубже:

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

Итак, если вы звоните glFinish, вы по существу, заставляя драйвер нажимать команды на GPU (который он до этого паковал и никогда не просил GPU работать), и останавливать CPU, пока команды не будут полностью выполнены. Поэтому в течение всего времени работы GPU CPU не работает (по крайней мере, в этом потоке). И все время, пока CPU делает свою работу (в основном пакетные команды), GPU ничего не делает. Так что да, glFinish должен повредить вашему выступлению. (Это приближение, так как драйверы могут начать работать с GPU некоторые команды, если многие из них уже были упакованы. Это не типично, хотя, поскольку буферы команд, как правило, достаточно велики, чтобы содержать довольно много команд).

теперь, почему вы вообще позвонили glFinish ? Единственный раз, когда я использовал его, когда у меня были ошибки водителя. Действительно, если одна из команд, которые вы отправляете вниз к аппаратным сбоям GPU, то ваш самый простой вариант определить, какая команда является виновником, чтобы вызвать glFinish после каждого розыгрыша. Таким образом, вы можете сузить круг подозреваемых. точно вызывает сбой

в качестве примечания, API, такие как Direct3D, вообще не поддерживают концепцию Finish.


glFlush действительно восходит к модели клиент-сервер. Вы отправляете все команды gl через канал на сервер gl. Эта труба может стать буфером. Так же, как любой файл или сетевой ввод-вывод может буферизировать. glFlush только говорит: "отправьте буфер сейчас, даже если он еще не заполнен!". В локальной системе это почти никогда не требуется, потому что локальный API OpenGL вряд ли буферизует себя и просто выдает команды напрямую. Также все команды, которые вызывают фактический рендеринг, будут выполнять неявный флеш.

glFinish с другой стороны, был сделан для измерения производительности. Своего рода пинг на сервер GL. Он округляет команду и ждет, пока сервер не ответит "Я простаиваю".

В настоящее время современные, местные водители имеют довольно творческие идеи, что значит быть праздным. Это "все пиксели нарисованы"или" моя очередь команд имеет пространство"? Также потому, что многие старые программы посыпали glFlush и glFinish по всему их коду без причины, поскольку кодирование вуду многие современные драйверы просто игнорируют их как "оптимизация." Не могу винить их за это, правда.

Итак, вкратце: рассматривайте glFinish и glFlush как No ops на практике, если вы не кодируете древний удаленный сервер SGI OpenGL.


посмотреть здесь. Короче говоря, он говорит:

glFinish () имеет тот же эффект, что и glFlush (), с добавлением, что glFinish () будет блокировать, пока все представленные команды не будут выполнены.

еще один статьи описывает другие различия:

  • Swap функции (используется в приложениях с двойной буферизацией) автоматически промыть команды, так что нет необходимости вызывать glFlush
  • glFinish сил OpenGL для выполнения выдающихся команд, что является плохой идеей (например, с VSync)

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


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

интересно, если бы вы могли использовать flush до буферизации команд, но до начала рендеринга следующий кадр вы называете finish. Это позволит вам начать обработку следующего кадра по мере работы GPU, но если это не будет сделано к тому времени, когда вы вернетесь,finish будет блокировать, чтобы убедиться, что все в свежем состоянии.

я еще не пробовал, но скоро попробую.

Я пробовал это на старом приложении, которое имеет довольно даже CPU & GPU использовать. (Она изначально finish.)

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

Для сравнения, я убрал finished С начала кадра, оставляя flush, и он выполнил то же самое.

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


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

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