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).

Idealna częstotliwość klatek przy 30 Hz na urządzeniu 60 Hz

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ę.

Krótkie ramki gry

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.

sygnatury czasowe prezentacji,

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.

Długie ramki gry

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.

Oczekiwania dodane do warstwy aplikacji

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)

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.

Tryb potoku

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.

Tryb inny niż potok

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ą: