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

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

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

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

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

Мой код - типичный игровой цикл:

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

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

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

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

Изображение взято из книги "Расширенное графическое программирование с использованием OpenGL".

Ответ 4

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

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

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

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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

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

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

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

Ответ 7

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

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

Я не пробовал это, но скоро.

Я попробовал это на старом приложении, которое имеет довольно даже процессорное и графическое использование. (Первоначально он использовался finish.)

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

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

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

Ответ 8

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

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