Perfiles de juegos para móviles con Unity y ARM

MARK HARKNESS / UNITY TECHNOLOGIESSenior Software Engineer
Mar 11, 2021|11 minutos
Perfiles de juegos para móviles con Unity y ARM
Para tu comodidad, tradujimos esta página mediante traducción automática. No podemos garantizar la precisión ni la confiabilidad del contenido traducido. Si tienes alguna duda sobre la precisión del contenido traducido, consulta la versión oficial en inglés de la página web.

Aprende a resolver los problemas de rendimiento de los móviles con las herramientas de creación de perfiles de Unity y Arm. Profundiza en cómo crear perfiles con Unity, cómo optimizar las caídas de rendimiento y consejos y trucos para sacar el máximo partido a tus activos de juego.

En este blog, examinamos cómo identificar problemas de rendimiento en un juego para móviles mediante el uso de herramientas de creación de perfiles de Unity y ARM. También presentamos las mejores prácticas para optimizar el contenido de los juegos para móviles.

Para detectar problemas de rendimiento en tu juego, primero debes probarlo en distintos dispositivos. La mejor manera de hacerlo es capturar un perfil de rendimiento en un dispositivo real. Herramientas como el perfilador de Unity y el depurador de fotogramas pueden proporcionarte una gran visión de dónde están tomando sus recursos los elementos de tu juego. Además, herramientas como ARM Mobile Studio te permiten capturar datos de actividad del contador de rendimiento del dispositivo, para que puedas ver exactamente cómo está utilizando tu juego los recursos de la CPU y la GPU. Aunque el dispositivo que hemos utilizado tiene una GPU Mali, los conceptos aquí introducidos también se aplican a otras GPU para móviles.

Ejemplo de prueba

El juego que estamos probando es un RPG de acción, en el que el jugador debe luchar contra oleadas de PNJ enemigos con ataques cuerpo a cuerpo y de hechizos. Este tipo de juego puede verse rápidamente limitado por la GPU en un dispositivo móvil, con un número cada vez mayor de enemigos en pantalla, así como múltiples efectos visuales de partículas y posprocesamiento.

Creación de perfiles con Unity Profiler y Frame Debugger

Pasamos el juego por el Unity Profiler para identificar cualquier ralentización en el rendimiento. Encontramos algunos sospechosos de alta prioridad, post-procesamiento, y fijamos los picos de Timestep e instanciación.

Post Processing

Los efectos de postprocesado fueron una de las causas principales del bajo rendimiento de la CPU del juego.

La cámara de renderizado tarda mucho tiempo y cruza el límite de fotogramas.
La cámara de renderizado tarda mucho tiempo y cruza el límite de fotogramas.

De todos los efectos de postprocesado, el pase de floración, que hace que las zonas brillantes de la escena resplandezcan, fue el más agotador.

En la captura de pantalla de arriba, se puede ver que la Cámara de Render está tomando una gran cantidad de tiempo y cruza el límite de fotogramas. A continuación, el subproceso principal espera a que finalicen las órdenes de renderizado antes de preparar el siguiente fotograma. Echemos un vistazo al depurador de fotogramas de Unity para averiguar qué está pasando.

Visualización del Frame Debugger con el dispositivo a resolución de pantalla completa
Visualización del Frame Debugger con el dispositivo a resolución de pantalla completa

Lo primero que se observa en el depurador de fotogramas es que el juego se renderiza a la resolución de pantalla completa del dispositivo. Para un dispositivo móvil medio, esto supone una presión excesiva sobre la GPU del dispositivo, dada la complejidad del contenido. Reducir la resolución a algo más razonable, como 1080p o incluso 720p, reduciría considerablemente los costes de renderizado del juego, especialmente los efectos de postprocesado.

Visualización de las llamadas al sorteo de la pirámide de floración
Visualización de las llamadas al sorteo de la pirámide de floración

El siguiente punto de observación es que el efecto bloom se produce en 25 llamadas de sorteo de la pirámide bloom. Cada llamada a dibujo representa un búfer de destino con un tamaño que comienza en la mitad de la resolución del dispositivo a pantalla completa. Esta resolución se reduce a la mitad en cada iteración. Reducir la resolución de renderizado inicial es una forma de reducir el número potencial de iteraciones. Otra alternativa sería modificar el código fuente del efecto bloom para reducir el número de iteraciones que se producen e imponer algún límite razonable. Sin embargo, en este caso, sería mejor desactivar los efectos de postprocesamiento por ahora, debido a la considerable cantidad de tiempo que lleva manejar esos efectos. Al menos hasta que el resto del juego funcione a 30 imágenes por segundo.

Tiempo fijo

