Fortnite

Premiera Fortnite na Androida - artykuł techniczny

6.09.2018
Przez: Twórcy Fortnite

WSTĘP

9 sierpnia wydaliśmy wersję beta Fortnite na Androida, obsługującą wybrane urządzenia od naszych partnerów z firmy Samsung. Kilka dni później zaczęliśmy wysyłać zaproszenia do niektórych posiadaczy urządzeń z Androidem, wyprodukowanych przez konkretne firmy. W tym czasie wiele się nauczyliśmy w kwestii wydajności, zabezpieczeń, kompatybilności urządzeń i dostarczania Fortnite na Androida poprzez program startowy. Za chwilę zagłębimy się w szczegóły techniczne dotyczące premiery, a także opowiemy o naszej aktualnej pracy i planach na najbliższą przyszłość w związku z Androidem.  

STATYSTYKI

W ciągu 21 pierwszych dni po premierze Fortnite na Androida spotkaliśmy się z ogromnym zainteresowaniem: ponad 23 mln graczy wzięło udział w wersji beta, a ponad 15 mln zainstalowało naszą aplikację. Choć wersja na Androida jest jeszcze w fazie zaproszeń, przejście z etapu zaproszenia do gry wygląda podobnie, jak w wersji beta na iOS.
 

SPRZĘT I OPTYMALIZACJA


Wprowadzanie technologii.

Wydawanie tej samej gry na różnych platformach przy jednoczesnym umożliwieniu rozgrywki międzyplatformowej stanowi wyjątkowe wyzwanie. Podczas przystosowywania gry do urządzeń mobilnych zazwyczaj dochodzi do uproszczenia zawartości, a nawet projektu, w celu zachowania wydajności przy ograniczonych parametrach nowej platformy. Na przykład zmniejsza się odległość renderowania, aby zredukować liczbę poleceń renderowania. W Fortnite gracze na Androidzie mogą uczestniczyć w tej samej grze, co ich koledzy na PC i konsolach, więc musimy renderować wszystko, co ma wpływ na rozgrywkę.

Droga do wydania gry na Androida

Od stycznia 2018 r. pracowaliśmy w dużym zespole nad wersją FNBR na Androida. Choć mnóstwo czasu spędziliśmy nad wydajnością renderowania, stabilnością i pamięcią, to najpoważniejsze wyzwanie stanowiła dla nas różnorodność urządzeń z Androidem, wersji systemu i sterowników.

Współpraca z partnerami miała kluczowe znaczenie dla wydania wersji na Androida. Nie poradzilibyśmy sobie bez ich wiedzy, doświadczenia i ciężkiej pracy.

Blisko współpracowaliśmy z Samsungiem przy profilowaniu i optymalizacji Fortnite na ich urządzenia. Samsung przysłał inżynierów do wielu placówek Epica na całym świecie i współpracował bezpośrednio z naszymi inżynierami nad analizą wydajności i optymalizacji. Zawdzięczamy im także wiele zmian w kodzie, szczególnie w rendererze Vulkan. Przy użyciu diagnostycznych smartfonów testowych i firmowych narzędzi pomiarowych byli oni w stanie dać nam wgląd w źródło problemów z wydajnością i pamięcią, i znaleźć sposoby ich rozwiązania. Współpracowaliśmy też z Samsungiem nad stworzeniem jak najpłynniejszego i najbezpieczniejszego procesu instalacji dla użytkowników smartfonów tej firmy.

Również inżynierowie Google odwiedzili nas na miejscu, by pomóc nam w profilowaniu i optymalizacji Fortnite, identyfikacji kluczowych usprawnień oraz przecieków pamięci, a także w stworzeniu sprawnej implementacji równomiernego wyświetlania klatek OpenGL na Androidzie. Inżynierowie Google pracujący nad Androidem są bardzo utalentowani i pełni chęci, by uczynić ze środowiska Android świetne miejsce dla gier i nieustannie je usprawniać.

Ponadto udało nam się nawiązać współpracę z wieloma innymi partnerami w celu testowania i optymalizacji Fortnite. Należą do nich m.in. ARM, Qualcomm, Imagination Technologies, Razer czy HiSilicon.
 

Fragmentacja

