Fortnite

Blog técnico sobre el lanzamiento de Fortnite en Android

6.9.2018
Por El equipo de Fortnite

INTRODUCCIÓN

El 9 de agosto lanzamos la beta de Fortnite en Android para dispositivos seleccionados de nuestro colaborador Samsung. Algunos días después, comenzamos a mandar invitaciones a un pequeño grupo de usuarios con dispositivos de distintos fabricantes. Esta beta ha hecho que aprendamos un montón de cosas relacionadas con el rendimiento, la seguridad, la compatibilidad de dispositivos y la llegada de Fortnite a Android mediante el instalador de Fortnite. Vamos a sumergirnos de lleno en la información técnica relativa al lanzamiento, nuestro trabajo actual y el futuro cercano que creemos que le espera a Android.  

ESTADÍSTICAS

Durante los 21 primeros días tras el lanzamiento de Fortnite en Android, el interés se ha mantenido por las nubes, con más de 23 millones de jugadores accediendo a esta beta y más de 15 millones instalando el APK.  Aunque nos encontramos en una fase de acceso exclusivo por invitación para Android, la transición entre los jugadores invitados y los que terminan jugando es similar a la de la beta en iOS.
 

OPTIMIZACIÓN DEL HARDWARE


Introducción técnica

Lanzar el juego en todas las plataformas permitiendo el juego entre ellas supuso un desafío único. Por regla general, a la hora de preparar una versión de un juego en móviles, se suele simplificar el contenido y a veces el diseño para adaptarse a las restricciones de rendimiento de la plataforma. Por ejemplo, puedes obviar los objetos más cercanos a la cámara y así reducir el coste de renderización. En Fortnite, los jugadores de Android pueden estar en la misma partida con sus amigos de PC y consola, por lo que todo lo que afecte a la jugabilidad tiene que renderizarse.

La aventura del lanzamiento en Android

Llevamos desde enero de 2018 trabajando en la versión de Battle Royale de Fortnite para Android junto a un gran equipo. Aunque gran parte del trabajo para hacerlo posible lo dedicamos al rendimiento de renderizado, la estabilidad y la memoria, la increíble cantidad y variedad del hardware, las versiones de los SO y las de los controladores de Android se convirtieron en el mayor obstáculo que teníamos que sortear.

La colaboración con nuestros socios ha sido vital a la hora de llevar Fortnite a Android. Sin su conocimiento, experiencia y esfuerzo no habría sido posible.

Trabajamos codo con codo con Samsung para perfilar y optimizar Fortnite en sus dispositivos. Samsung envió a sus ingenieros a distintas oficinas de Epic en todo el mundo para que trabajaran estrechamente con los nuestros a la hora de optimizar y realizar análisis del rendimiento, y nos echaron un cable con muchos cambios en el código, en especial para el renderizador Vulkan. Mediante el uso de teléfonos de prueba y herramientas internas, fueron capaces de hacernos comprender cuáles eran los desafíos de rendimiento y memoria a los que nos enfrentábamos y cómo sortearlos. También trabajamos con Samsung para conseguir que el proceso de instalación fuera el más fluido y seguro para los usuarios de sus dispositivos.

Además, recibimos la visita de los ingenieros de Google para perfilar y optimizar Fortnite, lo que nos ayudó a identificar las optimizaciones clave y las fugas de memoria, además de concretar una implementación sólida del flujo de imágenes en OpenGL y Android. Los ingenieros de Google rebosan talento y pasión a la hora de conseguir que el ecosistema de Android sea ideal para jugar y lo mejoran constantemente.

Asimismo, hemos podido trabajar con muchos otros socios para probar y optimizar Fortnite, como ARM, Qualcomm, Imagination Technologies, Razer, HiSilicon y muchos más.
 

Fragmentación

