opengl: glFlush () a glFinish ()

105

Mam problem z odróżnieniem praktycznej różnicy między dzwonieniem glFlush()a glFinish().

Docs powiedzieć, że glFlush()i glFinish()pchnie wszystkie buforowane operacje OpenGL, dzięki czemu można mieć pewność, że wszystko będzie wykonane, z tą różnicą, że glFlush()wraca natychmiast, gdzie jako glFinish()bloki, aż wszystkie operacje są kompletne.

Po przeczytaniu definicji doszedłem do wniosku, że gdybym użył glFlush()tego, prawdopodobnie napotkałbym problem przesyłania większej liczby operacji do OpenGL, niż jest w stanie wykonać. Tak więc, żeby spróbować, zamieniłem mój glFinish()na a glFlush()i lo i oto mój program działał (o ile mogłem powiedzieć), dokładnie to samo; liczba klatek na sekundę, wykorzystanie zasobów, wszystko było takie samo.

Zastanawiam się więc, czy istnieje duża różnica między tymi dwoma wywołaniami, czy też mój kod sprawia, że ​​działają tak samo. Lub gdzie jeden powinien być używany w porównaniu z drugim. Pomyślałem również, że OpenGL będzie miał wywołanie, glIsDone()aby sprawdzić, czy wszystkie buforowane polecenia dla a glFlush()są kompletne, czy nie (więc nie można wysyłać operacji do OpenGL szybciej, niż można je wykonać), ale nie mogłem znaleźć takiej funkcji .

Mój kod to typowa pętla gry:

while (running) {
    process_stuff();
    render_stuff();
}
jay.lee
źródło

Odpowiedzi:

93

Pamiętaj, że te polecenia istnieją od wczesnych dni OpenGL. glFlush zapewnia, że ​​poprzednie polecenia OpenGL muszą zakończyć się w określonym czasie ( specyfikacje OpenGL 2.1 , strona 245). Jeśli rysujesz bezpośrednio do przedniego bufora, powinno to zapewnić, że sterowniki OpenGL zaczną rysować bez zbytniego opóźnienia. Możesz pomyśleć o złożonej scenie, która pojawia się obiekt po obiekcie na ekranie, gdy wywołujesz funkcję glFlush po każdym obiekcie. Jednak podczas korzystania z podwójnego buforowania glFlush nie ma praktycznie żadnego efektu, ponieważ zmiany nie będą widoczne, dopóki nie zamienisz buforów.

Funkcja glFinish nie wraca, dopóki wszystkie efekty wcześniej wydanych poleceń [...] nie zostaną w pełni zrealizowane . Oznacza to, że wykonanie twojego programu czeka tutaj, aż każdy piksel zostanie narysowany, a OpenGL nie ma już nic więcej do roboty. Jeśli renderujesz bezpośrednio do bufora przedniego, wywołanie glFinish jest wykonywane przed użyciem wywołań systemu operacyjnego do robienia zrzutów ekranu. Jest znacznie mniej przydatny do podwójnego buforowania, ponieważ nie widzisz zmian, które wymusiłeś do wykonania.

Więc jeśli używasz podwójnego buforowania, prawdopodobnie nie będziesz potrzebować ani glFlush, ani glFinish. SwapBuffers niejawnie kieruje wywołania OpenGL do odpowiedniego bufora, nie ma potrzeby wcześniejszego wywoływania glFlush . I nie przejmuj się akcentowaniem sterownika OpenGL: glFlush nie dusi się zbyt wieloma poleceniami. Nie ma gwarancji, że to wywołanie powróci natychmiast (cokolwiek to znaczy), więc przetworzenie poleceń może zająć dowolny czas.

Malte Clasen
źródło
1
Co masz na myśli, mówiąc, że glFlush „musi zakończyć się w Skończonym czasie”, a później powiedzieć, że glFlush nie dusi się, ponieważ „przetworzenie Twoich poleceń może zająć KAŻDY czas”? (Czapki moje) Czy to nie wyklucza się wzajemnie?
Praxiteles
1
Tak długo, jak zakończy się w pewnym momencie, spełnia kryteria MAKSYMALNEGO czasu. Niektórzy kierowcy całkowicie nie reagują na tę rozmowę.
chrisvarnz
SwapBuffers jest specyficzny dla kontekstu i wystarczy opróżnić kontekst wątku wywołującego. Jeśli renderujesz wiele kontekstów, każdy z nich powinien zostać opróżniony ręcznie, ponieważ nie jest to gwarantowane podczas zamiany buforów przez inny kontekst.
Andreas,
22