Otra mejora para el proyecto sería reducir la frecuencia del intervalo fijo Timestep. Podemos ver que actualmente es lo suficientemente corto para ser llamado múltiples veces por cuadro; por defecto, Unity establece esto a 0.02 o 50Hz. Puedes probar con un valor fijo de Timestep de 0,04 para títulos móviles que apunten a 30 FPS. El motivo es que, a 0,333, que correspondería a 30 FPS, existe la posibilidad de que un fotograma se dispare en el tiempo y acabes con dos llamadas en el fotograma siguiente. Esto significa que se tarda más, y nunca se puede romper el ciclo de un fotograma un poco más largo. El usuario también puede establecer el paso de tiempo máximo permitido para evitar que la recuperación tarde más de lo deseado.

Esta duración de Timestep afecta a los scripts que utilizan la función FixedUpdate y a cualquier sistema interno de Unity que se actualice en el intervalo de actualización fijo, por ejemplo, la física y la animación.

Timeline utilizando un Timestep fijo
Timeline utilizando un Timestep fijo

A efectos de este proyecto, sólo la física y Cinemachine contribuyeron en gran medida al tiempo empleado, unos 3 ms por llamada; una llamada significaba que el sistema se actualizaba por completo (aunque ser llamado otras 5 veces significaba que esto podía sumar 15 ms por fotograma de tiempo perdido).

FixedUpdate.PhysicsFixedUpdate ralentizado por efectos de postprocesado
FixedUpdate.PhysicsFixedUpdate ralentizado por efectos de postprocesado

Esto ocurre debido a la lentitud de los efectos del postprocesado. Sin embargo, la recomendación anterior de reducir la frecuencia fija de Timestep para evitar trabajo innecesario a la CPU sigue en pie.

Picos de instanciación

Durante el perfilado, se podían observar picos en el tiempo de fotogramas. Si se rastrean en la vista jerárquica del perfil de la CPU, se observa que tienen su origen en la instanciación de NPC.

Seguimiento de los picos de instanciación en la jerarquía de perfiles de CPU
Seguimiento de los picos de instanciación en la jerarquía de perfiles de CPU

La solución más común para esto es instanciar los personajes antes de tiempo y mantenerlos en un estado inactivo, en algún tipo de reserva de objetos. Estos NPCs se pueden coger de la reserva sin coste de instanciación. Si se necesitan más, la piscina puede ampliarse según las necesidades.

El mismo problema se observa cuando se utilizan habilidades, ya que también se instancian objetos.

Picos de instanciación de la CPU
Picos de instanciación de la CPU

La agrupación de objetos es la forma más sencilla de resolver estos problemas. Puede afectar a los tiempos de carga, pero permite una velocidad de fotogramas mucho más suave en tiempo de ejecución, que es el menor de dos males en este caso.

Creación de perfiles con Arm Mobile Studio

También hemos utilizado ARM Mobile Studio para conocer mejor el comportamiento del juego. Con las herramientas de Mobile Studio, podemos obtener datos de contraactividad del rendimiento de la CPU y la GPU, para saber exactamente cómo utiliza el juego los recursos del dispositivo.

Puede descargar gratuitamente ARM Mobile Studio aquí. Se incluyen 4 herramientas:

  • Asesor de rendimiento: para generar informes de fácil lectura y obtener consejos de optimización.
  • Streamline: un completo perfilador de rendimiento para captar toda la actividad de los contadores.
  • Compilador Mali Offline: para comprobar el rendimiento de un programa de sombreado en una GPU Mali.
  • Analizador de gráficos: para depurar las llamadas a la API de gráficos y analizar cómo se ha representado el contenido.
Asesor de rendimiento

El Asesor de Rendimiento nos proporciona un resumen rápido del rendimiento del juego, y está pensado para ser utilizado como un chequeo regular de salud. Es rápido generar un informe, sobre todo si lo incorporas a un flujo de trabajo de integración continua, junto con tu sistema de compilación nocturna. El Asesor de Rendimiento nos proporciona un resumen rápido del rendimiento del juego, y está pensado para ser utilizado como un chequeo regular de salud. Es rápido generar un informe, sobre todo si lo incorporas a un flujo de trabajo de integración continua, junto con tu sistema de compilación nocturna.

Asesor de resultados - Resumen de capturas
Asesor de resultados - Resumen de capturas

Durante los 2 primeros minutos del juego, Performance Advisor nos dice que sólo estamos alcanzando una media de 17 fotogramas por segundo. La sección verde al principio del gráfico de análisis de la velocidad de fotogramas indica dónde se está cargando el juego y, de repente, el gráfico se vuelve azul, lo que indica que el juego se ha fragmentado, y así permanece durante todo el proceso. Esto significa que la GPU del dispositivo tiene dificultades para procesar cargas de trabajo de fragmentos, lo que sugiere que el juego está solicitando demasiado trabajo o que no está procesando los píxeles de forma eficiente.