Środowisko Androida składa się ze smartfonów produkcji różnych firm. Centrum każdego z nich stanowi tzw. „system na kości” (System-on-Chip, SOC), zawierający konfigurację rdzeni procesora i karty graficznej. Istnieje kilka popularnych rodzin SOC-ów, np. Snapdragon firmy Qualcomm (w 71% aktualnie obsługiwanych urządzeń) zawierający procesor Adreno, a także Exynos firmy Samsung, seria MT firmy MediaTek oraz seria Kirin firmy HiSilicon, które to serie korzystają z procesorów ARM Mali. Każde urządzenie wypuszczane jest na rynek z nieco odmienną wersją systemu operacyjnego Android, a większość producentów dodatkowo dostosowuje funkcje zarządzania procesami i energią. Urządzenia z identycznym procesorem są też wydawane z różnymi wersjami sterowników graficznych. Efekt końcowy jest taki, że dwa urządzenia o identycznej podstawie sprzętowej mogą mieć bardzo odmienne parametry wydajności i być wrażliwe na różne błędy w kodzie.

Miłym zaskoczeniem był dla nas fakt, że najnowsza wersja Androida jest bardziej popularna na urządzeniach z ostatnich 2 lat, niż zakładaliśmy. Choć jesteśmy jeszcze na etapie bety i wyższych wymagań sprzętowych, widzimy już, że 92% użytkowników Fortnite pracuje na Androidzie 8 (Oreo) lub nowszym, ok. 8% na Androidzie 7 (Nougat), a mniej niż 0,5% na wersji wydanej w 2015 r. lub wcześniej. Na sprzęcie mającym co najmniej 2 lata odnotowujemy w tej kwestii poważny spadek. Producenci tych urządzeń wydają specjalnie dostosowane aktualizacje i w większości przypadków muszą skoordynować z operatorami bezprzewodowymi na całym świecie produkcję i wypuszczanie tych aktualizacji.

Do zarządzania tym skomplikowanym układem mamy w silniku Unreal Engine hierarchiczny system profilów urządzeń. Zaczynamy od stworzenia czterech profilów wydajności: niskiej, średniej, wysokiej i epickiej. Profile te zmieniają ustawienia skalowania w silniku gry, co pozwala jej działać na urządzeniach o różnej wydajności. Poziom niski ogranicza do minimum odległość renderowania, a także wyłącza opcjonalne efekty graficzne. Poziom epicki włącza wszystko: cienie, roślinność itp., a także wydłuża odległość renderowania na tyle, na ile można sobie pozwolić przy zachowaniu płynnej pracy na najnowszych urządzeniach.

Do tego dodajemy zestaw profilów karty graficznej, np. Adreno 54x czy Mali G72. Profile te wybierają profil wydajności pasujący do możliwości danego urządzenia, a także pozwalają nam włączyć optymalizacje lub obejścia potrzebne na tym konkretnym sprzęcie. I ostatecznie mamy zestaw profilów dla danego urządzenia, np. Samsung Galaxy Note 9 Adreno lub Google Pixel 2 XL. Ta ostatnia warstwa profilów urządzenia pozwala nam w razie potrzeby włączyć kolejne obejścia lub optymalizacje, tym razem przystosowane do konkretnego urządzenia. Podczas uruchamiania gry sprawdzamy parametry w rodzaju modelu urządzenia, wersji systemu i sterownika graficznego, aby ustalić, który profil urządzenia zastosować.
 

Wydajność renderowania

Dotychczas najpoważniejszą przeszkodę w utrzymaniu płynności gry stanowiło obciążenie procesora podczas renderowania. Zużywa to dużo więcej czasu procesora na Androidzie niż na komputerach PC, konsolach czy iOS-ie. Choć najnowsze urządzenia z Androidem mogą obsłużyć ponad 1500 poleceń renderowania na klatkę, inne obsługują o wiele mniej. Obsługiwane przez nas urządzenia średniej klasy muszą być zdolne do wykonania ok. 600 takich poleceń, a urządzenia niższej klasy ok. 400.

