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


105

У мене виникають проблеми з розрізненням практичної різниці між дзвінками glFlush()та glFinish().

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

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

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

Мій код - типовий цикл гри:

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

Відповіді:


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робити, і не можу сказати, що це зображення. Що зліва та справа? Також це зображення було випущене в Public Domain або за якоюсь ліцензією, яка дозволяє розміщувати його в Інтернеті?
Нікол Болас

Я мав би трохи допрацювати. Щодо ліцензії: я насправді не зовсім впевнений. Я натрапив на PDF в Google, роблячи дослідження.
Тара

7

Якщо ви не бачили різниці в продуктивності, це означає, що ви робите щось не так. Як згадували деякі інші, вам також не потрібно дзвонити, але якщо ви дійдете на дзвінок glFinish, ви автоматично втрачаєте паралелізм, який може досягати GPU та CPU. Дозвольте зануритися глибше:

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

Отже, якщо ви викликаєте glFinish, ви, по суті, змушуєте драйвер натискати команди на графічний процесор (щоб він до цього часу працював і ніколи не просив GPU працювати), і зупиняти процесор до тих пір, поки команди не будуть натиснуті повністю страчений. Тож протягом усього часу, коли працює графічний процесор, ЦП не робить його (принаймні на цій темі). І весь час, коли ЦП виконує свою роботу (здебільшого партії команд), GPU нічого не робить. Так що так, glFinish повинен зашкодити вашій роботі. (Це приблизне значення, оскільки драйвери можуть почати працювати з графічним процесором над деякими командами, якщо багато з них уже було пакетно. Хоча це не типово, оскільки буфери команд, як правило, досить великі, щоб вмістити досить багато команд).

Тепер, чому б ви взагалі називали glFinish? Єдиний раз, коли я використовував це, коли у мене були помилки драйверів. Дійсно, якщо одна з команд, які ви надсилаєте на апаратне забезпечення, руйнує GPU, то найпростішим варіантом визначення того, яка команда є винуватцем, є виклик glFinish після кожного Draw. Таким чином, ви можете звузити, що саме викликає збій

Як зауваження, такі API, як Direct3D, взагалі не підтримують концепцію Finish.


5
На тему "Тепер, чому б ви взагалі називали glFinish". Якщо ви завантажуєте ресурси (наприклад: текстури) на один потік і використовуєте їх для візуалізації на іншому, спільним доступом через wglShareLists або similars, вам потрібно зателефонувати glFinish, перш ніж сигналізувати потоку візуалізації, що він може візуалізувати те, що було асинхронно завантажено.
Marco Mp

Як я вже говорив нижче, це схоже на усунення помилок водія. Я не можу знайти жодної специфічної згадки про цю вимогу. У Windows драйвер повинен зняти замок, на mac ви повинні зробити CGLLockContext. Але використання glFinish здається дуже неправильним. Щоб знати, коли текстура є дійсною, вам потрібно зробити власний замок або подію в будь-якому випадку.
starmole

1
Документи opencl opengl interop рекомендують glFinish для синхронізації "До виклику clEnqueueAcquireGLObjects програма повинна забезпечити, що будь-які очікувані операції GL, які отримують доступ до об'єктів, визначених у mem_objects, завершені. Це може бути здійснено портативно шляхом видачі та очікування завершення команди glFinish для всіх. Контексти GL з відкладеними посиланнями на ці об’єкти "
Марк Ессел

1
вам не потрібно "викликати glFinish", ви можете використовувати об'єкти синхронізації.
chrisvarnz

Просто додати, оскільки початкова відповідь: Деякі реалізації GL почали використовувати флеш / фініш для синхронізації між спільними контекстами в різних потоках: 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.


4
Неправильно. Як і в коментарі, який я залишив у відповіді вище: Якщо ви завантажуєте ресурси (наприклад: текстури) на один потік і використовуєте їх для візуалізації на іншому, спільним доступом через wglShareLists або подібні програми, вам потрібно зателефонувати glFinish перед тим, як подати сигнал до потоку візуалізації що вільно візуалізувати те, що було асинхронно завантажено. І це не така опція, як ви сказали.
Marco Mp