Jak wskazywały inne odpowiedzi, tak naprawdę nie ma dobrej odpowiedzi zgodnie ze specyfikacją. Ogólnym zamiarem glFlush()jest to, że po wywołaniu go, procesor hosta nie będzie miał do wykonania żadnej pracy związanej z OpenGL - polecenia zostaną przesłane do sprzętu graficznego. Ogólnym zamiarem programu glFinish()jest to, że po jego powrocie nie pozostaje żadna praca, a wyniki powinny być również dostępne dla wszystkich odpowiednich interfejsów API innych niż OpenGL (np. Odczyty z bufora ramki, zrzuty ekranu itp.). To, czy tak się naprawdę dzieje, zależy od kierowcy. Specyfikacja pozwala na dużą swobodę co do tego, co jest legalne.

Andy Ross
źródło
18

Zawsze byłem zdezorientowany co do tych dwóch poleceń, ale ten obraz wyjaśnił mi wszystko: Kolor vs wykończenie Najwyraźniej niektóre sterowniki GPU nie wysyłają wydanych poleceń do sprzętu, chyba że została zgromadzona określona liczba poleceń. W tym przykładzie ta liczba to 5 .
Obraz przedstawia różne polecenia OpenGL (A, B, C, D, E ...), które zostały wydane. Jak widać na górze, polecenia nie są jeszcze wydawane, ponieważ kolejka nie jest jeszcze pełna.

W środku widzimy, jak glFlush()wpływa na polecenia w kolejce. Mówi sterownikowi, aby wysłał wszystkie ustawione w kolejce polecenia do sprzętu (nawet jeśli kolejka nie jest jeszcze pełna). To nie blokuje wątku wywołującego. Po prostu sygnalizuje kierowcy, że możemy nie wysyłać żadnych dodatkowych poleceń. Dlatego czekanie na zapełnienie kolejki byłoby stratą czasu.

Na dole widzimy przykład użycia glFinish() . Robi prawie to samo co glFlush(), z wyjątkiem tego, że powoduje, że wątek wywołujący czeka, aż wszystkie polecenia zostaną przetworzone przez sprzęt.

Zdjęcie pochodzi z książki „Advanced Graphics Programming Using OpenGL”.

Tara
źródło
Właściwie wiem, co glFlushi glFinishrobię, i nie mogę powiedzieć, co mówi ten obraz. Co jest po lewej i prawej stronie? Czy ten obraz został opublikowany w domenie publicznej lub na jakiejś licencji, która pozwala na umieszczenie go w Internecie?
Nicol Bolas,
Powinienem był trochę rozwinąć. O licencji: właściwie nie jestem do końca pewien. Natknąłem się na plik PDF w Google, robiąc badania.
Tara,
7

Jeśli nie zauważyłeś żadnej różnicy w wydajności, oznacza to, że robisz coś źle. Jak niektórzy wspominali, nie musisz też wywoływać, ale jeśli wywołasz glFinish, automatycznie tracisz równoległość, którą mogą osiągnąć GPU i procesor. Pozwól mi zanurkować głębiej:

W praktyce cała praca, którą przekazujesz kierowcy, jest grupowana i wysyłana do sprzętu potencjalnie później (np. W czasie SwapBuffer).

Tak więc, jeśli wywołujesz glFinish, zasadniczo zmuszasz sterownik do wysyłania poleceń do GPU (który do tego czasu był wsadowany i nigdy nie prosił GPU o pracę) i zatrzymaj procesor, dopóki wypchnięte polecenia nie zostaną całkowicie wykonany. Więc przez cały czas działa GPU, procesor nie (przynajmniej w tym wątku). I przez cały czas, gdy procesor wykonuje swoją pracę (głównie polecenia wsadowe), GPU nic nie robi. Więc tak, glFinish powinno zaszkodzić twojemu występowi. (Jest to przybliżenie, ponieważ sterowniki mogą zacząć pracować na GPU nad niektórymi poleceniami, jeśli wiele z nich było już wsadowanych. Nie jest to jednak typowe, ponieważ bufory poleceń są zwykle wystarczająco duże, aby pomieścić całkiem dużo poleceń).

Dlaczego więc w ogóle miałbyś nazywać glFinish? Użyłem go tylko wtedy, gdy miałem błędy sterownika. Rzeczywiście, jeśli jedno z poleceń wysyłanych do sprzętu powoduje awarię GPU, wtedy najprostszą opcją określenia, które polecenie jest winowajcą, jest wywołanie funkcji glFinish po każdym losowaniu. W ten sposób możesz zawęzić, co dokładnie powoduje awarię

Na marginesie, interfejsy API, takie jak Direct3D, w ogóle nie obsługują koncepcji Finish.