Zredukowaliśmy już liczbę obiektów renderowanych w każdej klatce do do praktycznego minimum przy okazji premiery wersji iOS w zeszłym roku, więc musieliśmy uciec się do innych sposobów. Spróbowaliśmy dynamicznego grupowania poleceń renderowania w polecenia przywołane i choć zapewniło to pewną poprawę wydajności, nie była ona na tyle istotna, by uzasadnić dodatkowe skomplikowanie kodu.
Niektóre optymalizacje w OpenGL przyniosły nam niewielkie korzyści, lecz największy krok naprzód był dla nas niespodzianką i pochodził z jednej z optymalizacji pamięci: emulacji buforów jednolitych. To ścieżka kodu, którą silnik UE4 obsługiwał od lat, używana do urządzeń z OpenGL ES2, które nie obsługują natywnie buforów jednolitych, znanych też jako bufory stałe. Działa to w następujący sposób: w momencie kompilacji shadera identyfikujemy wszystkie stałe potrzebne shaderowi i umieszczamy je w tablicy, którą czyta shader. Zachowujemy również tabelę mapowania, która mówi silnikowi gry, skąd brać stałe dla buforów jednolitych i gdzie je umieszczać w tablicy stałych. W czasie rzeczywistym trzymamy bufory jednolite w pamięci dostępnej dla procesora, korzystamy z tabeli mapowania, by skopiować je do bufora tymczasowego i wysyłamy wszystkie stałe przy użyciu jednego odwołania funkcji glUniform4fv.

Choć nadal jest ona dość zagadkowa, ścieżka kodu emulowanych buforów jednorodnych zaoszczędziła nam sporo czasu w sterowniku. Możliwe jest, że sterownik dokonuje podobnego procesu we własnym zakresie, a nasz sposób lepiej się sprawdza do naszych potrzeb. Możliwe też, że ponieważ tworzymy i przeznaczamy mniej zasobów, sterownik po prostu spędza mniej czasu na zarządzaniu czasem trwania tych buforów. Tak czy inaczej, sposób ten w niektórych przypadkach zmniejszył obciążenie sterownika o 10-15%.
 

Vulkan

Vulkan to najnowsze API graficzne na Androidzie, zaprojektowane pod kątem wydajności i dostępu do sprzętu na niskim poziomie. W 2016 r. we współpracy z Samsungiem stworzyliśmy demo ProtoStar, pokazując, co jest możliwe na Androidzie przy wykorzystaniu wysokowydajnego API. Jako że Fortnite wymaga renderowania mnóstwa obiektów w każdej klatce, Vulkan świetnie się do tego nadaje. Niestety sprawa okazała się bardziej złożona.

Obsługa Vulkana nie jest jeszcze wymogiem na Androidzie, więc nie możemy liczyć na to, że będzie dostępna w całym środowisku tego systemu. Ponieważ celujemy tylko w nowsze urządzenia, nie jest to dla nas poważną przeszkodą, lecz już napotkaliśmy kilka błędów i problemów z wydajnością w starszych wersjach Vulkana, przez co na większości urządzeń biblioteka OpenGL jest od niego szybsza i stabilniejsza. Nie dziwi nas to: w końcu nasza branża miała całą dekadę na optymalizację i zabezpieczenie implementacji OpenGL. Vulkan jest bardziej skomplikowanym API i trochę potrwa, zanim osiągnie podobny poziom dojrzałości.

Wprowadziliśmy jednak w grze obsługę Vulkana na urządzeniach Samsung S9+ (z Adreno) i Note 9 (też z Adreno). Dzięki bliskiej współpracy z inżynierami z Samsunga byliśmy w stanie zoptymalizować obsługę Vulkana przez UE4 do poziomu, na którym jest on średnio o 20% szybszy od OpenGL. W przyszłości nadal zamierzamy współpracować z zaprzyjaźnionymi producentami sprzętu, by zapewnić wydajną obsługę Vulkana na innych urządzeniach, co zwiększy komfort gry w Fortnite na Androida i poprawi wydajność sterowników Vulkana u twórców oprogramowania.
 

Pamięć

Pamięć jest dla nas nieustanną bolączką. Nie dość, że wydajemy pełną grę konsolową na urządzeniach mobilnych, to jeszcze nieustannie wprowadzamy do gry nowe elementy. W związku z tym ciągle szukamy sposobów na optymalizację zużycia pamięci, a Android stanowi wyjątkowe wyzwanie w porównaniu do innych platform.

Nie mamy jednego konkretnego budżetu pamięci, którym możemy gospodarować. Każde urządzenie może mieć inną ilość pamięci, inne aplikacje działające w tle i potencjalnie inne ustawienia decydujące o tym, kiedy usunąć aplikację z powodu nadmiernego zużycia pamięci. Nie ma żadnego API, które określałoby dany budżet pamięci. Przykładowo, pewnego razu rozpoczęliśmy rozgrywkę i zaczęliśmy przyznawać coraz więcej pamięci, by sprawdzić, w którym momencie system zakończy proces. Korzystając z nowo zrestartowanego smartfona Samsung Galaxy S8 (Mali), bez uruchamiania innych aplikacji byliśmy w stanie przydzielić 3 GB pamięci z łącznie dostępnych 4 GB, zanim proces nie został usunięty. Na smartfonie Google Pixel 2 udało nam się przydzielić tylko 1,8 GB pamięci z łącznie dostępnych 3,6 GB.