El ecosistema de Android se nutre de teléfonos producidos por distintos fabricantes. Cada teléfono está diseñado a partir de un sistema en chip o SOC que incluye una configuración de núcleos de CPU y GPU. Existen varias familias de SOC, como Snapdragon de Qualcomm (el 71 % de los dispositivos compatibles actuales) que utiliza GPU Adreno, sin contar Exynos de Samsung, la serie MT de Mediatek y la serie Kirin de HiSilicon, todas con GPU Mali de ARM. Cada dispositivo se lanza con una versión algo distinta del sistema operativo Android y la mayoría de fabricantes personalizan las características de gestión del planificador y la energía. Los dispositivos que comparten la misma GPU también incluyen distintos controladores de gráficos. El resultado es que dos dispositivos que comparten el mismo hardware básico pueden tener diferencias en el rendimiento y, además, sufrir errores distintos.

Nos sorprendió gratamente comprobar que el uso de la última versión de Android es mucho más potente de lo que imaginábamos para los dispositivos entre cero y dos años de antigüedad. En nuestra beta, con requisitos superiores, hemos visto que un 92 % de los usuarios utilizan la versión de Android 8 (Oreo) o superior, alrededor del 8 % la 7 (Nougat) y menos del 0,5 % utilizan una versión de 2015 o previa. Para los dispositivos con más de dos años de antigüedad, hay una disminución importante. En esos dispositivos, los fabricantes tienen que lanzar actualizaciones personalizadas y, en la mayoría de los casos, se tienen que coordinar con varios operadores móviles en todo el mundo para desarrollar y lanzar estas actualizaciones.

Para abordar esta tarea tan compleja, hemos utilizado el sistema jerárquico de perfiles de dispositivos en Unreal Engine. Comenzamos creando cuatro perfiles de rendimiento: bajo, medio, alto y épico. Estos perfiles ajustan la configuración de adaptabilidad en el motor para que el juego pueda funcionar en dispositivos con distintos tipos de rendimiento. El perfil bajo coloca la distancia de visión todo lo lejos posible y deshabilita todas las funciones gráficas opcionales. El épico lo activa todo: sombras, follaje y la distancia de visión máxima permitida en los últimos dispositivos.

Además, también tenemos perfiles de GPU, como Adreno 54x y Mali G72. Estos perfiles seleccionan el tipo de rendimiento que mejor encaja con las capacidades del hardware y nos permiten habilitar optimizaciones y soluciones concretas para ese hardware específico. Por último, también tenemos perfiles de dispositivos específicos, como Samsung Galaxy Note 9 Adreno y Google Pixel 2 XL. Esta última capa nos permite habilitar más soluciones y optimizaciones cuando sean necesarias en dispositivos específicos. Inicialmente, nos fijamos en propiedades como el modelo del dispositivo, la versión del SO y el controlador de gráficos para determinar qué perfil aplicar.
 

Rendimiento de renderizado

El coste de la CPU al renderizar fue nuestro mayor obstáculo en el rendimiento a la hora de mantener una frecuencia de imágenes estable. Invertimos mucho más tiempo de la CPU en el controlador de gráficos de Android que en PC, consola o iOS. Aunque los dispositivos más recientes de Android, como la versión Adreno del Galaxy S9, pueden gestionar más de 1500 procesos de renderizado por fotograma, los dispositivos anteriores tienen capacidad para mucho menos. Los dispositivos medios compatibles necesitan una media de 600 procesos de renderizado mientras que en la gama inferior es de unos 400.

Ya habíamos reducido la cantidad de objetos necesarios para renderizar cada fotograma dentro de lo posible cuando lanzamos la versión de iOS a principios de año, así que tuvimos que buscar otras soluciones. Probamos a llevar a cabo un proceso de renderizado que agrupara múltiples elementos similares y los renderizara a la vez con el menor número de llamadas y, aunque conseguimos algunos resultados, no eran suficientes para justificar la complejidad del código añadida.

