Biblioteka do ustalania tempa wyświetlania klatek   Należy do Android Game Development Kit.

Biblioteka Android Frame Pacing, znana też jako Swappy, jest częścią bibliotek AGDK. Pomaga w osiągnięciu płynnego renderowania i prawidłowego tempa wyświetlania klatek w przypadku gier OpenGL i Vulkan na Androidzie. W tym dokumencie definiujemy synchronizację klatek, opisujemy sytuacje, w których jest ona potrzebna, oraz przedstawiamy, jak biblioteka rozwiązuje te problemy. Jeśli chcesz przejść bezpośrednio do wdrażania wyrównywania klatek w swojej grze, przeczytaj Kolejny krok.

Tło

Tempo generowania klatek to synchronizowanie logiki gry i pętli renderowania z podsystemem wyświetlania systemu operacyjnego oraz sprzętem wyświetlacza. Podsystem wyświetlania Androida został zaprojektowany tak, aby uniknąć artefaktów wizualnych (zwanych rozmazywaniem), które mogą wystąpić, gdy sprzęt wyświetlania przełączy się na nową klatkę w trakcie aktualizacji. Aby uniknąć tych artefaktów, system wyświetlania wykonuje te czynności:

  • buforuje poprzednie klatki wewnętrznie;
  • Wykrywanie spóźnionych przesyłek klatek
  • Powtarza wyświetlanie poprzednich klatek, gdy wykryto opóźnione klatki.

Gra informuje SurfaceFlingera, czyli kompozytora w ramach podsystemu wyświetlacza, że przesłał wszystkie wywołania draw potrzebne do utworzenia klatki (wywołując eglSwapBuffers lub vkQueuePresentKHR). SurfaceFlinger sygnalizuje dostępność klatki do sprzętu wyświetlacza za pomocą blokady. Następnie wyświetlacz wyświetla daną klatkę. Sprzęt wyświetlacza działa z ciągłą częstotliwością, np. 60 Hz, a jeśli nie ma nowej klatki, gdy sprzęt tego potrzebuje, wyświetla ponownie poprzednią.

Niezgodne czasy wyświetlania klatek często występują, gdy pętla renderowania gry renderuje z inną częstotliwością niż natywny sprzęt wyświetlania. Jeśli gra działająca z częstotliwością 30 FPS próbuje renderować na urządzeniu, które obsługuje 60 FPS, pętla renderowania gry nie zauważy, że powtórony obraz pozostaje na ekranie przez dodatkowe 16 milisekund. Takie rozłączenie zwykle powoduje znaczną niespójność w czasie trwania klatek, np. 49 ms, 16 ms, 33 ms. Problem ten pogłębiają zbyt skomplikowane sceny, które powodują pominięcie klatek.

Rozwiązania nieoptymalne

W przypadku gier w przeszłości stosowano następujące rozwiązania dotyczące tempa wyświetlania klatek, które zwykle prowadziły do niespójności czasu wyświetlania klatek i zwiększonego opóźnienia sygnału wejściowego.

Przesyłaj klatki tak szybko, jak pozwala interfejs API do renderowania

Takie podejście wiąże grę z zmienną aktywnością SurfaceFlingera i wprowadza dodatkową latencję klatki. System wyświetlania zawiera kolejkę klatek, która zwykle ma rozmiar 2. Zapełnia się ona, gdy gra próbuje wyświetlać klatki zbyt szybko. Ponieważ nie ma już miejsca w kolejce, pętla gry (lub co najmniej wątek renderowania) jest blokowana przez wywołanie OpenGL lub Vulkan. Gra jest wtedy zmuszona do oczekiwania na wyświetlenie obrazu przez sprzęt wyświetlacza. Ten odwrotny nacisk synchronizuje oba komponenty. Ta sytuacja nazywana jest przepełnianiem bufora lub przepełnianiem kolejki. Proces renderowania nie wie, co się dzieje, więc niespójność liczby klatek na sekundę się pogłębia. Jeśli gra pobiera dane wejściowe przed ramką, opóźnienie wejścia się wydłuża.

Korzystanie z Android Choreographer bez innych narzędzi