System, szczególnie sterownik graficzny, pozwala nam przydzielić naprawdę dużo pamięci. Silnik UE4 śledzi pamięć, jakiej żądamy bezpośrednio od systemu, w tym orientacyjną ilość przydzielanej pamięci karty graficznej, lecz nie mamy dobrego wglądu w to, co konkretnie przydziela sterownik. Drogą eksperymentów doszliśmy do tego, ze większość pamięci przydzielanej w Fortnite przez sterownik pochłaniają shadery.

Wspomniana już optymalizacja drogą emulacji buforów jednolitych została wprowadzona z powodu pamięci. OpenGL wymaga od aplikacji możliwości sprawdzenia położenia każdego elementu bufora jednolitego. Przykładowo, można zapytać OpenGL o adres macierzy LocalToWorld w buforze jednolitym Primitive. Aby to obsłużyć, sterownik musi zachować w pamięci pewnego rodzaju metadane, np. tabelę adresów, zachowując nazwę każdego elementu i musi to robić dla każdego programu.

W Fortnite używamy szerokiego wachlarza shaderów. Podczas typowej sesji gry silnik użyje ok. 2000 różnych programów z rodzaju shaderów. W każdym z nich sterownik przechowuje ww. metadane, co razem daje naprawdę sporo pamięci. Korzystając z emulacji buforów jednolitych zaoszczędziliśmy w Fortnite ponad 400 MB pamięci!

Gdy wydawaliśmy Fortnite na Androida, nasze wewnętrzne testy wykazywały, że mieścimy się w granicach pamięci dostępnej na obsługiwanych urządzeniach. Prowadziliśmy próby z włączoną nawigacją map Google, podczas streamowania muzyki i wykazaliśmy, w takich warunkach można bezproblemowo grać w Fortnite. Jednak po premierze okazało się, że u wielu graczy gra kończy pracę lub działa z niską wydajnością właśnie z powodu braku pamięci.

Gdy na smartfonie z Androidem brakuje pamięci, system próbuje zwolnić zasoby, zamykając nieużywane aplikacje. Okazuje się jednak, że sporo co bardziej upartych aplikacji i usług po zamknięciu przez system po prostu uruchamia się ponownie, co jeszcze bardziej pogarsza sytuację. Android zamyka apkę, aby zwolnić pamięć, a ta odpala się na nowo i znowu zużywa tyle pamięci, co poprzednio! Co gorsza, zamykanie i uruchamianie aplikacji obciąża procesor, więc nie dość, że nie zwolniliśmy pamięci, to jeszcze niepotrzebnie zużyliśmy dodatkowe zasoby procesora.

Zmieniliśmy nasze procedury testów, instalując i włączając więcej często spotykanych u naszych użytkowników aplikacji, co pozwoliło nam wyłapywać te problemy we wcześniejszym stadium, lecz i tak musieliśmy szybko zwolnić więcej pamięci. Ustaliliśmy, że problem ogranicza się do użytkowników, grających na ustawieniach wysokich lub epickich na nowych urządzeniach Snapdragon firmy Qualcomm z 4 GB pamięci. I znowu największą różnicę zrobiła tu pamięć shaderów. Prowizoryczne wyłączenie cieni rozwiązało problem poprzez zmniejszenie liczby wymaganych shaderów, gdyż nie potrzebowaliśmy już wariantów do sprawdzania i filtrowania map cieni. Dodaliśmy również do shaderów cache LRU, co pozwala nam to zachowywać w pamięci jedynie shadery potrzebne do renderowania aktualnej sceny. Inne programy shaderów pozostają skompresowane. W miarę potrzeby rozpakowujemy je i z powrotem przekazujemy sterownikowi.
 

Przyszłość – kolejne kroki