Como hemos añadido anotaciones de región al juego, el gráfico de análisis de la velocidad de fotogramas muestra nuestros nombres de región personalizados. Donde el gráfico muestra un marcador etiquetado con una 'S', Performance Advisor ha tomado una captura de pantalla del juego para ayudarnos a identificar lo que está sucediendo en la pantalla en ese punto. Puede configurar las capturas de pantalla para que se realicen cuando los FPS caigan por debajo de un valor especificado. En este caso, como los FPS se mantienen bajos durante todo el proceso, el Asesor de rendimiento realiza una captura de pantalla en el intervalo predeterminado de cada 200 fotogramas.

Echa un vistazo al gráfico de ciclos por fotograma de la GPU, donde hemos añadido un presupuesto de 28 millones de ciclos por fotograma para este dispositivo. Hemos calculado que este es el número máximo de ciclos que este dispositivo debería ser capaz de manejar, sin dejar de alcanzar una frecuencia de imagen de 30 FPS. Aquí, podemos ver que el número de ciclos de la GPU excede significativamente este presupuesto, y que el número de ciclos aumenta con el tiempo.

Asesor de rendimiento - Ciclos de GPU por fotograma
Asesor de rendimiento - Ciclos de GPU por fotograma

El Asesor de rendimiento ofrece consejos de optimización cuando detecta un problema. Si nos fijamos en el gráfico de ciclos de sombreado por fotograma, vemos que el número de ciclos del motor de ejecución es elevado. Dentro de un núcleo de sombreado Mali, el motor de ejecución se encarga de procesar las operaciones aritméticas. Performance Advisor ha señalado esto como un problema y nos aconseja reducir el cálculo en los sombreadores.

Asesor de rendimiento - Ciclos de sombreado por fotograma
Asesor de rendimiento - Ciclos de sombreado por fotograma

Esto tiene fácil solución. Puedes reducir la precisión de las variables del sombreador a mediump, en lugar de highp, sin que se note ningún cambio en pantalla. Esto reducirá significativamente el coste de los sombreadores. Para obtener información sobre cómo hacerlo, consulte Tipos de datos de sombreado y precisión en nuestra documentación. Además, como descubrimos anteriormente con el depurador de fotogramas de Unity, el juego se está renderizando actualmente a la resolución de pantalla completa del dispositivo. Cualquier cambio que hagamos para reducir la resolución de renderizado del juego (a 1080p o 720p) reducirá también el coste del sombreado de fragmentos.

Métricas de contenido

Habíamos fijado un presupuesto de 500.000 vértices por fotograma para este dispositivo. El presupuesto se supera en torno a los 45 segundos y la cifra aumenta constantemente con el tiempo.

Asesor de rendimiento - Vértices por fotograma
Asesor de rendimiento - Vértices por fotograma

Si observamos el gráfico de primitivas por fotograma, nos damos cuenta de que el número total de primitivas que se procesan aumenta con el tiempo, aunque el número de primitivas visibles se mantiene relativamente constante. En los primeros 2 minutos del juego, los únicos objetos nuevos que se crean son los PNJ enemigos, que luego son destruidos en una ráfaga de rayos por nuestro héroe. Esto sugiere que cuando los enemigos son destruidos, su geometría sigue presente, aunque no sea visible.

Asesor de rendimiento - Primitivas por fotograma
Asesor de rendimiento - Primitivas por fotograma

Hay varias razones por las que la GPU puede no ser capaz de manejar las demandas del juego, así que tenemos que explorar más a fondo la herramienta de perfilado de ARM con Streamline. Streamline nos dirá más sobre esta pesada carga de trabajo de fragmentos, y mirando los otros contadores, podemos encontrar pistas sobre cómo aligerar la carga.

Racionalizar el análisis

Si observamos la misma sección del juego en Streamline, podemos explorar una serie de gráficos que muestran la actividad del contador de la GPU en las distintas fases del procesamiento de geometría y píxeles. Esto ilustra cómo procesa la GPU el contenido del juego y si hay procesamiento innecesario.

Las GPU basadas en Mali adoptan un enfoque basado en mosaicos para procesar las cargas de trabajo gráficas, en el que el espacio de la pantalla se divide en mosaicos, y cada mosaico se procesa hasta completarse en orden. Para cada mosaico, primero se ejecuta el procesamiento de la geometría y, a continuación, se colorean los píxeles durante el procesamiento de píxeles.

Las GPU basadas en Mali procesan cargas de trabajo gráficas
Las GPU basadas en Mali procesan cargas de trabajo gráficas

