En este documento, se muestra cómo optimizar el rendimiento del juego con herramientas para identificar y resolver los cuellos de botella de la CPU y la GPU.
Optimización de la CPU
Si el análisis muestra que el juego está limitado por la CPU, es fundamental realizar una investigación más detallada. Para ello, es necesario identificar los subprocesos o las APIs específicos que causan cuellos de botella y reducen los FPS.
Por lo general, una solución universal no es eficaz para la optimización de la CPU. En su lugar, debes identificar la carga de trabajo más exigente según el juego o la escena y, luego, optimizar la lógica y las funciones pertinentes.
Herramientas de registro de tiempo del motor de juego
Las siguientes herramientas pueden ayudarte con este análisis:
Estadísticas irreales
En los proyectos de Unreal Engine, la herramienta Unreal Insight facilita el análisis de la información de registro de tiempo para los subprocesos individuales que componen un fotograma.
A modo de ilustración, el GameThread suele utilizar la mayor proporción de tiempo de CPU, lo que se atribuye principalmente al tiempo de tick. Además, una parte importante del tiempo de ticks se consume en tareas asociadas a FActorComponentTickFunction.
Para optimizar FActorComponentTick, es fundamental excluir los cálculos y aplicar la eliminación de personajes y objetos que se encuentran fuera del campo de visión de la cámara. Además, aprovechar las animaciones basadas en el LOD (nivel de detalle) puede generar más mejoras en el rendimiento.
Unity Profiler (Unity)
El análisis con Unity Profiler revela que el subproceso principal consume más de 45 ms, y PostLateUpdate.FinishFrameRendering ocupa 16.23 ms, lo que la convierte en la operación que requiere más tiempo. Dentro de este, se observan varias invocaciones de Inl_RenderCameraStack. Es recomendable determinar la necesidad de las cámaras habilitadas y optimizarlas según corresponda.
Herramientas de generación de perfiles a nivel del sistema
Usa las siguientes herramientas de generación de perfiles:
Perfetto
Con el registro de Perfetto, puedes determinar las asignaciones de núcleos de CPU y los detalles de ejecución de cada subproceso en un dispositivo con Android. Esto te permite identificar cuellos de botella en el rendimiento analizando los datos de ejecución de subprocesos.
Caso de sobrecarga de CPU
El registro indica que la carga de trabajo en GameThread y RenderThread está causando demoras en QueuePresent de RHI Thread, lo que genera una situación vinculada a la CPU, según VSync.
Caso de sobrecarga de GPU
El registro indica que la finalización de la GPU en sí supera los 25 ms, lo que significa una situación vinculada a la GPU.
Simpleperf
Para identificar las funciones con el mayor uso actual de CPU, se puede utilizar simpleperf. Para obtener resultados óptimos, se recomienda ordenar estas funciones para priorizar y abordar primero las que tienen el mayor uso.
Simpleperf te ayuda a examinar datos sobre las funciones que usan la mayor cantidad de tiempo de CPU. Para optimizar el uso de la CPU, comienza con las funciones que usan más CPU. En este ejemplo, USkeletalMeshComponent, que se asocia con la animación en ActorComponentTickFunctions, usa la mayor cantidad de CPU.
Optimización de la GPU
Si el análisis muestra que el juego está vinculado a la GPU, es fundamental realizar una investigación adicional. Esto requiere el uso de diversas herramientas y técnicas para la optimización y el análisis de la GPU.
Para optimizar la GPU, usa un depurador de fotogramas para analizar la canalización de renderización y las llamadas de dibujo de cada escena. Además, debes comprender a fondo la arquitectura de la GPU y el comportamiento de la canalización para identificar las operaciones innecesarias o las áreas que se pueden optimizar.
En las siguientes secciones, se explican los métodos y las herramientas para la optimización de la GPU.
Elimina los RenderPasses innecesarios
Para mejorar el rendimiento de la renderización y reducir la carga de trabajo de la GPU, elimina los pases de renderización innecesarios. Esto incluye cualquier pase de renderización que no tenga llamadas de dibujo o cuyo resultado no se use en el fotograma final.
Usa un depurador de GPU, como RenderDoc, para analizar la canalización de renderización e identificar oportunidades de optimización.
No hay llamadas de dibujo: Verifica si el pase de renderización incluye alguna llamada de dibujo. Si no tiene llamadas de dibujo, quita el pase.
Salida sin usar: Verifica si los pases posteriores acceden a las salidas de los pases de renderización o las muestran, por ejemplo, el color o la profundidad. Si no es así, quita el pase.
Pases combinables: Identifica los pases que puedes combinar:
- El mismo búfer de fotogramas o los mismos archivos adjuntos
- Operaciones de carga o almacenamiento compatibles
- No hay barreras de dependencia entre ellas
Minimiza las operaciones de carga o almacenamiento
Las operaciones de carga o almacenamiento requieren muchos recursos porque usan mucha memoria.
Minimiza las operaciones de carga y almacenamiento innecesarias. Realiza estas acciones solo cuando se requieran archivos adjuntos dentro de un RenderPass. De lo contrario, reemplázalas por operaciones Clear o Don't care para reducir la sobrecarga.
Optimización
Usa un depurador de GPU, como RenderDoc, para analizar la canalización de renderización e identificar las siguientes oportunidades de optimización:
Carga: Si un archivo adjunto de pase de renderización no usa datos de un pase o archivo adjunto anterior, no es necesaria una operación de carga. En estos casos, usar
Don't careoClearpuede reducir la sobrecarga.Almacenamiento: Si no se usa un archivo adjunto de pase de renderización después del pase de renderización actual, la operación de almacenamiento es innecesaria. En esos casos, usa
Don't careoClear.Reemplazar: Determina si la configuración actual de carga o almacenamiento se puede reemplazar por
ClearoDon't Caresin afectar el fotograma final.
Evita el descarte para habilitar Early-Z
Early-Z mejora el rendimiento en plataformas móviles. Sin embargo, una instrucción discard dentro de un sombreador inhabilita automáticamente Early-Z. Si la instrucción discard no es esencial, quítala.
Aceleración de Early-Z
Esta optimización reduce significativamente las operaciones del sombreador de fragmentos y mejora el rendimiento de la GPU.
Early-Z Pruebas de profundidad y estencil
Optimización
Usa un depurador de GPU, como RenderDoc, para analizar la canalización de renderización e identificar las siguientes oportunidades de optimización:
Uso de
discarden sombreadores de fragmentos: La palabra clavediscardevita que la GPU realice pruebas de profundidad anticipadas, ya que la visibilidad del fragmento no se conoce de antemano.Modificación de
gl_FragDepth: La modificación dinámica degl_FragDepthcambia la profundidad de un fragmento, lo que inhabilita la optimización de Early-Z porque la profundidad final se desconoce antes del procesamiento del fragmento.Habilitado de alfa a cobertura: Cuando se habilita de alfa a cobertura (a menudo se usa en la renderización de MSAA), la cobertura de fragmentos depende de los valores alfa. Esto puede retrasar las pruebas de profundidad y, también, inhabilitar Early-Z.
Optimiza el formato de la textura
La selección óptima del formato de textura reduce el consumo de memoria, mejora la eficiencia del ancho de banda y optimiza el rendimiento del procesamiento. El uso de formatos con una precisión excesivamente alta puede desperdiciar recursos de GPU sin proporcionar ventajas visuales.
Optimización
Usa un depurador de GPU, como RenderDoc, para analizar la canalización de renderización e identificar las siguientes oportunidades de optimización:
- Usa
D24S8en lugar deD32S8para los búferes de profundidad y estencil: El uso deD24S8para los búferes de profundidad y estencil reduce el consumo de memoria en un 20% en comparación conD32S8, con poca o ninguna diferencia notable en la calidad visual en la mayoría de las aplicaciones. - Usa la compresión
ASTCpara las texturas de color: La compresiónASTCreduce significativamente el uso de memoria de textura (hasta 8 veces en comparación con los formatos sin comprimir) y, al mismo tiempo, conserva una alta calidad visual. - Usa formatos de medio punto flotante en lugar de punto flotante completo: Usa
R16FoRG16Fpara reducir el ancho de banda de la memoria y el consumo de almacenamiento. Estos formatos son adecuados para los búferes de posprocesamiento.
Optimiza la complejidad de la geometría
Minimizar la complejidad geométrica mejora el rendimiento de la renderización, en especial en dispositivos móviles con capacidades de GPU limitadas. Esto implica usar una cantidad reducida de vértices y triángulos, consolidar objetos para disminuir las llamadas de dibujo y eliminar la geometría innecesaria o que no se renderiza. Las técnicas como la simplificación de la malla, el nivel de detalle (LOD) y el descarte por frustum o por oclusión pueden reducir significativamente la carga de trabajo de la GPU y aumentar la velocidad de fotogramas.
Optimización
Usa herramientas de generación de perfiles y depuradores de GPU, como RenderDoc, Android GPU Inspector o cualquier otro analizador de rendimiento, para identificar cuellos de botella relacionados con la geometría.
Reduce Triangle Count: Minimiza el uso de polígonos, en especial para objetos pequeños o distantes.
Usa el nivel de detalle (LOD): Según la distancia de la cámara, se usan automáticamente mallas más simples.
Merge Small Meshes: Consolida objetos estáticos para reducir las llamadas de dibujo y la sobrecarga de la CPU.
Frustum and Occlusion Culling: Evita renderizar objetos que están fuera de la vista o que están ocultos por otros elementos.
Quita los archivos adjuntos innecesarios
Los archivos adjuntos de pases de renderización (por ejemplo, color, profundidad, esténcil) consumen ancho de banda de memoria y recursos de GPU, incluso si no se usan. Quitar los archivos adjuntos innecesarios o redundantes mejora el rendimiento y reduce el consumo de energía, en especial en las plataformas para dispositivos móviles.
Optimización
Usa herramientas de generación de perfiles y depuradores de GPU, como RenderDoc, Android GPU Inspector o cualquier otro analizador de rendimiento, para identificar cuellos de botella relacionados con la geometría.
- Verifica el uso real: ¿Hay llamadas de dibujo o sombreadores que escriban en el archivo adjunto o lean desde él?
- Analiza el resultado del fotograma: Usa
RenderDoco utilidades comparables para determinar si el adjunto contribuye a la imagen final. - Considera usar archivos adjuntos transitorios o ficticios: Los archivos adjuntos transitorios o una operación de almacenamiento "Don't Care" se deben usar para los datos temporales que no requieren almacenamiento persistente.
Optimiza la precisión del sombreador
Usar una precisión excesivamente alta (por ejemplo, highp en lugar de mediump o lowp) en los sombreadores aumenta la carga de trabajo de la GPU, el consumo de energía y la presión de registros, en especial en las GPU para dispositivos móviles. Si usas la precisión adecuada más baja para las variables (por ejemplo, posiciones, colores, UV), puedes mejorar el rendimiento sin un impacto visual perceptible.
Optimización
Usa herramientas de generación de perfiles y depuradores de GPU, como RenderDoc, Android GPU Inspector o cualquier otro analizador de rendimiento, para identificar cuellos de botella relacionados con la geometría.
Revisa el código del sombreador: Evalúa las variables del sombreador y confirma que se use una alta precisión solo cuando sea necesario, como para los cálculos de profundidad o de espacio de pantalla. Usa una precisión media o baja para los colores, las coordenadas UV o los valores que no requieren una precisión alta.
Usa depuradores de GPU: Las utilidades de diagnóstico, como RenderDoc o los generadores de perfiles de GPU para dispositivos móviles (por ejemplo, AGI, Mali/GPU Inspector), identifican el uso elevado de registros o las detenciones del sombreador asociadas con problemas de precisión.
Habilita el descarte de caras posteriores
A menudo, no es necesario renderizar los triángulos que no están orientados hacia la cámara (caras posteriores) para los objetos sólidos.
Optimización
El uso de VK_CULL_MODE_NONE puede afectar negativamente el rendimiento, ya que obliga a la GPU a renderizar las caras frontales y posteriores, lo que aumenta la carga de trabajo de renderización.
Cómo minimizar la superposición en escenas de la IU
Elimina las llamadas de dibujo y los pases de renderización innecesarios, en especial en las escenas de la IU, para mejorar el rendimiento de la renderización y reducir la carga de trabajo de la GPU. Por ejemplo, en una escena de IU en la que se renderiza todo el mundo antes de superponer la IU en la pantalla, el renderizado del mundo se vuelve redundante.
Optimización
Usa un depurador de GPU, como RenderDoc, para analizar la canalización de renderización e identificar las siguientes oportunidades de optimización:
- Verifica la ausencia de sobrepintado superfluo. En los contextos de la interfaz de usuario, en los que se puede renderizar toda la pantalla, confirma que los pases de renderización anteriores no se hayan sobregirado innecesariamente.
- Habilita las pruebas de profundidad y el descarte para optimizar el rendimiento.
- Considera el orden de renderización de adelante hacia atrás.