OpenGL Deferred shading. Pętla główna i jej implementacje. Debugowanie i analiza wydajności. Algorytmy grafiki komputerowej czasu rzeczywistego, Informatyka S2
FORWARD W klasycznym podejściu (ang. forward shading) oświetlenie jest obliczane dla każdego fragmentu powstałego przy renderowaniu kolejnych elementów sceny. W ten sposób dla każdego piksela bufora klatki wielokrotnie wykonuje się czasochłonne obliczenia zupełnie na darmo. Oczywiście stosowane są różne optymalizacje zmniejszające ten problem, jednak nie można go całkowicie wyeliminować. Geometria Shading Geometria Shading Geometria Shading Geometria Shading Geometria Shading Framebuffer Wydajność potoku jest więc ściśle zależna od złożoności geometrii, a także od liczby źródeł światła. 2
Podejście z odroczonym cieniowaniem (ang. deferred shading) polega na przebudowie potoku tak, by właściwe cieniowanie odbywało się dopiero po wyrenderowaniu geometrii. Na pierwszym etapie obliczane są tylko podstawowe wartości, które posłużą później do właściwego obliczenia oświetlenia. Najbardziej kosztowne operacje związane z oświetleniem są zatem zawsze wykonywane tyle razy, ile mamy pikseli w buforze klatki (w uproszczeniu), a więc niezależnie od faktycznej złożoności geometrii. Geometria Geometria Geometria G-Buffer Shading Framebuffer Geometria Geometria 3
Technika nie jest nowością. Idea pochodzi już z początku lat 90, ale popularność w grach zyskała dopiero w ciągu ostatnich lat. Saito, Takahashi. Comprehensible rendering of 3D shapes Screen z UE3 prezentuje technikę Deferred Shading ze 123 dynamicznymi światłami (rok 2011) Źródło obrazu: Epic Games, UDK 4
Drugi etap nie musi dotyczyć wyłącznie oświetlenia. Można odroczyć też wiele innych operacji aż do momentu gdy będziemy mieli wyrenderowaną całą źródłową geometrię. Proces dwuetapowy Geometry pass Wejście: geometria sceny Wyjście: G-Buffer (Geometry buffer) Zbiór wartości które przydadzą się do obliczenia oświetlenia Multiple render targets (MRT) Lighting pass (deferred pass) Wejście: G-Buffer Wyjście: gotowa zawartość klatki Geometria Geometry pass G-Buffer Lighting pass Frame buffer 5
G-Buffer Realizuje się go jako FBO posiadający kilka color attachments Zwykle dużo obszerniejszy niż zwyczajny framebuffer przechowujący gotową klatkę Zawartość G-Buffera może być dowolna. Należy zastanowić się które wartości można efektywnie wyliczyć już w pierwszym przebiegu renderowania. Konieczne rozsądne zarządzanie pamięcią Przykładowe składowe: Położenie w przestrzeni świata Zakładając że źródła światła też są w niej określone Wektor normalny Albedo (kolor pochodzący z materiału, tekstury...) Mnożnik specular, shininess Komponent emissive Wektor ruchu (np. dla motion blur) Odległość (np. dla depth of field) Współrzędne tekstury Współczynnik przysłonięcia słońca... 6
Color attachments podłączone do jednego FBO wcale nie muszą mieć wszystkie tego samego formatu. G-Buffer Warto zastanowić się nad efektywnym zaplanowaniem przydziału pamięci Różne formaty tekstur Łączenie ze sobą składowych w obrębie jednej tekstury Przykład Killzone 2: Wymiary tekstur również nie muszą być identyczne, ale wówczas renderowanie będzie się odbywało tylko do wymiarów najmniejszej z nich zwykle nie jest to więc oczekiwany rezultat. Źródło obrazu: Sony, Guerilla Games 7
Scena Geometry pass FBO Pierwszym krokiem jest wyrenderowanie geometrii sceny z użyciem shadera, który zapisze do odpowiednich attachments takie wartości, które później będą niezbędne do obliczenia oświetlenia. Depth/stencil attachment Pozycja G-Buffer Wektory normalne Albedo... Na przykład jest to pozycja w przestrzeni świata, wektor normalny, albedo (kolor). Bufor głębokości również może być częścią G-Buffera jeśli będziemy potrzebować jego zawartości w kolejnym kroku. 8
Scena Geometry pass FBO Poszczególne attachments tworzące G-Buffer są następnie podłączane jako tekstury do kolejnego etapu. Depth/stencil attachment Pozycja G-Buffer Ten etap odbywa się w przestrzeni ekranu, technicznie jest więc etapem post processingu. Shader na tym etapie oblicza oświetlenie dla każdego fragmentu, a więc dla każdego piksela ekranu, na podstawie danych odczytanych z G-Buffera. Wektory normalne Albedo... Full-screen quad Lighting pass Default framebuffer 9
Scena Geometry pass FBO Dodatkowo można dokonać blittingu bufora głębokości otrzymanego w pierwszym przebiegu. Depth/stencil attachment Pozycja Albedo... Full-screen quad (framebuffer blitting) G-Buffer W ten sposób możliwe będzie użycie rezultatu lighting pass z testem głębokości tak, jakby powstał podczas zwyczajnego forward shadingu. Wektory normalne Lighting pass Default framebuffer 10
Scena Geometry pass FBO Bufor klatki można wtedy używać do kolejnych etapów renderowania, także takich z użyciem forward shadingu. G-Buffer Full-screen quad Lighting pass (framebuffer blitting) W ten sposób można dołożyć kolejne obiekty, np. z przezroczystością lub takie które nie biorą udziału w oświetleniu, a test głębokości zadziała poprawnie. Depth/stencil attachment Default framebuffer Dodatkowa geometria Additional forward pass Default framebuffer 11
Pozycja XYZ we współrzędnych świata dla każdego z fragmentów, zapisana jako RGB. Pozycja będzie potrzebna do określenia kierunku padania światła ze źródeł, a także do określenia odległości fragmentu od świateł. Można ją pominąć, wykorzystując głębokość i położenie w screen space. Koniecznie należy tutaj użyć tekstury zmiennoprzecinkowej. Współrzędne rozciągają się przecież poza zakres 0.0-1.0! Można rozważyć przeskalowanie i przesunięcie wartości dla osiągnięcia najlepszej precyzji. 12
XYZ wektorów normalnych we współrzędnych świata. Analogicznie do pozycji, wartości zapisuje się odpowiednio jako składowe R, G i B. Warto zwrócić uwagę że wektory normalne zawsze przyjmują wartości z zakresu -1.0-1.0. Można zatem przeskalować je by zmieściły się w zakresie 0.0-1.0 i użyć tekstury RGB8, jednak wówczas precyzja może być zbyt mała. Wektory normalne mogą pochodzić z normal mappingu. 13
Albedo czyli kolor który później zostanie użyty jako kolor materiału dla komponentu diffuse. W naszym przypadku jest to kolor odczytany z tekstur na podstawie wysokości terenu i jego nachylenia. Można rozważyć odroczenie odczytów z tekstur, do G-Buffera zapisując jedynie współrzędne UV, jednak wówczas nie będzie możliwe wygodne korzystanie z mimappingu oraz filtracji anizotropowej. 14
Obliczenie oświetlenia korzystając ze składowych G-Buffera oraz listy źródeł światła. Dodatkowo na tym etapie została obliczona mgła na podstawie odległości fragmentów od kamery. Rezultat zawiera liniowe wartości wykraczające poza zakres 0.0-1.0, przedstawiony jest tutaj bez tone mappingu. 15
Rezultat po dodaniu skydome a i po zastosowaniu mapowania tonów. Skydome został dodany za pomocą forward passa, korzystając z bufora głębokości powstałego w geometry pass. 16
Istotną kwestią jest dobór formatów tekstur Dane o pozycji wymagają dużej precyzji i formatu zmiennoprzecinkowego RGB16F zwykle nie jest wystarczający! Problem wynika ze zbyt małej precyzji formatu tekstury, gdzie każdy z komponentów XYZ pozycji przechowywany jest za pomocą liczby 16-bitowej. Kłopot pojawia się gdy takie wartości o stosunkowo małej rozdzielczości biorą udział w rozwiązywaniu modelu oświetlenia. Wskazane jest używanie 32 bitów na każdy z komponentów. 17
Deferred shading ma przede wszystkim jeden cel Należy pamiętać, że w niektórych przypadkach forward shading może okazać się szybszy! Deferred shading jest przeznaczony do sytuacji gdy mamy bardzo złożoną geometrię i dużą liczbę dynamicznych źródeł światła. Zredukować zależność szybkości renderowania od liczby źródeł światła i złożoności sceny W klasycznym deferred shadingu, gdzie głównym kosztem jest oświetlenie, czas przygotowania klatki zależy głównie od liczby pikseli na ekranie 1: Geometry pass 2: Lighting pass Cała geometria Tylko full-screen quad Wiele fragmentów Tyle fragmentów ile pikseli Zwykle mało kosztowny Fragment Shader Właściwie, kosztowne oświetlenie we Fragment Shaderze 18
Light culling Źródła światła mają określony zasięg Branie pod uwagę każdego źródła światła dla każdego piksela ekranu jest nieefektywne Niektóre źródła światła celowo dotyczą całego ekranu, np. słońce w środowiskach otwartych. Wówczas jak najbardziej właściwym jest uwzględnianie tego źródła światła przy cieniowaniu każdego piksela. 19
Light culling Renderowanie brył światła powoduje wygenerowanie fragmentów związanych z tą częścią ekranu, na którą światło ma faktyczny wpływ. Źródła światła mają określony zasięg Branie pod uwagę każdego źródła światła dla każdego piksela ekranu jest nieefektywne Light volumes Ograniczenie zasięgu wpływu poszczególnych źródeł światła Oddzielne przebiegi dla źródeł światła Oddzielne żądania renderowania Forward-rendering z geometrią która przybliża kształt światła Inne podejście Full-screen quad oraz stencil buffer dla maskowania Addytywny blending uzyskanych rezultatów = 20
Wpływ świateł jest kolejno dodawany z użyciem addytywnego blendingu. Dzięki zastosowaniu light volumes, każdorazowo modyfikowana jest wyłącznie część klatki. Źródło obrazu: Rockstar Games http://www.adriancourreges.com/ 21
Wpływ świateł jest kolejno dodawany z użyciem addytywnego blendingu. Dzięki zastosowaniu light volumes, każdorazowo modyfikowana jest wyłącznie część klatki. Źródło obrazu: Rockstar Games http://www.adriancourreges.com/ 22
Wpływ świateł jest kolejno dodawany z użyciem addytywnego blendingu. Dzięki zastosowaniu light volumes, każdorazowo modyfikowana jest wyłącznie część klatki. Źródło obrazu: Rockstar Games http://www.adriancourreges.com/ 23
Wpływ świateł jest kolejno dodawany z użyciem addytywnego blendingu. Dzięki zastosowaniu light volumes, każdorazowo modyfikowana jest wyłącznie część klatki. Źródło obrazu: Rockstar Games http://www.adriancourreges.com/ 24
Light culling Źródła światła mają określony zasięg Branie pod uwagę każdego źródła światła dla każdego piksela ekranu jest nieefektywne Light volumes Promień kuli można wyznaczyć z równania tłumienia światła wraz z odległością (attenuation). a (d )= Bryła przypominająca zasięg światła 1 2 k c +k l d +k q d Kule jako aproksymacja zasięgu point lights rozsianych na scenie. Źródło obrazu: http://richdavison.co.uk/ 25
Light culling Źródła światła mają określony zasięg Branie pod uwagę każdego źródła światła dla każdego piksela ekranu jest nieefektywne Light volumes Promień kuli można wyznaczyć z równania tłumienia światła wraz z odległością (attenuation). a (d )= Bryła przypominająca zasięg światła 1 2 k c +k l d +k q d Kule jako aproksymacja zasięgu point lights rozsianych na scenie. Źródło obrazu: http://richdavison.co.uk/ 26
Light culling Źródła światła mają określony zasięg Branie pod uwagę każdego źródła światła dla każdego piksela ekranu jest nieefektywne Light volumes Renderowanie kuli jest raczej niepotrzebnym przerostem formy nad treścią, wprowadza zbyt wiele wierzchołków. Axis-Aligned Bounding Box (AABB) Efektywniejszym rozwiązaniem jest posłużenie się przybliżoną bryłą lub kształtem w przestrzeni ekranu. Należy pamiętać o zasięgu komponentu specular który może być znacznie większy. 27
Light culling Źródła światła mają określony zasięg Branie pod uwagę każdego źródła światła dla każdego piksela ekranu jest nieefektywne Light volumes Axis-Aligned Bounding Box (AABB) Podstawowy problem: dużo zachodzących na siebie obszarów, a więc niepotrzebnie zwielokrotnione odczyty z G-Buffera - dla każdego żądania renderowania, dla każdego fragmentu osobno. 28
Light culling Źródła światła mają określony zasięg Branie pod uwagę każdego źródła światła dla każdego piksela ekranu jest nieefektywne Light volumes Oriented Bounding Box (OBB) Aby lepiej oddać kształt np. światła stożkowego (spotlight) o bardzo wąskim kącie świecenia i nie angażować niepotrzebnie dużo zbędnych pikseli, można posłużyć się OBB. 29
Light culling Duża liczba żądań renderowania też jest istotnym problemem który można próbować zredukować. Źródła światła mają określony zasięg Branie pod uwagę każdego źródła światła dla każdego piksela ekranu jest nieefektywne Light volumes: grupowanie źródeł światła Utworzenie pojedynczego light volume dla kilku znajdujących się w pobliżu źródeł światła często bywa bardziej efektywne (mniej żądań renderowania) 2. Render call (2 światła) 1. Render call (3 światła) 30
Light culling Źródła światła mają określony zasięg Branie pod uwagę każdego źródła światła dla każdego piksela ekranu jest nieefektywne Tiled shading Podział przestrzeni ekranu na kafelki (ang. tiles) Osobna lista świateł dla każdego kafelka Redukuje problem wielokrotnego dostępu do G-Buffera przy renderowaniu light volumes Wizualizacja liczby źródeł światła dla każdego z kafelków. Im jaśniejszy kolor kafelka, tym więcej źródeł światła jest branych pod uwagę. Źródło obrazu: Leif Erkenbrach, http://leifnode.com 31
Technika deferred shading posiada istotne wady Kłopoty z antialiasingiem Rezultat geometry pass można zapisać w buforze z multisamplingiem, ale wywołanie go przed lighting passem spowoduje uzyskanie niepoprawnych wartości! Np. pozycja ulegnie uśrednieniu na granicach obiektów Konieczne jest ręczne odczytywanie wielu próbek z bufora podczas wykonywania lighting pass Istnieją różne adaptacyjne techniki pozwalające zoptymalizować takie podejście, dokonując odczytywania wielu próbek wyłącznie dla krawędzi obiektów Brak obsługi przezroczystości Zawartość G-Buffera dotyczy tylko geometrii która jest najbliższa kamery Przezroczyste obiekty zwykle dodaje się w oddzielnym passie Potrzeba dużej ilości pamięci do przechowania G-Buffera Jeden shader dla wszystkich renderowanych obiektów 32
LIGHTING Rozwinięcie idei deferred shading Nazywane często Light pre-pass Odroczone renderowanie pełnoekranowej light mapy, która później jest używana do oświetlenia geometrii Trzy przebiegi Geometry pass Wejście: Geometria Wyjście: G-Buffer Lighting pass Wejście: G-Buffer Wyjście: Full-screen light map Final pass Wejście: Geometria + Full-screen light map Wyjście: Gotowa klatka 33
LIGHTING Przykładowa pełnoekranowa mapa światła. Źródło obrazu: Sony, Guerilla Games 34
LIGHTING Scena Geometry pass FBO G-Buffer Full-screen quad Lighting pass Komponenty światła można traktować osobno. FBO Diffuse map Specular map 35
LIGHTING Scena Geometry pass FBO G-Buffer Full-screen quad Lighting pass FBO Full-screen light map Podczas ostatniego przebiegu jest dostęp do pełnej informacji o geometrii sceny, możliwy jest więc łatwiejszy antialiasing. Scena Final pass Default framebuffer 36
GAME LOOP Tym co odróżnia sposób działania gry komputerowej od aplikacji użytkowej jest schemat cyklu prowadzenia obliczeń. O ile najczęściej zadaniem aplikacji użytkowej jest generowanie odpowiedzi na żądania ze strony użytkownika, o tyle gra nawet jeśli te żądania nie nadchodzą, dokonuje ciągłej symulacji świata rozgrywki. Podstawą architektury aplikacji grafiki czasu rzeczywistego jest więc nieskończona pętla główna. 37
GAME LOOP Nieskończona pętla Warunkiem stopu jest wystąpienie żądania zakończenia działania programu Postać podstawowej wersji pętli: while (użytkownik nie chce wyjść) { sprawdź urządzenia wejścia; wykonaj zadania AI; przemieść przeciwników; sprawdź kolizje; renderuj świat; odtwórz dźwięki; prześlij dane do serwera;... } 38
GAME LOOP W dalszych rozważaniach przyjmiemy uproszczenie, że podstawowy game loop ma postać: while (użytkownik nie chce wyjść) { aktualizuj stan gry; renderuj świat gry; } Zatem zdefiniujmy następujące określenia: Aktualizacja stanu gry Wszystkie operacje wpływające na stan świata, wynik rozgrywki, pozycję obiektów, obsługa wejścia i zdarzeń,... Renderowanie świata gry Wybór obiektów do renderowania, wyznaczenie ich macierzy przekształceń, zlecenie renderowania karcie graficznej, zamiana buforów,... 39
WYŚWIETLANIE KLATEK Współczesne wyświetlacze komputerowe traktują obraz jako raster Wyświetlacze działają ze stałą częstotliwością odświeżania (dawniej odchylania pionowego) Najczęściej spotykaną częstotliwością odświeżania jest obecnie 60Hz Dla obrazu stereoskopowego, będzie to dwa razy więcej Progresywne wyświetlanie klatki: 1/60s Źródło obrazu: http://www.wikipedia.org 40
WYŚWIETLANIE KLATEK Karta graficzna odpowiada za przygotowanie zawartości klatki, przechowywanie jej i wysłanie do monitora gdy ten będzie gotowy wyświetlić kolejną klatkę Obraz do wyświetlenia przechowywany jest w buforze klatki Proces generowania zawartości bufora klatki i proces przesyłania jej do wyświetlacza są niezależne Program Renderowanie Bufor klatki Wyświetlanie KARTA GRAFICZNA 41
WYŚWIETLANIE KLATEK Komponowanie zawartości bufora klatki nie jest natychmiastowe. W grafice czasu rzeczywistego elementy sceny renderowane są jeden po drugim. Zatem dopóki nie zakończy się renderowanie wszystkich elementów sceny, bufor klatki może zawierać nieukończony obraz. Aby uniknąć migotania obrazu podczas jego przerysowywania gdy występuje konieczność jego akutalizacji, stosuje się technikę podwójnego buforowania (ang. double buffering) Mamy wówczas dwa bufory klatki: Bufor "przedni" (ang. front buffer) Bufor "tylny" (ang. back buffer) Jest wyświetlany Jest aktualizowany Gdy zakończy się aktualizacja back buffera, zamieniane są one miejscami (ang. buffer swap) Użycie podwójnego buforowania rozwiązuje ten problem. Dopóki nowa klatka nie zostanie ukończona, wyświetlana będzie poprzednia klatka. Front Front Back 42
WYŚWIETLANIE KLATEK Komponowanie zawartości bufora klatki nie jest natychmiastowe. W grafice czasu rzeczywistego elementy sceny renderowane są jeden po drugim. Zatem dopóki nie zakończy się renderowanie wszystkich elementów sceny, bufor klatki może zawierać nieukończony obraz. Aby uniknąć migotania obrazu podczas jego przerysowywania gdy występuje konieczność jego akutalizacji, stosuje się technikę podwójnego buforowania (ang. double buffering) Mamy wówczas dwa bufory klatki: Bufor "przedni" (ang. front buffer) Bufor "tylny" (ang. back buffer) Jest wyświetlany Jest aktualizowany Gdy zakończy się aktualizacja back buffera, zamieniane są one miejscami (ang. buffer swap) Użycie podwójnego buforowania rozwiązuje ten problem. Dopóki nowa klatka nie zostanie ukończona, wyświetlana będzie poprzednia klatka. Back Front Front 43
WYŚWIETLANIE KLATEK Dla zachowania wrażenia płynności wyświetlanego ruchomego obrazu, należy upewnić się że kolejne klatki animacji będą docierać do monitora, gdy ten będzie gotowy je wyświetlić W tym celu stosuje się tzw. synchronizację pionową (ang. vertical synchronisation / VSync) Sterownik karty graficznej lub program czeka z zamianą buforów, aż monitor będzie gotowy wyświetlić kolejną klatkę W innym wypadku pomimo sprzętowej możliwości wygenerowania nawet większej liczby klatek na sekundę niż wyświetla monitor, obraz nie będzie płynny Artefakt nazywany tearing, wyświetlany obraz składający się z więcej niż jednej klatki 44
WYŚWIETLANIE czas renderowania jednej klatki KLATEK Dla wyświetlaczy oferujących wyższą częstotliwość odświeżania ekranu niż 60Hz, czas cyklu będzie krótszy niż 1/60s. cykl odświeżenia obrazu na wyświetlaczu VSync Off swap interval = 0 VSync On swap interval = 1 Procesor graficzny czekając na gotowość wyświetlacza nie musi wykonywać operacji, podczas gdy bez użycia synchronizacji od razu zaczynałby renderować kolejną klatkę. max FPS VSync On swap interval = 2 (sleep) (sleep) (sleep) 1/60s (sleep) 60 FPS (sleep) 30 FPS 1/60s czas Użycie synchronizacji pionowej może więc też prowadzić do zmniejszenia zużycia energii. Możliwość ustawienia interwału w GLFW: glfwswapinterval(interval); 45
WYŚWIETLANIE KLATEK Synchronizacja pionowa wyłączona Synchronizacja pionowa włączona Artefakt tego rodzaju nazywa się często ang. słowem tearing. 46
WYŚWIETLANIE KLATEK Technologia NVIDIA G-Sync Synchronizacja wyświetlacza z kartą graficzną czyli podejście odwrotne Wyświetlacz ma adaptacyjną częstotliwość odświeżania, dostosowującą się do aktualnej liczby klatek na sekundę Zakres 30-144 Hz, zależnie od sprzętu Podejście wbrew pozorom nie jest nowe, podobne rozwiązanie Adaptive Sync jest częścią standardu DisplayPort 1.2a zaimplementowane choćby przez AMD w ich FreeSync; różnice: G-Sync duplikuje klatki gdy ich częstotliwość poniżej minimum G-Sync zapobiega nadpisywaniu klatek Źródło obrazu: NVIDIA 47
POMIAR SZYBKOŚCI DZIAŁANIA Cząstkowy pomiar czasu (np. rysowania jednego obiektu) jest niemiarodajny double t0 = glfwgettime(); gldrawarrays(...); double t = glfwgettime() - t0; Wpływ na szybkość renderowania ma bardzo wiele czynników. Brak liniowej zależności czasu renderowania od złożoności sceny: Renderowanie 10.000 wielokątów nie jest dwa razy szybsze niż renderowanie 20.000. kolejkowanie, buforowanie, etc. w karcie graficznej Pomiar czasu wykonania linijki odpowiadającej za żądanie renderowania najczęściej da czas zbliżony do zera Wartościowy jest pomiar czasu pomiędzy kolejnymi momentami zamiany buforów (buffer swap) Sumaryczny czas tworzenia całej klatki aktualizuj stan gry; renderuj świat gry; t0 = glfwgettime(); double t = t0 prevt0; prevt0 = t0; Więcej na ten temat: http://www.opengl.org/wiki/performance http://www.mvps.org/directx/articles/fps_versus_frame_time.htm Niewiele mówi o faktycznym czasie renderowania sceny jeśli jest włączona synchronizacja pionowa 48
POMIAR SZYBKOŚCI DZIAŁANIA Liczba klatek na sekundę (ang. frames per second, FPS) Miara nieliniowa Należy odróżnić czas renderowania klatki od czasu trwania aktualizacji stanu. Użytkownik widzi tylko czas renderowania, ale podczas optymalizacji należy mieć na uwadze faktyczne przyczyny trwania cyklu przygotowania kolejnej klatki animacji. Czas klatki (ang. frame time) Spadek liczby FPS z 900 do 450 jest tym samym, co z 60 do 56.25! Miara liniowa Pozostałe składowe czasu przygotowania klatki Czas aktualizacji stanu Opóźnienie wynikające z działania urządzeń wejścia/wyjścia Więcej na ten temat: http://www.opengl.org/wiki/performance http://www.mvps.org/directx/articles/fps_versus_frame_time.htm 49
POMIAR SZYBKOŚCI DZIAŁANIA Użycie dedykowanych narzędzi Z racji buforowania instrukcji, zwykle okazuje się że najwięcej czasu zajmuje wykonywanie buffer swap. Nie oznacza to, że sama zamiana buforów jest wąskim gardłem! W tym czasie wykonywane są wszystkie dotychczas zbuforowane żądania renderowania. Debugowanie Analiza stanu serwera OpenGL Analiza zawartości pamięci karty graficznej Profiling Znajdowanie wąskich gardeł Czas wykonania funkcji z API OpenGL nie mówi o faktycznym koszcie danych operacji! Dostępne narzędzia: NVIDIA Nsight Tylko GPU NVIDIA AMD CodeXL Dowolne GPU (jeśli chodzi o debugowanie OpenGL) Dawniej gdebugger RenderDoc, GLIntercept, glsldevil, Apitrace,... 50
GAME LOOP IMPLEMENTACJE Za przykład do analizy posłuży nam prosty, obracający się sześcian. Kod przykładu jest uproszczony i celowo korzysta z archaicznego immediate mode. Kody źródłowe dostępne na: http://bazyluk.net/dydaktyka 51
GAME LOOP Najprostsza forma: IMPLEMENTACJE while (użytkownik nie chce wyjść) { aktualizuj stan gry; renderuj świat gry; } Podstawowy game loop Aktualizacja występuje ze stałym krokiem. Absolutnie nie jest to podejście zalecane! Zalety: Prostota (?) Problemy: Szybkość rozgrywki zależna od wydajności sprzętu Szybkość rozgrywki może się zmieniać w trakcie gry 52
GAME LOOP IMPLEMENTACJE Stała szybkość gry Podejście może być korzystne w przypadku gier na urządzenia mobilne. Może bowiem redukować zużycie energii. Określamy, co ile czasu ma wykonać się aktualizacja stanu gry. Każda aktualizacja stanu zawsze oznacza następujące po nim renderowanie obrazu. Zalety: Stałe tempo gry (o ile wystarczający sprzęt) Oszczędność baterii Problemy: Sprzęt nie daje rady osiągnąć tyle FPS => gra zwalnia. Wydajny sprzęt => i tak mamy tyle FPS, co update'ów. 53
GAME LOOP IMPLEMENTACJE Szybkość gry uzależniona od FPS Pozwalamy na renderowanie tak szybko, jak to jest możliwe. Każdemu renderowaniu towarzyszy aktualizacja stanu ze zmiennym krokiem. Podejście na pierwszy rzut oka bardzo dobre, ale ma istotne słabe strony. Zalety: Maksymalna liczba FPS Problemy: Nieprzewidywalność rozgrywki Konieczność aktualizacji zależnej od zmiennego czynnika 54
GAME LOOP IMPLEMENTACJE Stała szybkość gry i maksimum FPS Podejście w podstawowej postaci prowadzi do marnowania mocy obliczeniowej. Rozwinięcie koncepcji stałego kroku aktualizacji Jeśli mamy opóźnienie, to wykonujemy dodatkową aktualizację ze stałym krokiem żeby nadrobić stratę. Zalety: Jeśli mamy jeszcze czas do zaplanowanej, następnej aktualizacji, to renderujemy dodatkową klatkę. Rozwiązuje problem wpływu długiego czasu renderowania na częstotliwość aktualizacji. Eliminuje zbędne oczekiwanie (czasem to minus). Problemy: Jeśli mamy odpowiednio wydajny sprzęt, renderujemy wiele takich samych klatek. 55
GAME LOOP IMPLEMENTACJE Stała szybkość gry i maksimum FPS + interpolacja Aby nie renderować identycznych klatek pomiędzy aktualizacjami, można dokonać interpolacji. Wprowadza opóźnienie o wielkości jednego cyklu. Innym, pokrewnym podejściem jest predykcja. Szczególnie dla wartości kontrolowanych przez gracza może wywołać niepożądane efekty. Podejście zalecane dla jednowątkowej architektury. Zalety: Stały krok aktualizacji, maksimum FPS. Każda klatka jest inna. Problemy: Nieznaczne opóźnienie. 56
GAME LOOP IMPLEMENTACJE Zrównoleglony game loop Rozdzielenie aktualizacji i renderowania na dwa osobne wątki, które mogą być wykonywane równolegle przez np. procesor wielordzeniowy. Konieczne jest korzystanie z interpolacji bądź predykcji. Schemat w przykładzie jest tutaj uproszczony. Wątków może być znacznie więcej. Zalety: Współbieżne renderowanie i aktualizacja, niezależne od siebie. Stały krok aktualizacji i maksymalna liczba FPS. Problemy: Konieczność uważnego planowania architektury silnika. 57
GAME LOOP IMPLEMENTACJE Wielowątkowość w grach możliwości i problemy Nie warto uciekać od wielowątkowości w nowoczesnych silnikach. Współczesny sprzęt daje ogromne możliwości. Należy je wykorzystać. Jeden kontekst OpenGL może w danym momencie być używany tylko przez jeden wątek. Komunikacja CPU -> karta graficzna nie może być traktowana jako możliwa do zrównoleglenia. Można stworzyć kilka kontekstów, które współdzielą dane nie jest to jednak podejście wydajne ani zalecane. Inne możliwości wykorzystania wielowątkowości: Odczyt tekstur i zasobów z dysku odbywający się "w tle". Dysk nie jest urządzeniem pozwalającym na odczyt równoległy (z wyłączeniem macierzy dyskowych)! Wydajna, zrównoleglona aktualizacja stanu: Obliczenia AI podzielone na kilka wątków. Symulacja fizyki. Systemy cząsteczkowe. Odtwarzanie dźwięku. 58
OpenGL Deferred shading. Pętla główna i jej implementacje. Debugowanie i analiza wydajności. Algorytmy grafiki komputerowej czasu rzeczywistego, Informatyka S2