Wykorzystanie pamięci jest kluczowe w tworzeniu gier na Androida. Podczas tworzenia gier za pomocą Unreal Engine musisz stale analizować i śledzić stan pamięci, aby spełnić te ograniczenia, zapobiegać wyciekom pamięci i nadmiernemu przydzielaniu pamięci.
Oszczędność pamięci dzięki zmniejszeniu liczby permutacji shaderów
Usuwając nieużywane opcje w sekcji Lighting lub Mobile Shader Permutation
Reduction, możesz zminimalizować liczbę permutacji shaderów. Znacznie zmniejsza to rozmiar pakietu aplikacji, liczbę obiektów stanu potoku (PSO) i zapobiega tworzeniu nieużywanych docelowych tekstur renderowania lub tekstur zastępczych, co prowadzi do zmniejszenia ilości pamięci używanej w czasie działania i lepszej wydajności.
Więcej informacji znajdziesz w sekcji Misc Lighting and Shader Permutation Reduction Settings for Mobile w artykule Performance Guidelines for Mobile Devices.
Optymalizacja wczytywania komponentów
Podczas wczytywania komponentów za pomocą twardych odwołań do pamięci wczytywana jest jednocześnie połączona zawartość, która nie jest nawet używana w grze, co powoduje niepotrzebne zużycie pamięci. W szczególności, gdy dziedziczenie jest nieprawidłowo używane w Blueprints, przydzielane są niepotrzebne CDO (Class Default Objects) z elementów nadrzędnych, co powoduje marnowanie znacznej ilości pamięci. W takich przypadkach zastosuj te techniki, aby zoptymalizować wykorzystanie pamięci.
- Definicja kodu C++ i rozdzielenie danych: zdefiniuj podstawowe funkcje
za pomocą
UCLASS(Abstract)w kodzie C++, oddzielając je od rzeczywistych danych. - Minimalizuj dziedziczenie: konfiguruj dane i wczytuj komponenty tylko w Blueprints z węzłami liści.
- Używaj odwołań pośrednich: stosuj pośrednie odwołania do właściwości za pomocą
TSoftObjectPtraby uniknąć twardych odwołań, i asynchronicznie wczytuj komponenty tylko wtedy, gdy są potrzebne w czasie działania. - Środki ostrożności: gdy używasz asynchronicznego wczytywania komponentów, upewnij się, że podczas wywołania zwrotnego po zakończeniu wczytywania wykonywane są kontrole poprawności
(
IsValid), aby zapobiec powstawaniu wiszących wskaźników lub awarii.
Więcej informacji znajdziesz w artykułach Referencing Assets i Building Mobile Games with UE5.
Dostrajanie inteligentnych wskaźników i odśmiecania pamięci (GC)
Używanie inteligentnych wskaźników (takich jak TSharedPtr i TWeakPtr) podczas tworzenia w C++ zapobiega powstawaniu wiszących wskaźników i wyciekom pamięci. Ponadto używanie TWeakPtr pozwala uniknąć tworzenia silnych odwołań do obiektów, dzięki czemu odśmiecacz pamięci nie zwiększa liczby odwołań podczas zbierania obiektów. Zapobiega to niepotrzebnemu przetrwaniu obiektów i zmniejsza złożoność wykresu odwołań, który musi przejść odśmiecacz pamięci, co znacznie zmniejsza obciążenie odśmiecacza pamięci. Odśmiecanie pamięci można skutecznie zarządzać w środowiskach mobilnych, wywołując ForceGarbageCollection podczas przejść między poziomami lub okresów bezczynności, np. gdy nie ma interakcji z użytkownikiem. Można je też wywołać, gdy gra jest w stanie widocznym lub działa w tle (np. wywołania zwrotne onStop), co zapewnia, że odśmiecanie pamięci jest wykonywane bez wpływu na aktywną rozgrywkę.
Więcej informacji znajdziesz w artykułach Garbage Collection i Smart Pointer.
Optymalizacja za pomocą kompresji tabeli relokacji
Kompresja tabeli relokacji to metoda optymalizacji, która kompresuje rozmiar tabeli relokacji w plikach binarnych ELF, aby zmniejszyć rozmiar pliku binarnego Androida. Gdy minSDKVersion Androida to 23 lub nowsza, obsługiwana jest kompresja tabeli relokacji APS. Gdy minSDKVersion to 28 lub nowsza, dodatkowo obsługiwana jest kompresja tabeli relokacji RELR, co znacznie zmniejsza rozmiar pliku .so. Zmniejsza to nie tylko rozmiar pobierania, ale także wykorzystanie pamięci przez mmap pliku .so w czasie działania, co zmniejsza ogólne obciążenie pamięci fizycznej.
AndroidToolChain.cs
if (MinSDKVersion >= 28)
{
Result += " -Wl,--pack-dyn-relocs=android+relr,--use-android-relr-tags";
}
else if (MinSDKVersion >= 23)
{
Result += " -Wl,--pack-dyn-relocs=android";
}
Aby zastosować tę optymalizację, sprawdź, czy w Ustawieniach projektu jest włączona opcja Enable compression of relocation tables lub czy włączona jest zmienna bEnableAdvancedBinaryCompression.
Aby sprawdzić, czy kompresja RELR jest prawidłowo stosowana do pliku binarnego, użyj narzędzia llvm-readelf z Android NDK.
llvm-readelf -d libUnreal.so | grep RELR
W praktyce utworzenie szablonu ThirdPerson Unreal Engine i pomiar za pomocą dumpsys meminfo potwierdza zmniejszenie zużycia PSS i RSS w przypadku mmap pliku .so.
| Kategoria | Łączna liczba PSS | Prywatne zmodyfikowane | Prywatne czyste | Zmodyfikowane w pamięci wymiany | Łączna liczba RSS |
|---|---|---|---|---|---|
| mmap pliku .so przed kompresją | 170 210 | 24 360 | 141 564 | 0 | 242 204 |
| mmap pliku .so po kompresji | 138 031 | 24 748 | 108 976 | 0 | 210 140 |
Kompresja tekstur
Użyj odpowiedniej kompresji tekstur, aby zmniejszyć wykorzystanie pamięci przez ogólne zasoby graficzne. Używanie adaptacyjnej skalowalnej kompresji tekstur (ASTC), standardu mobilnego, zapewnia lepszą jakość obrazu niż ETC2 przy tym samym rozmiarze lub osiąga wyższe współczynniki kompresji przy mniejszym zużyciu pamięci przy tym samym docelowym poziomie jakości obrazu.
Optymalizacja przesyłania tekstur
W środowiskach mobilnych używanie domyślnego rozmiaru puli przesyłania tekstur Unreal Engine może spowodować wstępne przydzielenie zbyt dużej puli pamięci w stosunku do specyfikacji urządzenia. Dlatego warto zdefiniować profile urządzeń mobilnych i wybrać odpowiedni rozmiar puli przesyłania tekstur zgodnie z limitami pamięci urządzenia. Użyj też funkcji Statistic w edytorze, aby sprawdzić statystyki tekstur i zidentyfikować tekstury zajmujące dużo pamięci, które w czasie działania mają ekstremalne rozdzielczości. Połącz to z funkcją Required Texture Resolution , aby wymusić limity lub zmniejszyć maksymalne rozdzielczości wymagających tekstur, utrzymując zużycie zasobów pod kontrolą w przydzielonym buforze pamięci tekstur.