Ya sabemos que la GPU del dispositivo está al máximo con las cargas de trabajo de fragmentos, así que tenemos que buscar formas de reducir la presión sobre la etapa de procesamiento de píxeles.

Una forma de reducir la carga de procesamiento de píxeles es reducir la complejidad de la geometría que se envía para el procesamiento de píxeles en primer lugar. La geometría que está completamente fuera de la pantalla o de espaldas a ella se elimina antes del procesamiento de píxeles, pero los triángulos pequeños que sólo cubren parcialmente cuadrángulos de 2×2 píxeles pueden erosionar la eficiencia de los fragmentos y tener un alto coste de ancho de banda por píxel de salida.

Los gráficos Mali Geometry Usage y Mali Geometry Culling Rate de Streamline muestran la eficiencia con la que la GPU procesa la geometría. Podemos ver el número de primitivas que se envían a la GPU y cuántas de ellas se eliminan durante el procesamiento de la geometría. El trabajo que se elimine en esta fase no pasará al procesamiento de píxeles. Es una buena noticia, pero podríamos organizar el contenido de forma más eficiente, de modo que las primitivas no visibles no se pasen en absoluto.

Gráficos de uso de geometría Mali y tasa de eliminación de geometría Mali en Streamline
Gráficos de uso de geometría Mali y tasa de eliminación de geometría Mali en Streamline

En el gráfico de uso de la geometría Mali, podemos ver que , 1,07 millones de primitivas entran en el procesamiento de la geometría (línea naranja) en el marco temporal seleccionado (unos 0,05 segundos), pero 700.000 primitivas se eliminan en esta fase (línea roja).

El gráfico de la tasa de eliminación de la geometría Mali muestra por qué se eliminan. Alrededor de la mitad son eliminados por la prueba de orientación (línea naranja), lo que es de esperar, ya que se trata de los triángulos orientados hacia atrás de nuestros objetos 3D. Lo más preocupante es que el 31,9% de las primitivas son eliminadas por la prueba de muestreo (línea morada); lo ideal sería que esta cifra fuera inferior al 5%. La prueba de muestreo indica que estas primitivas eran demasiado pequeñas para ser rasterizadas, no alcanzando un solo punto de muestreo, y por tanto, consideradas invisibles. Esto puede ocurrir cuando los objetos con mallas complejas se colocan lejos de la cámara y los triángulos de la malla son demasiado pequeños para ser visibles. Los números más altos podrían indicar que las mallas de los objetos del juego son demasiado complejas para su posición en la pantalla.

Este problema se agrava en el caso de primitivas lo suficientemente grandes como para superar la prueba de muestreo, pero que sólo cubren unos pocos píxeles. Estos "microtriángulos" pasan al procesamiento de píxeles y son caros de procesar. Esto se debe a que, durante el sombreado de fragmentos, los triángulos se rasterizan en parches de dos por dos píxeles, llamados quads. Los triángulos diminutos sólo alcanzan un subconjunto de píxeles dentro de un cuadrado, pero hay que enviar todo el cuadrado para su procesamiento. Esto significa que el fragment shader se ejecutará con carriles ociosos en el hardware, haciendo que la ejecución del shader sea menos eficiente.

Triángulos diminutos que golpean un subconjunto de píxeles dentro de un cuadrado
Triángulos diminutos que golpean un subconjunto de píxeles dentro de un cuadrado

Para comprobar si tenemos un problema con los microtriángulos, podemos utilizar el gráfico de propiedades de la carga de trabajo del núcleo Mali en Streamline para supervisar la eficacia de la cobertura. Lo ideal es que sea inferior al 10%. Podemos ver aquí que en algunas secciones, la tasa de cobertura parcial (línea verde) es muy alta, superior al 70%. Este valor sugiere que el contenido tiene una alta densidad de microtriángulos, lo que confirma el problema señalado anteriormente por la elevada tasa de eliminación de muestras.

Mali Core Workload Property chart
Mali Core Workload Property chart

La geometría que aparece en pantalla debe tener el tamaño adecuado para su posición. Un paisaje complejo y lejano no necesita ser muy detallado, ya que no aporta mucho a la escena. Podríamos utilizar mallas de nivel de detalle (LOD)para los objetos que están más alejados de la cámara, para reducir la complejidad y ahorrar potencia de procesamiento y ancho de banda de DRAM. O, en lugar de utilizar la geometría, podríamos utilizar texturas y mapas de normales para construir detalles de la superficie de los objetos.

Análisis de sombreadores