Дійсно? Чи можете ви створити резервну копію необхідності викликати glFinish перед використанням спільного об'єкта за допомогою якогось специфічного посилання? Я цілком можу побачити помилкового драйвера, якому це потрібно. І це повертається до того, що я сказав. Люди використовують glFinish, щоб обійти баггі-драйвери. Через це драйвери, які працюють належним чином, ігнорують його на продуктивність. Оригінальний випадок використання специфікації профілювання (і одноразового буферування) більше не працює.
starmole

2
Особистий досвід роботи з пошкодженими текстурами, плюс це: higherorderfun.com/blog/2011/05/26/… Якщо ви задумаєтесь про це, це має сенс, оскільки це не дуже відрізняється від того, мати мутекси чи інші синхронізації. api між потоками - перед тим, як контекст може використовувати спільний ресурс, інший повинен завершити завантаження цього ресурсу, таким чином, потрібно переконатися, що буфер випорожнений. Я думаю, що більше кутових специфікацій, а не помилка, але я не маю доказів для цього твердження :)
Marco Mp

Дивовижна відповідь, дякую. Особливо "багато старих програм безпричинно посипали glFlush та glFinish у всьому своєму коді як вуду".
акауппі

Чи справді немає опс? Чи не є glFinish та / або glFlush дуже цінними для обробки зображень високої інтенсивності чи математичної сумісної обробки на основі GPU? Наприклад, ми не читали результати обчислень на GPU від пиксельного буфера в CPU , поки після виклику glFinish , щоб гарантувати , що всі пікселі обчислення було написано. Ми щось пропускаємо?
Праксителі

5

Погляньте тут . Коротше кажучи:

glFinish () має той самий ефект, що і glFlush (), при цьому додаток, що glFinish () буде блокувати, поки не будуть виконані всі подані команди.

Інша стаття описує інші відмінності:

  • Функції "swap" (використовуються в додатках із подвійним буфером) автоматично відмикають команди, тому не потрібно дзвонити glFlush
  • glFinish змушує OpenGL виконувати видатні команди, що погана ідея (наприклад, з VSync)

Підсумовуючи це, це означає, що вам навіть не потрібні ці функції при використанні подвійного буферизації, за винятком випадків, коли ваша реалізація swap-буферів не автоматично промиває команди.


Так, але в документі сказано, що обидва мають однаковий ефект, маючи лише декілька відмінностей, наприклад, щодо віконної системи. Але все одно я відредагував свою відповідь;)
AndiDog

Приємний виклик на нюанс. Він уточнює, чи потрібно викликати glFlush (), а потім glFinish () - (питання, яке ми мали після того, як прочитати все вище)
Praxiteles

4

Здається, не існує способу запиту про стан буфера. Є це розширення Apple, яке могло б служити тій самій цілі, але воно не здається кросплатформенним (не пробували його.) На ньому швидкий погляд, здається, перед тим, як flushви натиснете команду паркан у; Ви можете запитувати статус цього забору під час переміщення через буфер.

Цікаво, чи можете ви використати flushдо буферизації команд, але перед початком надання наступного кадру, який ви викликаєте finish. Це дозволить вам почати обробку наступного кадру, як працює графічний процесор, але якщо це не буде зроблено до моменту повернення, finishблокується, щоб переконатися, що все у свіжому стані.

Я цього не пробував, але незабаром.

Я спробував це на старому додатку, який має навіть рівне використання процесора та GPU. (Спочатку використовується finish.)

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

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

Так що я б сказав , використовувати flushі finish, тому що , коли буфер порожній при виклику finish, немає ніякого зниження продуктивності. І я здогадуюсь, якщо буфер був заповнений, ви хочете finishвсе-таки захотіти .


0

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.