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

105

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

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

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

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

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

while (running) {
    process_stuff();
    render_stuff();
}
jay.lee
источник

Ответы:

93

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

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

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

Мальте Класен
источник
1
Что вы имеете в виду, что glFlush «должен завершиться в КОНЕЧНОЕ время», а затем позже сказать, что glFlush не будет подавляться, потому что «это может занять ЛЮБОЕ время, необходимое для обработки ваших команд»? (Колпачки мои) Разве это не исключают друг друга?
Пракситель
1
Если он заканчивается в какой-то момент, он соответствует критериям КОНЕЧНОГО времени. Некоторые драйверы вообще не обращаются к этому вызову.
chrisvarnz
SwapBuffers зависит от контекста и требуется только для очистки контекста вызывающего потока. При рендеринге нескольких контекстов каждый должен быть сброшен вручную, так как это не гарантируется при перестановке буферов через другой контекст.
Андреас
22

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

Энди Росс
источник
18

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

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

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

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

Тара
источник
Я действительно знаю, что glFlushи glFinishделаю, и я не могу сказать, что говорит этот образ. Что слева, а что справа? Кроме того, было ли это изображение выпущено в общественное достояние или по какой-либо лицензии, позволяющей размещать его в Интернете?
Никол Болас
Я должен был немного уточнить. По поводу лицензии: на самом деле я не совсем уверен. Я наткнулся на PDF-файл в Google, когда занимался исследованием.
Тара
7

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

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

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

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

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

Bahbar
источник
5
На тему «А зачем тогда вообще вызывать glFinish?». Если вы загружаете ресурсы (например, текстуры) в один поток и используете их для рендеринга в другом, с совместным использованием через wglShareLists или аналогичные, вам необходимо вызвать glFinish, прежде чем сигнализировать потоку рендеринга, что он свободен для рендеринга того, что было асинхронно загружено.
Marco Mp
Как я сказал ниже, это похоже на обходное решение ошибки драйвера. Я не могу найти никаких упоминаний об этом требовании. В Windows драйвер должен использовать блокировку, в Mac вы должны использовать CGLLockContext. Но использование glFinish кажется очень неправильным. Чтобы узнать, когда текстура действительна, вы в любом случае должны сделать свою собственную блокировку или событие.
starmole
1
opencl opengl interop docs рекомендует glFinish для синхронизации "Перед вызовом clEnqueueAcquireGLObjects приложение должно убедиться, что все ожидающие операции GL, которые обращаются к объектам, указанным в mem_objects, завершены. Это может быть выполнено переносимо путем выдачи и ожидания завершения команды glFinish на всех Контексты GL с ожидающими рассмотрения ссылками на эти объекты »
Марк Эссель
1
вам не «нужно» вызывать glFinish, вы можете использовать объекты синхронизации.
chrisvarnz
Просто чтобы добавить, поскольку исходный ответ: некоторые реализации GL начали использовать flush / finish для синхронизации между общими контекстами в разных потоках: developer.apple.com/library/ios/documentation/3DDrawing/… - В первую очередь Apple IOS. Я думаю, что это принципиально очередное злоупотребление API. Но это оговорка к моему первоначальному ответу: в некоторых реализациях требуется завершение / очистка. Но как и почему определяется реализация, а не спецификация OpenGL.
starmole
7

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

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

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

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

Starmole
источник
4
Неправильно. Как и в комментарии, который я оставил в ответе выше: если вы загружаете ресурсы (например, текстуры) в один поток и используете их для рендеринга в другом, с совместным использованием через wglShareLists или аналогичные, вам необходимо вызвать glFinish перед передачей сигнала потоку рендеринга что он может отображать то, что было асинхронно загружено. И это не запрет, как вы сказали.
Marco Mp
В самом деле? Можете ли вы подтвердить необходимость вызова glFinish перед использованием общего объекта с какой-либо ссылкой на спецификацию? Я полностью вижу неисправный драйвер, которому это нужно. И это возвращает нас к тому, что я сказал. Люди используют glFinish для устранения ошибок в драйверах. Из-за этого драйверы, которые работают правильно, игнорируют это для производительности. Исходный вариант использования профилирования (и рендеринга с одной буферизацией) больше не работает.
starmole
2
Личный опыт работы с поврежденными текстурами, плюс это: upperorderfun.com/blog/2011/05/26/… Однако, если подумать, это имеет смысл, поскольку не сильно отличается от мьютексов или другой синхронизации. api между потоками - прежде чем контекст сможет использовать разделяемый ресурс, другой должен завершить загрузку этого ресурса, поэтому необходимо убедиться, что буфер был опустошен. Я думаю, что это скорее краеугольный камень спецификаций, а не ошибка, но у меня нет доказательств этому утверждению :)
Marco Mp
Отличный ответ, спасибо. В частности, «многие старые программы без всяких на то оснований вуду использовали glFlush и glFinish в своем коде».
akauppi
Неужели это не опера? Разве glFinish и / или glFlush не очень ценны для высокоинтенсивной обработки изображений или математической сопроцессорной обработки на базе графического процессора? Например, мы не читали результаты вычислений на GPU от пиксельного буфера в CPU , пока после вызова glFinish , чтобы гарантировать , что все пиксели вычисление было написано. Мы что-то упускаем?
Пракситель
5

Взгляните сюда . Короче говоря:

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

В другой статье описаны другие отличия:

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

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

AndiDog
источник
Да, верно, но в документе говорится, что оба имеют одинаковый эффект с небольшими различиями, например, в оконной системе. Но в любом случае я отредактировал свой ответ;)
AndiDog
Приятно отметить нюанс. Он уточняет, нужно ли вызывать glFlush () и ЗАТЕМ glFinish () - (вопрос, который у нас возник после прочтения всего вышесказанного.)
Пракситель
4

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

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

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

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

Когда я изменил его на " flushв конце" и " finishв начале", проблем не возникло. (Все выглядело нормально!) Скорость отклика программы увеличилась, вероятно, из-за того, что ЦП не остановился в ожидании работы ГП. Определенно лучший метод.

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

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

GManNickG
источник
0

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

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

анон
источник