Gracias al informe Performance Advisor, descubrimos que nuestros sombreadores podían ser demasiado caros y que nos beneficiaría reducir su precisión. En Streamline, podemos utilizar el gráfico de uso variable de Mali para ver el número de ciclos en los que está activa la interpolación de 32 bits (alta precisión) o de 16 bits (precisión media). Aquí, podemos ver que la interpolación de 32 bits se utiliza en la mayoría de los ciclos. Las variables de 16 bits interpolan el doble de rápido que las variables de 32 bits, y utilizan la mitad de espacio en los registros del sombreador para almacenar los resultados de la interpolación, por lo que se recomienda utilizar entradas variables mediump (16 bits) en los sombreadores de fragmentos siempre que sea posible.

Cuadro de utilización variable de Malí
Cuadro de utilización variable de Malí
Análisis de sombreadores con el compilador Mali Offline

Para explorar los shaders, podemos utilizar la herramienta de compilador estático offline de ARM Mobile Studio, para generar un análisis rápido del programa de shaders.

Para hacer esto, usted necesita tomar el código del shader del archivo compilado que Unity le da, entonces ejecute Mali Offline Compiler en ese archivo:

1. En Unity, seleccione el sombreador que desea analizar, ya sea directamente desde su carpeta de activos, o seleccionando un material, haciendo clic en el icono de engranaje y eligiendo Seleccionar sombreador.

2. Seleccione Compilar y muestre el código en el Inspector. El código del shader compilado se abrirá en tu editor de código predeterminado. Este archivo contiene varias variantes de código de sombreado.

3. Copie una variante del sombreador de vértices o de fragmentos de este archivo en un archivo nuevo y dele una extensión.vert o .frag. Los sombreadores de vértices comienzan con #ifdef VERTEX y los de fragmentos con #ifdef FRAGMENT. Terminan con su respectivo #endif. (No incluya las sentencias #ifdef y #endif en el nuevo fichero).

4. En un terminal de comandos, ejecute Mali Offline Compiler en este archivo, especificando la GPU que desea probar. Por ejemplo: malioc -c Mali-G72 myshader.frag Consulte Introducción al compilador Mali sin conexión para obtener más instrucciones.

Optamos por analizar el fragment shader responsable del efecto de disolución que se produce cuando mueren los NPC enemigos. Aquí está el informe del compilador Mali Offline, con las secciones de interés resaltadas:

Informe del compilador Mali Offline

Vemos que sólo el 2% de los cálculos aritméticos se realizan con precisión de 16 bits. El shader funcionará más eficientemente si reducimos la precisión de highp a mediump. Esto reduce tanto el consumo de energía como la presión de registro, y puede duplicar el rendimiento. Hay situaciones en las que siempre es necesario un valor alto, como para los cálculos de posición y profundidad, pero en muchos casos apenas se aprecia diferencia en pantalla al reducir la precisión a un valor medio.

El informe ofrece un desglose aproximado del coste del ciclo de las principales unidades funcionales del núcleo de sombreado Mali. Aquí podemos ver que la unidad aritmética es la más utilizada.

En la sección de propiedades del shader, vemos que este shader contiene computación uniforme que depende sólo de constantes literales o valores uniformes. Esto produce el mismo resultado para cada subproceso en una llamada de dibujo o envío de cálculo. Lo ideal sería trasladar este tipo de cómputo uniforme a la lógica de aplicación en la CPU.

También podemos ver que el sombreador puede modificar la máscara de cobertura de fragmentos que determina qué puntos de muestra de cada píxel están cubiertos por un fragmento, utilizando la sentencia de descarte para descartar fragmentos por debajo de un umbral alfa. Los sombreadores con cobertura modificable deben utilizar una actualización ZS tardía, lo que puede reducir la eficiencia de las pruebas ZS tempranas y la programación de fragmentos para fragmentos posteriores en la misma coordenada. En la medida de lo posible, se debe minimizar el uso de declaraciones de descarte y de alfa a cobertura en los sombreadores de fragmentos. Consulte la guía ARM Mali Best Practices para obtener consejos sobre el uso de las sentencias de descarte.

Análisis de llamadas a la API de gráficos con Graphics Analyzer

En el analizador de gráficos de ARM Mobile Studio, puede ver todas las llamadas a la API de gráficos realizadas por la aplicación y recorrerlas una a una para ver cómo se construye la escena. Esto ayuda a identificar objetos demasiado complejos para su tamaño en pantalla y la distancia a la cámara. He aquí algunos ejemplos que encontramos en este juego:

La mampostería de la esquina más alejada de la escena está construida con geometría y utiliza 2064 vértices. El detalle no es extremadamente visible en el resultado final, por lo que se trata de un procesamiento desperdiciado.

Muestra de ladrillos en 2064 vértices
Muestra de ladrillos en 2064 vértices