Bahbar
źródło
5
Na "Teraz, dlaczego w ogóle miałbyś nazywać glFinish". Jeśli ładujesz zasoby (np .: tekstury) w jednym wątku i używasz ich do renderowania w innym, z udostępnianiem przez wglShareLists lub podobne, musisz wywołać funkcję glFinish przed zasygnalizowaniem wątkowi renderującemu, że może renderować to, co zostało załadowane asynchronicznie.
Marco Mp
Jak powiedziałem poniżej, wygląda to na obejście błędu sterownika. Nie mogę znaleźć żadnej specyfikacji dotyczącej tego wymagania. W systemie Windows sterownik musi mieć blokadę, na Macu musisz zrobić CGLLockContext. Ale używanie glFinish wydaje się bardzo złe. Aby wiedzieć, kiedy tekstura jest prawidłowa, w każdym przypadku musisz wykonać własną blokadę lub zdarzenie.
starmole
1
opencl opengl interop docs zaleca glFinish do synchronizacji "Przed wywołaniem clEnqueueAcquireGLObjects aplikacja musi upewnić się, że wszystkie oczekujące operacje GL, które uzyskują dostęp do obiektów określonych w mem_objects, zostały zakończone. Można to zrobić przenośnie, wydając i czekając na zakończenie polecenia glFinish na wszystkich Konteksty GL z oczekującymi odniesieniami do tych obiektów ”
Mark Essel
1
nie "musisz" wywoływać funkcji glFinish, możesz używać obiektów synchronizacji.
chrisvarnz
Wystarczy dodać, że od czasu oryginalnej odpowiedzi: niektóre implementacje GL zaczęły używać flush / finish do synchronizacji między współdzielonymi kontekstami w różnych wątkach: developer.apple.com/library/ios/documentation/3DDrawing/ ... - Przede wszystkim Apple IOS. Myślę, że jest to zasadniczo kolejne niewłaściwe użycie API. Ale jest to zastrzeżenie do mojej pierwotnej odpowiedzi: w niektórych implementacjach wymagane jest wykończenie / wyrównanie. Ale jak i dlaczego jest definiowana implementacja, a nie specyfikacja OpenGL.
starmole
7

glFlush tak naprawdę sięga modelu klienta-serwera. Wysyłasz wszystkie polecenia gl przez potok do serwera gl. Ta rura może buforować. Tak jak każdy plik lub sieć może buforować we / wy. glFlush mówi tylko „wyślij bufor teraz, nawet jeśli nie jest jeszcze pełny!”. W systemie lokalnym prawie nigdy nie jest to potrzebne, ponieważ lokalny interfejs API OpenGL raczej nie buforuje się i po prostu wydaje polecenia bezpośrednio. Również wszystkie polecenia, które powodują rzeczywiste renderowanie, wykonają niejawny flush.

Z drugiej strony glFinish został stworzony do pomiaru wydajności. Rodzaj PINGa do serwera GL. Odwraca polecenie i czeka, aż serwer odpowie „Jestem bezczynny”.

Współcześni, lokalni kierowcy mają jednak dość kreatywne pomysły, co to znaczy być bezczynnym. Czy to „wszystkie piksele są rysowane” czy „moja kolejka poleceń ma miejsce”? Również dlatego, że wiele starych programów bez powodu używało glFlush i glFinish w swoim kodzie, ponieważ kodowanie voodoo, wiele nowoczesnych sterowników ignoruje je jako „optymalizację”. Naprawdę nie mogę ich za to winić.

Podsumowując: traktuj glFinish i glFlush jako brak operacji w praktyce, chyba że kodujesz dla starożytnego zdalnego serwera SGI OpenGL.