Gry używają też do synchronizacji Android Choreographer. Ten komponent jest dostępny w języku Java od wersji 16 interfejsu API i w języku C++ od wersji 24 interfejsu API. Wyświetla regularne sygnały o tej samej częstotliwości co system wyświetlania. Nadal istnieją subtelności dotyczące tego, kiedy ten sygnał jest dostarczany w stosunku do rzeczywistego sprzętowego VSYNC, a te przesunięcia różnią się w zależności od urządzenia. Buforowanie może nadal występować w przypadku długich ramek.

Zalety biblioteki Frame Pacing

Biblioteka Frame Pacing korzysta z Android Choreographer do synchronizacji i zajmuje się zmiennością dostarczania sygnałów. Używa sygnatur czasowych wyświetlania, aby zapewnić prawidłowe wyświetlanie klatek, oraz ograniczników synchronizacji, aby uniknąć wypełniania bufora. Biblioteka używa NDK Choreographer, jeśli jest dostępna, a w przeciwnym razie wraca do Java Choreographer.

Biblioteka obsługuje wiele częstotliwości odświeżania, jeśli są one obsługiwane przez urządzenie, co daje większą elastyczność w prezentowaniu klatek. Na przykład w przypadku urządzenia obsługującego częstotliwość odświeżania 60 Hz i 90 Hz, aby zachować płynność, gra, która nie może wyświetlać 60 FPS, może zamiast 30 FPS wyświetlać 45 FPS. Biblioteka wykrywa oczekiwaną liczbę klatek na sekundę w grze i odpowiednio dostosowuje czas wyświetlania klatek. Biblioteka Frame Pacing pozwala też oszczędzać energię, ponieważ zapobiega niepotrzebnym aktualizacjom wyświetlania. Jeśli na przykład gra renderuje się z częstotliwością 60 FPS, a wyświetlacz odświeża się z częstotliwością 120 Hz, ekran jest aktualizowany dwukrotnie na każdą klatkę. Biblioteka Frame Pacing zapobiega temu, 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 następnych sekcjach opisujemy, jak biblioteka Frame Pacing radzi sobie z długimi i krótkimi klatkami w grze, aby zapewnić prawidłowe tempo wyświetlania klatek.

Prawidłowe odświeżanie obrazu z częstotliwością 30 Hz

Podczas renderowania z częstotliwością 30 Hz na urządzeniu z częstotliwością 60 Hz idealna sytuacja na Androidzie jest pokazana na rysunku 1. SurfaceFlinger blokuje nowe bufory graficzne, jeśli są dostępne (na diagramie jest to oznaczone jako „brak bufora”, a poprzedni bufor jest powtarzany).

Idealne tempo wyświetlania klatek na poziomie 30 Hz na urządzeniu z częstotliwością 60 Hz

Rysunek 1. Idealne tempo wyświetlania klatek na poziomie 30 Hz na urządzeniu z częstotliwością 60 Hz.

Krótkie klatki w grze powodują zacinanie

W przypadku większości nowoczesnych urządzeń silniki gier polegają na tym, że platforma dostarcza sygnały do przesyłania klatek. Nadal jednak istnieje możliwość wystąpienia złego tempa wyświetlania klatek z powodu krótkich klatek, jak widać na rysunku 2. Krótkie klatki, po których następują długie klatki, są postrzegane przez odtwarzacz jako zacinanie.

Krótkie klatki z gry

Rysunek 2. Krótka klatka C gry powoduje, że klatka B zawiera tylko jedną klatkę, a następnie następuje kilka klatek C.

Biblioteka Frame Pacing rozwiązuje ten problem, używając sygnatur czasowych prezentacji. Biblioteka używa rozszerzeń sygnatury czasowej prezentacji EGL_ANDROID_presentation_time i VK_GOOGLE_display_timing, aby nie wyświetlać klatek przedwcześnie, jak widać na rysunku 3.

Sygnatury czasowe prezentacji

Rysunek 3. Ramka z gry B wyświetlana dwukrotnie w celu zapewnienia płynności obrazu.

Długie klatki powodują zacinanie i opóźnienia

Gdy wyświetlanie zajmuje więcej czasu niż aplikacja, dodatkowe klatki są dodawane do kolejki. Powoduje to znów zacinanie, a także może prowadzić do dodatkowego opóźnienia obrazu z powodu wypełniania bufora (patrz rys. 4). Biblioteka usuwa zacinanie i dodatkowy klatka opóźnienia.

Długie klatki

Rysunek 4. Długa klatka B powoduje nieprawidłowe tempo dla 2 klatek – A i B

