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();
}
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 programuglFinish()
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.źródło
Zawsze byłem zdezorientowany co do tych dwóch poleceń, ale ten obraz wyjaśnił mi wszystko: 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 coglFlush()
, 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”.
źródło
glFlush
iglFinish
robię, 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?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.
źródło
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.
źródło
Zajrzyj tutaj . Krótko mówiąc, mówi:
W innym artykule opisano inne różnice:
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ń.
źródło
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
flush
wł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ć
flush
przed buforowaniem poleceń, ale przed rozpoczęciem renderowania następnej wywoływanej ramkifinish
. 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żywany
finish
.)Kiedy zmieniłem to na
flush
na koniec ifinish
na 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
finished
z początku kadru, wychodzącflush
, i to samo.Więc powiedziałbym, że użyj
flush
ifinish
, ponieważ gdy bufor jest pusty na wywołaniefinish
, nie ma trafienia w wydajność. I zgaduję, że gdyby bufor był pełny, i tak powinieneś tego chciećfinish
.źródło
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.
źródło