starmole
źródło
4
Źle. Jak w komentarzu, który zostawiłem w odpowiedzi powyżej: Jeśli ładujesz zasoby (np .: tekstury) w jednym wątku i używasz ich do renderowania w innym, udostępniając przez wglShareLists lub podobne, musisz wywołać glFinish przed sygnalizacją do wątku renderującego że można bezpłatnie renderować to, co zostało załadowane asynchronicznie. I to nie jest nie-op, jak powiedziałeś.
Marco Mp
Naprawdę? Czy możesz poprzeć tę potrzebę wywołania funkcji glFinish przed użyciem udostępnionego obiektu z linkiem do specyfikacji? Całkowicie widzę buggy kierowcę, który tego potrzebuje. I to wraca do tego, co powiedziałem. Ludzie używają glFinish do obejścia błędnych sterowników. Z tego powodu sterowniki, które działają poprawnie, ignorują to dla wydajności. Oryginalny przypadek użycia specyfikacji profilowania (i renderowania z pojedynczym buforowaniem) już nie działa.
starmole
2
Osobiste doświadczenie związane z koniecznością radzenia sobie z uszkodzonymi teksturami, a także to: upperorderfun.com/blog/2011/05/26/... Jeśli się nad tym zastanowić, ma to sens, ponieważ nie różni się to zbytnio od muteksów lub innej synchronizacji. api między wątkami - zanim kontekst będzie mógł użyć współdzielonego zasobu, drugi musi zakończyć ładowanie tego zasobu, stąd potrzeba upewnienia się, że bufor został opróżniony. Myślę, że bardziej przypadek narożny specyfikacji, a raczej błąd, ale nie mam na to żadnego dowodu :)
Marco Mp
Świetna odpowiedź, dzięki. Szczególnie „wiele starych programów bez powodu posypało glFlush i glFinish w swoim kodzie jako voodoo”.
akauppi
Czy to naprawdę nie ma operacji? Czy funkcje glFinish i / lub glFlush nie są bardzo wartościowe w przypadku przetwarzania obrazu o wysokiej intensywności lub współprzetwarzania matematycznego opartego na GPU? Na przykład, nie czytać wyniki obliczeń GPU z bufora piksela do procesora aż po wywołanie glFinish aby wszystkie piksel obliczenia zostały napisane. Czy czegoś brakuje?
Praxiteles
5

Zajrzyj tutaj . Krótko mówiąc, mówi:

glFinish () ma taki sam efekt jak glFlush (), z tą różnicą, że glFinish () będzie blokować, dopóki wszystkie przesłane polecenia nie zostaną wykonane.

W innym artykule opisano inne różnice:

  • Funkcje swap (używane w aplikacjach z podwójnym buforowaniem) automatycznie opróżniają polecenia, więc nie ma potrzeby dzwonienia glFlush
  • glFinish zmusza OpenGL do wykonywania wyjątkowych poleceń, co jest złym pomysłem (np. z VSync)

Podsumowując, oznacza to, że nie potrzebujesz tych funkcji nawet podczas korzystania z podwójnego buforowania, z wyjątkiem sytuacji, gdy Twoja implementacja swap-buffers nie opróżnia automatycznie poleceń.

AndiDog
źródło
Tak, zgadza się, ale dokument mówi, że oba mają ten sam efekt, z kilkoma tylko różnicami dotyczącymi na przykład systemu okiennego. Tak czy inaczej zredagowałem odpowiedź;)
AndiDog
Niezłe odwołanie do niuansów. Wyjaśnia, czy konieczne jest wywołanie funkcji glFlush (), a WTEDY glFinish () - (pytanie, które mieliśmy po przeczytaniu wszystkiego powyżej.)
Praxiteles
4

Wydaje się, że nie ma sposobu na sprawdzenie stanu bufora. Jest to rozszerzenie Apple, które może służyć temu samemu celowi, ale nie wydaje się być wieloplatformowe (nie próbowałem tego). Na pierwszy rzut oka wydaje się, że zanim flushwłożysz polecenie ogrodzenia; możesz następnie zapytać o stan tego ogrodzenia, gdy przechodzi przez bufor.

Zastanawiam się, czy mógłbyś użyć flushprzed buforowaniem poleceń, ale przed rozpoczęciem renderowania następnej wywoływanej ramki finish. Umożliwiłoby to rozpoczęcie przetwarzania następnej klatki podczas pracy GPU, ale jeśli nie zostanie to zrobione przed powrotem,finish zostanie zablokowane, aby upewnić się, że wszystko jest w nowym stanie.

Nie próbowałem tego, ale wkrótce to zrobię.

Wypróbowałem to na starej aplikacji, która ma dość równomierne wykorzystanie procesora i GPU. (Pierwotnie używanyfinish .)

Kiedy zmieniłem to na flush na koniec i finishna początku, nie było żadnych bezpośrednich problemów. (Wszystko wyglądało dobrze!) Szybkość reakcji programu wzrosła, prawdopodobnie dlatego, że procesor nie zatrzymał się, czekając na GPU. Zdecydowanie lepsza metoda.

Dla porównania usunąłem finishedz początku kadru, wychodząc flush, i to samo.

Więc powiedziałbym, że użyj flushi finish, ponieważ gdy bufor jest pusty na wywołanie finish, nie ma trafienia w wydajność. I zgaduję, że gdyby bufor był pełny, i tak powinieneś tego chcieć finish.

GManNickG
źródło
0

Pytanie brzmi: czy chcesz, aby Twój kod działał dalej podczas wykonywania poleceń OpenGL, czy tylko po wykonaniu poleceń OpenGL.

Może to mieć znaczenie w przypadkach, takich jak opóźnienia w sieci, aby niektóre dane wyjściowe konsoli były wyświetlane dopiero po narysowaniu obrazów lub tym podobnych.

zaraz
źródło