Encontramos el mismo problema para las baldosas del suelo: tienen 1170 vértices cada una, pero aunque el objeto está cerca de la cámara, la escena no se beneficia realmente de esta complejidad. Sería más eficiente utilizar un mapa normal aquí, para representar las protuberancias y los bordes angulares en lugar de construirlo con triángulos. Además, podemos ver que estos objetos se dibujan utilizando llamadas a dibujo separadas. La reducción del número de llamadas a dibujo mediante la agrupación de objetos o el uso de instanciación de objetos podría aumentar el rendimiento.

Muestra de baldosa en 1170 vértices
Muestra de baldosa en 1170 vértices

Otro ejemplo son las estatuas del fondo de la escena: 6966 vértices cada una. Se puede ver que la malla es bastante compleja, lo que dará un gran resultado visual cuando el jugador se acerque a las estatuas, pero desde esta posición de la cámara, apenas se notan. Ahorraría mucho poder de procesamiento usar Mesh LODs aquí para representar estos objetos cuando están tan lejos de la cámara.

Muestra de estatua en 6966 vértices
Muestra de estatua en 6966 vértices

Recuerde que reducir la complejidad de muchos objetos similares supone un gran ahorro en el procesamiento de la geometría, lo que a su vez reduce la cantidad de sombreado de fragmentos necesaria. Esto no sólo reducirá la carga de trabajo de los fragmentos y aumentará nuestros fotogramas por segundo, sino que también reducirá la huella de instalación del APK.

Optimizaciones del juego

Hemos descubierto varias áreas en las que podríamos introducir cambios en el juego para mejorar el rendimiento. He aquí las que decidimos aplicar y cómo lo hicimos.

Tiempo fijo

Fixed Timestep es un intervalo independiente de la velocidad de fotogramas que controla cuándo se realizan los cálculos de física y los eventos FixedUpdate(). Por defecto, está configurado para funcionar a 50 FPS. Mientras que 50 o incluso 60 FPS son sostenibles en dispositivos móviles de gama alta, los dispositivos más comunes funcionan a 30 FPS, a los que se dirige este título. Vaya a Editar > Configuración del proyecto, y luego a la categoría Tiempo, para establecer la propiedad Temporización fija en 0,04. Esto garantizará que los cálculos de física, FixedUpdate() y las actualizaciones se ejecuten de forma sincronizada.

Establecer el paso de tiempo fijo en 0,04 en la configuración del proyecto
Establecer el paso de tiempo fijo en 0,04 en la configuración del proyecto

Después de que los ajustes fueron hechos al Timestep fijo en Unity, la porción de actualización fija del bucle principal del juego fue llamada solamente una vez por cuadro, por un promedio de 1.5ms. Esto supone una enorme mejora respecto a los 12 ms que tardaba antes, y una solución sencilla a un problema de rendimiento habitual.

Carpeta de recursos

Al iniciar la aplicación, los datos de todos los objetos a los que se hace referencia en las escenas integradas o en la carpeta Recursos se cargan en la caché de ID de instancia. Estos activos se tratan como un gran paquete de activos, por lo que hay metadatos e información de indexación que siempre se cargan en la memoria. Una vez que se utiliza un activo de este paquete, nunca se puede descargar de la memoria.

El método recomendado para gestionar los activos y recursos cuando se pretende mejorar el consumo de memoria es a través del sistema Addressables Asset System, que proporciona una forma eficaz de descargar de la memoria los contenidos que ya no se necesitan.

Instanciación de GPU

En nuestro entorno, tenemos muchos objetos que aparecen varias veces. Paredes, baldosas y otros elementos del entorno se duplican para construir esta escena. Podemos ahorrar llamadas de dibujo habilitando la instanciación de la GPU en el material de los objetos. La creación de instancias en la GPU renderiza mallas idénticas con un número reducido de llamadas a dibujo y permite que cada instancia tenga parámetros diferentes, como el color o la escala. Esta modificación puede aumentar el rendimiento de la CPU. A continuación, puedes ver los datos de Performance Advisor antes de que se activara el instanciamiento de la GPU.

Datos antes de activar la instanciación de la GPU
Datos antes de activar la instanciación de la GPU

Y aquí puedes ver la misma parte de la aplicación, pero con el instanciado de GPU activado: una ganancia pequeña pero apreciable hacia nuestro objetivo de 30 FPS.

Datos con instanciación de GPU activada
Datos con instanciación de GPU activada
Renderizar texturas

