Biblioteka tempa klatek Część pakietu Android Game Development Kit.
Biblioteka Android Frame Pacing, zwana też Replacepy, jest częścią bibliotek AGDK. Dzięki niemu gry z obsługą OpenGL i Vulkan zapewniają płynne renderowanie i poprawną częstotliwość klatek na Androidzie. Dokument definiuje tempo wyświetlania klatek, opisuje sytuacje, w których taka szybkość jest potrzebna, i pokazuje, jak biblioteka radzi sobie z takimi sytuacjami. Jeśli chcesz od razu przejść do implementacji tempa klatek w grze, przeczytaj Następny krok.
Tło
Tempo wyświetlania klatek to synchronizacja logiki gry i pętli renderowania z podsystemem wyświetlania systemu operacyjnego i podstawowym sprzętem wyświetlacza. Podsystem ekranu z Androidem został zaprojektowany w taki sposób, aby unikać artefaktów wizualnych (nazywanych rozerwaniem), które mogą się pojawiać, gdy sprzęt wyświetlacza przełącza się w trakcie aktualizacji na nową ramkę. Aby uniknąć tych artefaktów, podsystem wyświetlania wykonywał te czynności:
- Wewnętrzne buforowanie wcześniejszych ramek
- Wykrywanie późnych klatek.
- Powtarza wyświetlanie poprzednich klatek po wykryciu opóźnionych klatek.
Gra informuje SurfaceFlinger, kompozytor w podsystemie wyświetlania, że przesłał wszystkie wywołania rysowania wymagane dla ramki (wywoływanie eglSwapBuffers
lub vkQueuePresentKHR
). SurfaceFlinger za pomocą zatrzasku sygnalizuje dostępność klatki na wyświetlaczu. Ekran będzie wtedy wyświetlać daną klatkę. Ekran działa ze stałą częstotliwością, np. 60 Hz, a jeśli nie ma nowej klatki, gdy jest potrzebna, sprzęt ponownie wyświetla poprzednią klatkę.
Niespójne liczby klatek często występują, gdy pętla renderowania gry jest renderowana z inną szybkością niż sprzęt natywnego wyświetlacza. Jeśli gra z prędkością 30 FPS próbuje renderować się na urządzeniu natywnie obsługującym 60 FPS, pętla renderowania nie zauważy, że na ekranie pozostają 16 milisekund ponownie. Zwykle powoduje to znaczną niespójność w czasie renderowania klatki, np.: 49 milisekund, 16 milisekund, 33 milisekundy. Zbyt złożone sceny jeszcze bardziej komplikują ten problem, ponieważ powodują również niedostatki.
Nieoptymalne rozwiązania
Poniższe rozwiązania do regulacji tempa klatek były w przeszłości stosowane w grach i zwykle powodują niespójności w czasie renderowania klatek i zwiększają opóźnienie sygnału wejściowego.
Przesyłaj klatki tak szybko, na ile pozwala to interfejs API renderowania
To podejście wiąże grę ze zmienną aktywnością SurfaceFlinger i wprowadza dodatkowy przedział czasu oczekiwania. Potok wyświetlania zawiera kolejkę ramek (zazwyczaj o rozmiarze 2), która wypełnia się, jeśli gra próbuje wyświetlać klatki zbyt szybko. Jeśli w kolejce nie ma już miejsca, pętla gry (lub przynajmniej wątek renderowania) jest blokowana przez wywołanie OpenGL lub Vulkan. Gra musi wtedy czekać, aż sprzęt wyświetlacza pokaże klatkę, co spowoduje synchronizację tych dwóch elementów. Nazywamy to upychaniem bufora lub upychaniem kolejki. Proces renderowania nie zdaje sobie sprawy, co się dzieje, więc niespójności w liczbie klatek narastają. Jeśli próbkowanie gry będzie miało miejsce przed ramką, opóźnienie sygnału wejściowego się zwiększy.
Korzystanie z choreografa Androida samodzielnie
Gry korzystają też z choreografa Androida do synchronizacji. Ten komponent, dostępny w języku Java od interfejsu API 16 i w C++ z interfejsu API 24, dostarcza zwykłe taktyki z tą samą częstotliwością co podsystem wyświetlania. Są też pewne subtelne informacje dotyczące tego, kiedy jest on dostarczany w porównaniu z rzeczywistym systemem VSYNC. Te przesunięcia różnią się w zależności od urządzenia. W przypadku długich klatek może nadal występować buforowanie.
Zalety biblioteki Frame Pacing
Biblioteka tempa klatek korzysta z choreografa Androida do synchronizacji i zawiera umowy z różnymi zakresami czasu dostawy. Używa sygnatur czasowych prezentacji, aby zagwarantować, że ramki są wyświetlane w odpowiednim czasie, i synchronizacji granic, co pozwala uniknąć upychania bufora. Biblioteka korzysta z choreografa NDK, jeśli jest dostępny, lub z choreografa Java, jeśli nie jest.
Biblioteka obsługuje wiele częstotliwości odświeżania, jeśli urządzenie je obsługuje, co daje grze większą elastyczność wyświetlania klatki. Na przykład w przypadku urządzenia obsługującego częstotliwość odświeżania 60 Hz i 90 Hz gra, która nie generuje 60 klatek na sekundę, może zmniejszyć się do 45 FPS zamiast 30 FPS, aby zapewnić jej płynność. Biblioteka wykrywa oczekiwaną liczbę klatek w grze i automatycznie dostosowuje czas wyświetlania klatek. Biblioteka Frame Pacing zwiększa też żywotność baterii, bo pozwala uniknąć niepotrzebnych aktualizacji wyświetlacza. Jeśli np. gra renderuje się z szybkością 60 FPS, ale wyświetlacz aktualizuje się z częstotliwością 120 Hz, ekran jest aktualizowany dwukrotnie dla każdej klatki. Biblioteka tempa klatek pozwala temu zapobiec, ustawiając częstotliwość odświeżania na wartość obsługiwaną przez urządzenie, która jest najbliższa docelowej liczby klatek.
Jak to działa
W kolejnych sekcjach opisujemy, jak biblioteka tempa klatek obsługuje długie i krótkie klatki gry w celu uzyskania poprawnego tempa.
Poprawna częstotliwość klatek przy 30 Hz
Idealna sytuacja w przypadku renderowania z częstotliwością 30 Hz na urządzeniu z częstotliwością 60 Hz została przedstawiona na ilustracji 1. SurfaceFlinger blokuje nowe bufory grafiki, jeśli są dostępne (oznaczenie NB na schemacie oznacza, że nie ma bufora, a poprzedni się powtarza).
Rysunek 1. Idealna częstotliwość klatek przy 30 Hz na urządzeniu 60 Hz
Krótkie klatki w grze powodują zacinanie się
Na większości nowoczesnych urządzeń silniki gier polegają na choreografie platformy, który dostarcza taktyki zwiększające przesyłanie klatek. Mimo to mimo to może się zdarzyć, że tempo będzie zbyt słabe z powodu krótkich klatek, jak widać na ilustracji 2. Odtwarzacz odtwarza krótkie klatki, po których występują długie klatki, czyli zacinanie się.
Rysunek 2. Krótka ramka C w grze sprawia, że w ramce B wyświetla się tylko 1 klatka, a po niej kilka klatek C
Biblioteka tempa klatek rozwiązuje ten problem, wykorzystując sygnatury czasowe prezentacji. Biblioteka korzysta z rozszerzeń sygnatury czasowej prezentacji EGL_ANDROID_presentation_time
i VK_GOOGLE_display_timing
, dzięki czemu klatki nie są wyświetlane wcześnie, jak widać na ilustracji 3.
Rysunek 3. Ramka gry B została wyświetlona dwukrotnie dla płynniejszego wyświetlania.
Długie klatki powodują zacinanie się i opóźnienia
Gdy zadanie wyświetlania trwa dłużej niż zadanie aplikacji, do kolejki są dodawane dodatkowe ramki. Ponownie prowadzi to do zacinania się, a także do dodatkowego opóźnienia z powodu buforowania bufora (zobacz ilustrację 4). Biblioteka eliminuje zacinanie się i dodatkowy przedział czasu oczekiwania.
Rysunek 4. Długa klatka B powoduje nieprawidłowe tempo wyświetlania 2 klatek – A i B
Biblioteka rozwiązuje ten problem, wykorzystując granice synchronizacji (EGL_KHR_fence_sync
i VkFence
) do wstrzykiwania w aplikacji oczekiwania, co pozwoli potokowi wyświetlania nadrobić zaległości, zamiast narastania napięcia wstecznego. Ramka A nadal zawiera dodatkową
ramkę, ale ramka B wyświetla się teraz prawidłowo, jak widać na ilustracji 5.
Rysunek 5. Klatki C i D czekają na wyświetlenie
Obsługiwane tryby działania
Biblioteka tempa klatek można skonfigurować tak, aby działała w jednym z 3 tych trybów:
- Wyłączono tryb automatyczny + potok
- Tryb automatyczny włączony + potok
- Tryb automatyczny włączony + automatyczny tryb potoku (w trybie potoku/bez potoku)
Tryb zalecany
Możesz poeksperymentować z trybami automatycznymi i trybami potoku, ale najpierw je wyłącz, a po zainicjowaniu programu Replacepy uwzględnij poniższe elementy:
swappyAutoSwapInterval(false);
swappyAutoPipelineMode(false);
swappyEnableStats(false);
swappySwapIntervalNS(1000000000L/yourPreferredFrameRateInHz);
Tryb potoku
Do koordynowania zadań silnika biblioteka używa zwykle modelu potoku, który rozdziela zadania związane z procesorem i procesorem GPU na granicy VSYNC.
Rysunek 6. Tryb potoku
Tryb inny niż potok
Ogólnie takie podejście powoduje mniejsze i bardziej przewidywalne opóźnienia na ekranie wprowadzania. W przypadkach, gdy gra ma bardzo krótki czas renderowania klatek, zadania procesora i GPU mogą się mieścić w jednym przedziale wymiany. W tym przypadku podejście bez potoków w rzeczywistości spowodowałoby mniejsze opóźnienie na ekranie wejścia.
Rysunek 7. Tryb inny niż potok
Tryb automatyczny
Większość gier nie wie, jak wybrać interwał zmiany, czyli czas, przez jaki każda klatka jest prezentowana (np.33,3 ms przy 30 Hz). Na niektórych urządzeniach gra może renderować się z szybkością 60 FPS, a na innych może obniżyć tę wartość. Tryb automatyczny mierzy czasy procesora i GPU, aby:
- Automatycznie wybieraj interwały zmiany: gry, które w niektórych scenach wyświetlają częstotliwość 30 Hz, a w innych 60 Hz, umożliwiają bibliotece dynamiczne dostosowywanie tego interwału.
- Dezaktywuj potok w przypadku bardzo szybkich klatek: zapewnia optymalne opóźnienie ekranu wejściowego we wszystkich przypadkach.
Wiele częstotliwości odświeżania
Urządzenia, które obsługują wiele częstotliwości odświeżania, dają większą elastyczność w wybieraniu płynnego interwału wymiany:
- Na urządzeniach 60 Hz: 60 FPS / 30 FPS / 20 FPS
- Na urządzeniach 60 Hz + 90 Hz: 90 FPS / 60 FPS / 45 FPS / 30 FPS
- Na urządzeniach 60 Hz + 90 Hz + 120 Hz: 120 FPS / 90 FPS / 60 FPS / 45 FPS / 40 FPS / 30 FPS
Biblioteka wybiera częstotliwość odświeżania, która najlepiej pasuje do rzeczywistego czasu renderowania klatek z gry, co zapewnia lepsze wrażenia wizualne.
Więcej informacji o tempie wyświetlania klatek z wieloma częstotliwościami odświeżania znajdziesz na blogu w poście na temat renderowania z dużą częstotliwością odświeżania na urządzeniach z Androidem.
Statystyki klatek
Biblioteka Frame Pacing udostępnia następujące statystyki do debugowania i profilowania:
- Histogram z liczbą odświeżeń ekranu, w której ramka czekała w kolejce kompozytora po zakończeniu renderowania.
- Histogram liczby odświeżeń ekranu, który upłynął między żądanym czasem prezentacji a rzeczywistą godziną bieżącą.
- Histogram liczby odświeżeń ekranu między 2 kolejnymi klatkami.
- Histogram liczby odświeżeń ekranu, które upłynęły między rozpoczęciem pracy procesora a rzeczywistą godziną bieżącą.
Następny krok
Zapoznaj się z poniższymi przewodnikami, by zintegrować bibliotekę Android Frame Pacing z grą:
- Zintegruj funkcję Android Frame Pacing z mechanizmem renderowania OpenGL
- Zintegruj Android Frame Pacing z mechanizmem renderowania Vulkan