Las distintas optimizaciones en nuestro renderizador OpenGL también dieron sus frutos, pero el mayor éxito llegó de sorpresa como parte de una optimización de memoria: los objetos de búfer uniformes emulados. Esta es una ruta de código compatible con UE4 durante años que se utiliza en dispositivos OpenGL ES2 y que no tiene compatibilidad nativa con los objetos de búfer uniformes o búferes de constantes. Funciona de la siguiente manera. Durante la compilación del sombreador identificamos todas las constantes necesarias para el sombreador y las agrupamos en una selección de la que este se nutre. También almacenamos una tabla de mapeo que le indica al motor dónde reunir constantes de búferes uniformes y dónde colocarlos en la disposición de constantes. Durante la ejecución, mantenemos los búferes uniformes en la memoria accesible de la CPU, usamos la tabla de mapeo para copiarlos a un búfer temporal y cargamos los constantes con una sola llamada de función glUniform4fv.

Aunque resulta un misterio, la ruta de código de los objetos de búfer uniformes emulados nos ahorró una gran cantidad de tiempo en el controlador. Es posible que el controlador esté haciendo algo similar de forma interna y nuestra implementación sea más rápida para nuestras cargas de trabajo. También es posible que, como creamos y vinculamos menos recursos, el controlador emplee menos tiempo registrando la gestión de esos búferes. Sea cual sea el motivo, esta ruta reduce el coste del controlador entre un 10 y un 15 % en algunos casos.
 

Vulkan

Vulkan es la nueva API de gráficos para Android y está diseñada en pos del rendimiento y un nivel de acceso bajo al hardware. En 2016 empezamos a colaborar con Samsung para crear la demo de ProtoStar y mostrar lo que podíamos conseguir con una API de gráficos de alto rendimiento en Android. Como Fortnite necesita renderizar tantos objetos por fotograma, Vulkan parecía la opción más sensata. Por desgracia, no todo iba a ser tan fácil.

La compatibilidad con Vulkan no es obligatoria en Android, por lo que no podíamos contar con que estuviera disponible en todo el ecosistema. Como nuestro objetivo era el hardware más reciente, no era un impedimento para nosotros, pero nos topamos con algunos errores y problemas de rendimiento en los primeros controladores de Vulkan, por lo que concluimos que OpenGL era más rápido y estable que Vulkan en la mayoría de dispositivos. Esto no es ninguna novedad: la industria ha tenido toda una década para optimizar y reforzar las implementaciones en OpenGL. Vulkan es una API más compleja y todavía llevará algo de tiempo alcanzar el mismo nivel de madurez.

Aunque el juego es compatible con Vulkan en Samsung S9+ (Adreno) y Note 9 (Adreno). Al colaborar estrechamente con los ingenieros de Samsung pudimos optimizar la compatibilidad de Vulkan con UE4 y conseguir que sea un 20 % más rápido que OpenGL de media. De cara al futuro seguimos trabajando con nuestros socios para conseguir que Vulkan sea compatible con otros dispositivos, con el objetivo de que los jugadores de Fortnite en Android disfruten de una experiencia superior, además de mejorar el rendimiento de los controladores de Vulkan para todos los desarrolladores.
 

Memoria

La memoria es nuestra lucha constante. No solo hemos lanzado un juego de consola al completo en móviles, sino que seguimos añadiendo constantemente nuevo contenido. Es por esto que siempre estamos optimizando la memoria. Android nos plantea desafíos nunca vistos en comparación con otras plataformas.

No podemos centrarnos en un solo tipo de memoria. Cada dispositivo cuenta con una cantidad de memoria distinta, distintos tipos de aplicaciones funcionando en segundo plano y posiblemente distintas políticas a la hora de decidir si cerrar una aplicación por usar mucha memoria. Ninguna API puede decirte de qué tipo de memoria se trata. Como punto de datos, durante una prueba de memoria entramos en una partida y empezamos a asignar memoria hasta que el SO detuvo el proceso. Con teléfonos recién reiniciados sin ejecutar ninguna aplicación, en Samsung Galaxy S8 (Mali) pudimos asignar 3 GB de sus 4 totales de memoria antes de que cerrara la aplicación. En un Google Pixel solo pudimos asignar 1,8 GB de sus 3,6 de memoria total.