Las texturas de renderizado son una forma de añadir elementos 3D a su interfaz de usuario, así como muchos otros casos de uso. Si tienes una cámara renderizando a la textura de render, asegúrate de desactivar la cámara cuando no esté en pantalla. No es necesario renderizar algo que el usuario no verá. Utilice Graphics Analyzer o Unity's Frame Debugger para asegurarse de que estas texturas no están siendo actualizadas fuera de la pantalla.

Puesta en común de objetos

En lugar de sobrecargar la CPU creando y destruyendo los mismos objetos una y otra vez, pruebe la agrupación de objetos. La agrupación de objetos es un patrón de diseño que te lleva a crear los objetos que necesitarás por adelantado, adelantando el trabajo de la CPU. Entonces, en lugar de destruirlos, puede volver a añadirlos a la reserva para reutilizarlos cuando vuelva a necesitar un objeto del mismo tipo. Es una forma fantástica de aliviar la capacidad de procesamiento de la CPU, para que pueda trabajar libremente en tareas más importantes para tu juego.

Con el cambio a la agrupación de objetos, no hay ningún pico asociado a las oleadas de enemigos que aparecen en pantalla que pueda identificarse en las capturas de Unity Profiler, así como ningún efecto perceptible en la velocidad de fotogramas.

Mallas de nivel de detalle (LOD)

Cuando una malla está en pantalla, la GPU dedica tiempo a renderizar todos los triángulos de la malla, por pequeños que sean. En los juegos en los que la cámara o los activos pueden moverse, esto suele crear una situación en la que se pueden gastar muchos recursos de la GPU renderizando triángulos de mallas que son demasiado pequeños para verse en el fotograma. Para ello, utilice las mallas de nivel de detalle (LOD). Esto permite a tu juego aprovechar mallas menos complejas a medida que la cámara se aleja de los activos, lo que disminuye la complejidad de la malla que la GPU debe renderizar y reduce el recuento de vértices por fotograma, dando triángulos más grandes al procesamiento de píxeles. Esto no sólo mejora la eficacia, sino que mantiene intacta la integridad artística de la escena.

Mallas LOD
Mallas LOD

Para obtener más consejos sobre la optimización de recursos, consulta las guías para artistas de juegos de ARM.

Atlas de texturas

Si sabe que en una misma escena se van a utilizar materiales con las mismas propiedades, puede agruparlos por lotes. Combinar sus datos de textura en un único atlas de textura, lo que ahorrará llamadas de dibujo al dibujarlos de una sola vez, y resultará en una huella más pequeña cuando se comprima, en comparación con múltiples archivos separados.

Precisión Float vs Half Shader

Cuando escribas tus propios shaders personalizados, o utilices Shader Graph, puedes decidir qué precisión utilizar: float o half. Elegir la mitad, siempre que sea posible, hará que los sombreadores tengan un mayor rendimiento, pero recuerda que probablemente tendrás que utilizar float para cualquier cosa que tenga que ver con posiciones en el espacio del mundo o cálculos de profundidad.

Ajuste de la precisión a la mitad en Shader Graph
Ajuste de la precisión a la mitad en Shader Graph
Funciones integradas frente a Post Processing V2

Cuando empiece a planificar los efectos de postprocesado de su proyecto, tiene dos opciones: el conjunto de funciones integradas heredadas o el nuevo conjunto de funciones Post Processing v2. A continuación, puedes ver el juego utilizando el conjunto de funciones integradas.

GPU Profiler sin Post Processing v2
GPU Profiler sin Post Processing v2

Cada 3-4 fotogramas, vemos un pico en V-Sync, donde el sistema está esperando el fotograma para renderizar. Esto hace que el juego caiga por debajo de los 30 FPS, de forma constante, y desperdicia energía en el dispositivo. Aquí, sin embargo, puedes ver los datos del perfilador del juego utilizando los mismos efectos, esta vez, con el conjunto de características Post Processing v2.

GPU Profiler con Post Processing v2
GPU Profiler con Post Processing v2

Este gráfico de perfiles es mucho mejor, ya que Post Processing v2 está optimizado para funcionar en hardware móvil. Utilícelo en su proyecto para obtener el mejor rendimiento del postprocesado.

Posproceso de efectos

Añadir efectos de posprocesamiento a tu juego puede dar un toque de refinamiento y profundidad visual a tu proyecto. Pero también es importante equilibrar estos efectos con el rendimiento. Al fin y al cabo, estos efectos pueden salir caros. Apagarlos en los dispositivos de gran consumo puede ahorrar mucha energía y evitar que el dispositivo se caliente en las manos de los jugadores.

Una vez aplicadas las demás optimizaciones, aún se observaban picos en algunas zonas. Usando la búsqueda binaria, activando y desactivando cosas, finalmente localizamos dos cosas: Una era la pila de postprocesado que se utilizaba. Esto ayudó a reducir el tiempo total, pero la velocidad de fotogramas se estabilizó en cuanto desactivamos el antialiasing, hasta el punto de que parte del posprocesamiento pudo seguir funcionando, incluso en los dispositivos de especificaciones más bajas que utilizamos para las pruebas.