Biblioteka rozwiązuje ten problem, używając ogrodzeń synchronizacji (EGL_KHR_fence_sync i VkFence), aby wstrzyknąć do aplikacji opóźnienia, które pozwalają potoku wyświetlania nadrobić zaległości, zamiast dopuścić do wzrostu ciśnienia wstecznego. Ramka A nadal zawiera dodatkową klatkę, ale ramka B jest już prawidłowa (patrz rysunek 5).

Czas oczekiwania dodany do warstwy aplikacji

Rysunek 5. Ramki C i D czekają na prezentację.

Obsługiwane tryby pracy

Bibliotekę Frame Pacing możesz skonfigurować tak, aby działała w jednym z tych 3 trybów:

  • Tryb automatyczny wyłączony + strumień
  • Tryb automatyczny + Potok
  • Tryb automatyczny włączony + tryb automatycznego potoku (automatyczny/nieautomatyczny)

Możesz eksperymentować z trybem automatycznym i trybami przepływu, ale najpierw musisz je wyłączyć i uwzględnić po zainicjowaniu Swappy:

  swappyAutoSwapInterval(false);
  swappyAutoPipelineMode(false);
  swappyEnableStats(false);
  swappySwapIntervalNS(1000000000L/yourPreferredFrameRateInHz);

Tryb potoku

Aby koordynować zadania silnika, biblioteka zwykle używa modelu tworzenia kolejek, który rozdziela zadania procesora i procesora graficznego na granicach synchronizacji VSYNC.

Tryb potoku

Rysunek 6. Tryb potoku.

Tryb bez rurociągu

Ogólnie rzecz biorąc, takie podejście powoduje mniejsze i bardziej przewidywalne opóźnienia w przypadku ekranu z klawiaturą. W przypadku gier o bardzo krótkim czasie renderowania klatki zarówno obciążenie procesora, jak i karty graficznej może zmieścić się w jednym interwale wymiany. W tym przypadku podejście bez użycia potoku zapewniłoby w rzeczywistości mniejsze opóźnienie ekranu z klawiaturą.

Tryb bez rurociągu

Rysunek 7. Tryb bez potoku.

Tryb automatyczny

Większość gier nie wie, jak wybrać interwał wymiany, czyli czas wyświetlania każdego obrazu (np.33,3 ms dla 30 Hz). Na niektórych urządzeniach gra może działać z prędkością 60 FPS, a na innych może być konieczne zmniejszenie tej wartości. Tryb automatyczny mierzy czas procesora i GPU w celu:

  • Automatyczne wybieranie interwałów przełączania: gry, które w niektórych scenach wyświetlają 30 Hz, a w innych 60 Hz, mogą pozwolić bibliotece na dynamiczne dostosowywanie tego interwału.
  • Dezaktywuj przetwarzanie równoległe w przypadku ultraszybkich klatek: zapewnia optymalne opóźnienie sygnału wejściowego w każdym przypadku.

Wiele częstotliwości odświeżania

Urządzenia obsługujące różne częstotliwości odświeżania zapewniają większą elastyczność w wybieraniu interwału wymiany, który wygląda płynnie:

  • Na urządzeniach z wyświetlaczem 60 Hz: 60 FPS / 30 FPS / 20 FPS
  • Na urządzeniach z częstotliwością 60 Hz i 90 Hz: 90 FPS / 60 FPS / 45 FPS / 30 FPS
  • Na urządzeniach z wyświetlaczami 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 gry, zapewniając lepsze wrażenia wizualne.

Więcej informacji o dostępności odświeżania z różną częstotliwością znajdziesz w poście na blogu Wyświetlanie z wysoką częstotliwością odświeżania na Androidzie.

Statystyki klatek

Biblioteka Frame Pacing udostępnia te statystyki do debugowania i profilowania:

  • Histogram liczby odświeżeń ekranu, w których przypadku klatka czekała w kole kompozytora po zakończeniu renderowania.
  • Histogram liczby odświeżeń ekranu w okresie od żądanego czasu wyświetlania do bieżącego czasu.
  • Histogram liczby odświeżeń ekranu między 2 kolejnymi klatkami.
  • Histogram liczby odświeżeń ekranu, które nastąpiły od rozpoczęcia pracy procesora w przypadku tej klatki do bieżącego czasu.

Następny krok

Aby zintegrować z grą bibliotekę Android Frame Pacing, zapoznaj się z jednym z tych przewodników:

Dodatkowe materiały