El sistema asigna mucha memoria por nuestra parte, sobre todo el controlador de gráficos. UE4 hace un seguimiento de toda la memoria que solicitamos al SO directamente, entre la que se encuentra una estimación de la asignación de memoria de la GPU, pero en realidad no sabemos a ciencia cierta lo que asigna el controlador. Haciendo pruebas descubrimos que buena parte de la memoria asignada por el controlador en Fortnite se destinaba a los sombreadores.

La optimización de búferes uniformes emulados ya mencionada resultaba excelente en términos de memoria. OpenGL necesita que la aplicación pueda consultar la ubicación de cualquier miembro de un búfer uniforme. Por ejemplo, se le puede preguntar a OpenGL el desplazamiento de la matriz LocalToWorld dentro del búfer uniforme primitivo. Para conseguir esto, el controlador necesita almacenar algunos metadatos en memoria, como una tabla de desplazamiento, que incluyan cadenas con el nombre de cada miembro y tiene que hacerlo para cada programa.

En Fortnite disponemos de una amplia variedad de sombreadores. Durante una sesión normal de juego, el motor utiliza unos 2000 programas de sombreado distintos. El controlador almacena los metadatos dentro de cada sombreador, lo que aumenta la cantidad de memoria. ¡Hemos ahorrado más de 400 MB de memoria en Fortnite utilizando los objetos de búfer uniformes emulados!

Cuando lanzamos Fortnite en Android, nuestro equipo de pruebas interno nos indicó que nos encontrábamos dentro de los límites de memoria de los dispositivos. Realizamos pruebas activando el navegador de Google, Maps, reprodujimos música y nos aseguramos de que podíamos jugar a Fortnite sin mayores complicaciones. Pero cuando el juego estuvo disponible, descubrimos que muchos jugadores sufrían cuelgues o un rendimiento inferior al quedarse sin memoria.

Cuando un teléfono Android tiene poca memoria, intentará liberar recursos al cerrar aplicaciones que no se estén usando. Sin embargo, resulta que existen una serie de aplicaciones y servicios con mal funcionamiento en segundo plano que simplemente se reinician cuando el SO las cierra. ¡Así que la situación empeora! Android cerraba las aplicaciones para recuperar memoria, pero estas se reinician y comienzan a consumir la misma cantidad que antes. Y lo que es peor, iniciar y detener aplicaciones consume tiempo de la CPU, por lo que el problema pasaba de no tener memoria a utilizar además muchísimos recursos de la CPU innecesarios.

Actualizamos nuestros procesos de pruebas para instalar y ejecutar las aplicaciones más habituales que usa la mayoría de usuarios y de esta forma poder adelantarnos a los problemas, pero seguíamos necesitando reducir el uso de memoria urgentemente. Fuimos capaces de acotar el problema a los usuarios que utilizaban los ajustes altos o épicos en dispositivos nuevos con Qualcomm Snapdragon y 4 GB de memoria. De nuevo, la memoria de los sombreadores parecía ser la principal diferencia. Como medida temporal, deshabilitar las sombras supuso una mejora del problema. De esta forma redujimos el número de sombreadores requeridos, ya que las variantes para buscar y filtrar los mapas de sombras ya no eran necesarias. También hemos añadido un caché para los sombreadores. Este caché nos permite mantener únicamente los sombreadores necesarios para renderizar la escena actual en memoria. El resto de sombreadores se almacenan comprimidos en memoria. Cuando sean necesarios, se descomprimen y pasan a disposición del controlador.
 

Futuro: siguientes pasos