GPU Profiler - Ejemplo optimizado
GPU Profiler - Ejemplo optimizado

Tras optimizar el juego, volvimos a pasarlo por ARM Mobile Studio para buscar diferencias. El informe del Asesor de Rendimiento muestra ahora que hemos alcanzado una media de 28,9 FPS (antes 17), y reducido el límite general de fragmentos. La actividad de los fragmentos sigue siendo alta en algunas secciones del juego, así que aún nos queda trabajo por hacer, pero con buenos datos que guíen nuestra investigación, deberíamos poder optimizar estas secciones para mejorar aún más el rendimiento.

Arm Mobile Studio - Resumen de la captura
Arm Mobile Studio - Resumen de la captura

El número de vértices por fotograma está ahora muy por debajo de nuestro presupuesto de 500.000, y se pueden ver caídas regulares cuando se destruyen los PNJ enemigos.

ARM Mobile Studio - Vértices por fotograma
ARM Mobile Studio - Vértices por fotograma

El uso de la geometría y el culling es ahora mucho más eficiente, con el número de primitivas visibles en un porcentaje mucho más saludable del número de primitivas de entrada. La prueba de encaramiento es responsable de alrededor del 50% de las primitivas eliminadas, como era de esperar, y las eliminadas por la prueba de muestra es inferior al 10%, lo que demuestra que hemos reducido el número de triángulos muy pequeños.

Uso de la geometría Mali y tasa de sacrificio
Uso de la geometría Mali y tasa de sacrificio
En resumen

Utilizando el Profiler y el Frame Debugger de Unity, junto con ARM Mobile Studio, hemos podido descubrir múltiples formas de mejorar el rendimiento y reducir la presión sobre la CPU y la GPU en un dispositivo móvil. Algunos de los problemas que descubrimos podrían evitarse en futuros títulos, ciñéndose a una serie de buenas prácticas en materia de contenidos.

Por supuesto, no queremos que las optimizaciones reduzcan la calidad de los efectos visuales en pantalla. Este es el aspecto de la versión optimizada del juego junto a la versión original.

Pruebas proactivas de rendimiento

Las pruebas de rendimiento suelen realizarse bastante tarde en el ciclo de desarrollo. Es estupendo encontrar nuevas oportunidades de optimización, pero ¿y si no hay tiempo para solucionar los problemas antes de la fecha límite de publicación? Es mucho más práctico diseñar el contenido de forma óptima para empezar. Puede ser útil establecer presupuestos de contenido en torno a la complejidad de la malla, la complejidad del sombreado y la compresión de texturas, para dar a su equipo la mejor oportunidad de diseñar eficientemente para móviles. He aquí algunos recursos que podrían ayudar a su equipo:

Una vez que sepa que la mayor parte de su aplicación y sus activos siguen un conjunto de buenas prácticas, podrá realizar pruebas de rendimiento periódicas a lo largo de su ciclo de desarrollo, para detectar cualquier problema con la suficiente antelación para solucionarlo.

Los equipos que utilizan un sistema de integración continua pueden aprovechar las pruebas de rendimiento automatizadas, disponibles con la edición profesional de ARM Mobile Studio. Esta edición puede ejecutarse en varios dispositivos de una granja de dispositivos y elimina las molestias de la creación manual de perfiles. Los datos notificados pueden incluso introducirse en cualquier base de datos compatible con JSON, de modo que pueda crear paneles visuales y alertas para supervisar cómo cambia el rendimiento a lo largo del tiempo y detectar antes los problemas.

¿Nunca ha probado las herramientas de creación de perfiles?

El perfilador integrado de Unity es un buen punto de partida. Lea sobre cómo perfilar su aplicación en la documentación de Unity. O explore el depurador de fotogramas, que le permite investigar cómo se construye un fotograma individual.

Descargue Arm Mobile Studio de forma gratuita desde el sitio web para desarrolladores de ARM y consulte las guías de inicio para Performance Advisor, Streamline, Mali Offline Compiler y Graphics Analyzer, para empezar a trabajar rápidamente.

Contáctanos

Para obtener ayuda adicional sobre la creación de perfiles con el Perfilador y el Depurador de Fotogramas de Unity, no dude en hacer preguntas en nuestro foro.

Para obtener más ayuda mientras trabajas con dispositivos Mali o Arm Mobile Studio, visita el foro de gráficos y juegos de ARM, donde podrás hacer preguntas y Arm estará encantado de ayudarte.