Aktualnie skupiamy się na tym, by nasza gra działała dobrze na wszystkich obsługiwanych urządzeniach, oraz na odzyskaniu takiej ilości pamięci, żebyśmy mogli polepszyć jej wygląd i stabilność. Następnie zajmiemy się problemami z kompatybilnością, np. z dźwiękiem (w wersji 5.40 usunęliśmy szereg problemów z dźwiękiem na wielu urządzeniach i powiększyliśmy listę obsługiwanego sprzętu). Zamierzamy również dać użytkownikom większe możliwości dostosowania ustawień swoich urządzeń na własną rękę i decydowania, co jest dla nich ważniejsze – wydajność czy jakość grafiki.

Nadal będziemy usprawniać obsługę Vulkana i z czasem wprowadzimy ją na kolejnych urządzeniach. Oznacza to dalszą pracę nad optymalizacją (zarówno silnika UE4, jak i sterowników) przy udziale producentów sprzętu. Na dłuższą metę inwestycja w obsługę Vulkana i sterowników umożliwi nam zapewnienie lepszej jakości we wszystkich grach na Androidzie korzystających z silnika UE4.

Nadal sprawdzamy możliwości starszych i słabszych smartfonów, lecz cofanie się za bardzo w przeszłość jest niepraktyczne. Każdego roku nowe smartfony są o 50% szybsze od tych z roku poprzedniego. Fortnite działa poprawnie na dwuletnich telefonach, dobrze na rocznych, i znakomicie na smartfonach wypuszczonych w tym roku. Tylko pomyślcie, jak będzie prezentować się na sprzęcie, jaki wyjdzie za rok!

image2.png


Na koniec warto dodać, że wiele z usprawnień kodu, jakich dokonaliśmy w naszym silniku, zostało już wydanych w wersji UE4 4.20. Pozostałe usprawnienia wypuścimy w nadchodzącej wersji 4.21. Będziemy się dzielić usprawnieniami z ogółem twórców korzystających z silnika UE4 także w przyszłości.
 

WALKA ZE ZŁOŚLIWYM OPROGRAMOWANIEM

Jeszcze przed ogłoszeniem wydania Fortnite na Androida zdaliśmy sobie sprawę z powstania nieautoryzowanych stron oferujących „Fortnite na Androida”. Zazwyczaj zawierają złośliwe oprogramowanie lub usiłują dokonać wyłudzenia. Po odkryciu takiej strony podejmujemy kroki w celu jej usunięcia poprzez kontakt z właścicielem portalu, odpowiednim dostawcą hostingu, lub reklamodawcą odpowiedzialnym za banery/filmy tę stronę promujące. Niezależnie od faktycznej dystrybucji złośliwych programów lub jej braku, uznajemy wszelką dystrybucję Fortnite na Androida lub reklamę takowej za nieautoryzowane. Jedynym uprawnionym źródłem Fortnite na Androida jest oficjalny program startowy pobrany bezpośrednio od firmy Epic.
 
Do tej pory firma Epic wszczęła postępowanie przeciwko 47 stronom oferującym „Fortnite na Androida”, z których wiele wygląda na dzieła tych samych sprawców. Nadal nadzorujemy sytuację z zamiarem usuwania tych stron lub ograniczania dostępu do nich poprzez współpracę z siecią partnerów przeciwdziałających oszustwom (m.in. dostawców internetu oraz producentom przeglądarek i programów antywirusowych), którzy mogą zamieścić w przeglądarce alarm podobny do poniższego:

Image1.png
 
Aktywnie szukamy nowo powstałych oszukańczych stron i wyznaczyliśmy do tego specjalny zespół. W celu poprawienia nadzoru wynajęliśmy też zewnętrzną agencję zajmującą się zwalczaniem naruszenia własności intelektualnej i wyłudzeń. Współpraca ta umożliwi nam wykrywanie i monitorowanie nowych domen o podejrzanych adresach, żeby w razie ich przemiany w złośliwe witryny Epic mógł podjąć odpowiednie kroki, w tym postępowanie sądowe.

PODSUMOWANIE

Fortnite na Androida przeciera szlaki dla całej branży jako pierwsza gra komputerowa i konsolowa, która oferuje pełną rozgrywkę międzyplatformową i kompatybilność, a także pierwsza gra z górnej półki rozprowadzana poza sklepem Google Play. Było to ogromne przedsięwzięcie i projekt badawczy, lecz szybkie przyjęcie przez ponad 15 mln użytkowników Androida pokazuje, że to podejście ma sens i może odnieść duży sukces. Co najważniejsze, cały dorobek techniczny z prac na Androidzie zostanie udostępniony wszystkim twórcom w wersji 4.21, żeby wszyscy mogli korzystać z owoców naszej pracy.