En estos momentos nos estamos centrando en conseguir que el juego funcione bien en todos los dispositivos compatibles y reuniendo la suficiente memoria para mejorar tanto la calidad como la estabilidad. A continuación, nos fijaremos en los problemas de compatibilidad, como los de sonido, aunque en la actualización 5.40 ya corregimos algunos de estos problemas en muchos dispositivos y ampliamos los teléfonos compatibles. También tenemos intención de darles un control más detallado a los usuarios para ajustar sus dispositivos y realizar intercambios entre la calidad y el rendimiento.

Seguiremos mejorando la compatibilidad con Vulkan y la implementaremos en más dispositivos a lo largo del tiempo. Esto implica que seguiremos colaborando con los fabricantes para sacar optimizaciones, tanto en UE4 como en sus controladores. A largo plazo, la mejora de soporte de Vulkan y los controladores nos permitirá ofrecer un mejor rendimiento a los jugadores en todos los juegos de UE4 para Android.

Seguimos analizando los teléfonos más antiguos y lentos, pero no tiene sentido retroceder demasiado. Todos los años, los teléfonos de alta gama se vuelven un 50 % más rápidos que los del año anterior. Fortnite funciona bastante bien en los teléfonos con dos años de antigüedad, muy bien en los de un año, y estupendamente en los de este mismo año. A este ritmo, ¡imaginaos el aspecto que tendrá Fortnite en los teléfonos del próximo año!

image2.png


Para terminar, muchas de las optimizaciones del código que realizamos en el motor ya se han implementado en la versión 4.20 de UE4. El resto de optimizaciones aparecerán en la 4.21, y seguiremos compartiendo futuras mejoras con todos los desarrolladores de UE4.
 

LA GUERRA CONTRA EL MALWARE

Antes incluso de anunciar el lanzamiento de Fortnite en Android, nos dimos cuenta de la aparición de sitios web ilegales que anunciaban "Fortnite para Android". Los típicos sitios llenos de malware y estafas.  Cuando fuimos conscientes de su existencia, nos pusimos manos a la obra para cerrarlos, tanto los sitios como sus proveedores de hosting, e incluso los anuncios o los vídeos que se hicieran eco de ellos.  Tanto si contenían malware como si no, consideramos que todos los sitios de distribución de Fortnite para Android eran ilegales. La única fuente de distribución legal de Fortnite para Android es directamente Epic a través del instalador de Fortnite.
 
Hasta el momento, Epic ha tomado medidas contra 47 páginas ilegales de "Fortnite para Android", y muchas de ellas parecen estar controladas por los mismos individuos. Seguimos monitorizando la situación con intención de retirarlas de la red o restringir el acceso fomentando nuestra colaboración con una serie de socios antifraude (como ISP, compañías de navegadores y de antivirus) que puedan implementar una alerta de navegación como la siguiente:

Image1.png
 
Continuamos buscando sin descanso nuevos sitios de malware a medida que aparecen con la ayuda de un equipo interno dedicado a este fin. Y eso no es todo, también hemos contratado los servicios de una IP de terceros y un organismo antifraude para incrementar la vigilancia. Mediante esta colaboración podemos detectar y controlar nuevos dominios registrados en direcciones URL sospechosas para, en caso de que su actividad se vuelva ilegal, Epic pueda tomar las medidas apropiadas, llegando incluso a demandarles.

CONCLUSIÓN

Fortnite en Android representa un primer paso en la industria en muchos aspectos: es el primer juego de consola y PC en aparecer en Android con juego multiplataforma y compatibilidad total, y es el primer gran juego en ser distribuido fuera de Google Play Store. Ha sido un proceso de compromiso y aprendizaje titánico, pero ver cómo se subían al barco más de 15 millones de usuarios de Android demuestra que este enfoque es viable y tiene muchas probabilidades de éxito. Y lo que es más importante, todo el trabajo técnico realizado en Android para Fortnite llegará a los desarrolladores de Unreal Engine con la actualización 4.21, para que todos podamos sacarle provecho.