Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Optimización de GPU

Introducción

La demanda de nuevas características y avances gráficos casi garantiza que te encontrarás con cuellos de botella gráficos. Algunos de estos cuellos de botella pueden ocurrir en el lado de la CPU, por ejemplo, en los cálculos dentro del motor Godot para preparar objetos para su renderización. Los cuellos de botella también pueden ocurrir en la CPU en el controlador gráfico, que ordena las instrucciones para pasar al GPU, y en la transferencia de estas instrucciones. Y finalmente, también pueden ocurrir cuellos de botella en el propio GPU.

Los cuellos de botella en el renderizado ocurren de manera muy específica según el hardware. Las GPU móviles en particular pueden tener dificultades con escenas que se ejecutan fácilmente en computadoras de escritorio.

Comprender e investigar los cuellos de botella de la GPU es ligeramente diferente a la situación en la CPU. Esto se debe a que, a menudo, solo puedes cambiar el rendimiento de manera indirecta al modificar las instrucciones que le das a la GPU. Además, puede ser más difícil tomar medidas. En muchos casos, la única forma de medir el rendimiento es examinando los cambios en el tiempo empleado en renderizar cada cuadro.

Llamadas de dibujo, cambios de estado y APIs

Nota

La siguiente sección no es relevante para los usuarios finales, pero es útil para proporcionar información de fondo que es relevante en secciones posteriores.

Godot sends instructions to the GPU via a graphics API (Vulkan, OpenGL, OpenGL ES or WebGL). The communication and driver activity involved can be quite costly, especially in OpenGL, OpenGL ES and WebGL. If we can provide these instructions in a way that is preferred by the driver and GPU, we can greatly increase performance.

Casi todos los comandos de la API en OpenGL requieren cierta cantidad de validación para asegurarse de que el GPU esté en el estado correcto. Incluso los comandos aparentemente simples pueden generar una serie de tareas de mantenimiento en segundo plano. Por lo tanto, el objetivo es reducir al mínimo estas instrucciones y agrupar objetos similares tanto como sea posible para que puedan ser renderizados juntos o con el menor número posible de cambios costosos de estado.

Procesamiento por lotes 2D

In 2D, the costs of treating each item individually can be prohibitively high - there can easily be thousands of them on the screen. This is why 2D batching is used with OpenGL-based rendering methods. Multiple similar items are grouped together and rendered in a batch, via a single draw call, rather than making a separate draw call for each item. In addition, this means state changes, material and texture changes can be kept to a minimum.

Vulkan-based rendering methods do not use 2D batching yet. Since draw calls are much cheaper with Vulkan compared to OpenGL, there is less of a need to have 2D batching (although it can still be beneficial in some cases).

Procesamiento por lotes 3D

In 3D, we still aim to minimize draw calls and state changes. However, it can be more difficult to batch together several objects into a single draw call. 3D meshes tend to comprise hundreds or thousands of triangles, and combining large meshes in real-time is prohibitively expensive. The costs of joining them quickly exceeds any benefits as the number of triangles grows per mesh. A much better alternative is to join meshes ahead of time (static meshes in relation to each other). This can be done by artists, or programmatically within Godot using an add-on.

También hay un costo asociado al agrupar objetos en 3D. Varios objetos renderizados como uno solo no pueden ser excluidos individualmente. Una ciudad completa que está fuera de la pantalla aún se renderizará si está unida a una única hoja de hierba que se encuentra en la pantalla. Por lo tanto, siempre debes tener en cuenta la ubicación y el culling (descarte) de los objetos al intentar agrupar objetos en 3D. A pesar de esto, los beneficios de unir objetos estáticos a menudo superan otras consideraciones, especialmente para un gran número de objetos distantes o de baja resolución.

Para obtener más información sobre optimizaciones específicas de 3D, consulte Optimizando las prestaciones en 3D.

Reuse shaders and materials

The Godot renderer is a little different to what is out there. It's designed to minimize GPU state changes as much as possible. StandardMaterial3D does a good job at reusing materials that need similar shaders. If custom shaders are used, make sure to reuse them as much as possible. Godot's priorities are:

  • Reutilización de materiales: Cuantos menos materiales diferentes haya en la escena, más rápido será el renderizado. Si una escena tiene una gran cantidad de objetos (en cientos o miles), intente reutilizar los materiales. En el peor de los casos, use atlas para disminuir la cantidad de cambios de textura.

  • Reusing Shaders: If materials can't be reused, at least try to reuse shaders. Note: shaders are automatically reused between StandardMaterial3Ds that share the same configuration (features that are enabled or disabled with a check box) even if they have different parameters.

If a scene has, for example, 20,000 objects with 20,000 different materials each, rendering will be slow. If the same scene has 20,000 objects, but only uses 100 materials, rendering will be much faster.

Costo de píxeles frente a costo de vértice

Es posible que haya escuchado que cuanto menor sea el número de polígonos en un modelo, más rápido se renderizará. Esto es realmente relativo y depende de muchos factores.

En una PC y una consola modernas, el costo de vértice es bajo. Las GPU originalmente solo representaban triángulos. Esto significaba que cada cuadro:

  1. Todos los vértices tuvieron que ser transformados por la CPU (incluido el recorte).

  2. Todos los vértices debían enviarse a la memoria de la GPU desde la RAM principal.

Nowadays, all this is handled inside the GPU, greatly increasing performance. 3D artists usually have the wrong feeling about polycount performance because 3D modeling software (such as Blender, 3ds Max, etc.) need to keep geometry in CPU memory for it to be edited, reducing actual performance. Game engines rely on the GPU more, so they can render many triangles much more efficiently.

En los dispositivos móviles, la historia es diferente. Las GPU de PC y consola son monstruos de fuerza bruta que pueden extraer tanta electricidad como necesiten de la red eléctrica. Las GPU móviles están limitadas a una batería diminuta, por lo que deben ser mucho más eficientes energéticamente.

Para ser más eficientes, las GPU móviles intentan evitar sobregiro. El sobregiro se produce cuando el mismo píxel en la pantalla se representa más de una vez. Imagina una ciudad con varios edificios. Las GPU no saben qué es visible y qué está oculto hasta que lo dibujan. Por ejemplo, se puede dibujar una casa y luego otra casa frente a ella (lo que significa que la renderización se realizó dos veces para el mismo píxel). A las GPU de PC normalmente no les importa mucho esto y simplemente lanzan más procesadores de píxeles al hardware para aumentar el rendimiento (lo que también aumenta el consumo de energía).

Usar más energía no es una opción en dispositivos móviles, por lo que los dispositivos móviles usan una técnica llamada renderizado basado en mosaicos que divide la pantalla en una cuadrícula. Cada celda mantiene la lista de triángulos dibujados en ella y los ordena por profundidad para minimizar sobredibujar. Esta técnica mejora el rendimiento y reduce el consumo de energía, pero afecta el rendimiento de los vértices. Como resultado, se pueden procesar menos vértices y triángulos para dibujar.

Además, el renderizado basado en mosaicos tiene problemas cuando hay objetos pequeños con mucha geometría dentro de una pequeña porción de la pantalla. Esto obliga a las GPU móviles a ejercer mucha presión sobre un mosaico de una sola pantalla, lo que disminuye considerablemente el rendimiento, ya que todas las demás celdas deben esperar a que se complete antes de mostrar el marco.

En resumen, no se preocupe por el recuento de vértices en dispositivos móviles, pero evite la concentración de vértices en partes pequeñas de la pantalla. Si un personaje, NPC, vehículo, etc.está lejos (lo que significa que parece pequeño), use un modelo de menor nivel de detalle (LOD). Incluso en las GPU de escritorio, es preferible evitar tener triángulos más pequeños que el tamaño de un píxel en la pantalla.

Presta atención al procesamiento adicional de vértices requerido al utilizar:

  • Skinning (animación esquelética)

  • Morphs (claves de forma)

Los sombreadores de píxeles/fragmentos y la tasa de llenado

In contrast to vertex processing, the costs of fragment (per-pixel) shading have increased dramatically over the years. Screen resolutions have increased: the area of a 4K screen is 8,294,400 pixels, versus 307,200 for an old 640×480 VGA screen. That is 27 times the area! Also, the complexity of fragment shaders has exploded. Physically-based rendering requires complex calculations for each fragment.

Puedes probar fácilmente si un proyecto tiene limitaciones de tasa de llenado. Desactiva la sincronización vertical (V-Sync) para evitar limitar los cuadros por segundo y luego compara los cuadros por segundo al ejecutar el proyecto en una ventana grande y en una ventana muy pequeña. También puedes beneficiarte de reducir el tamaño del mapa de sombras si estás utilizando sombras. Por lo general, encontrarás que la velocidad de cuadros por segundo aumenta considerablemente al utilizar una ventana pequeña, lo que indica que estás limitado en cierta medida por la tasa de llenado. Por otro lado, si hay poco o ningún aumento en la velocidad de cuadros por segundo, entonces tu cuello de botella se encuentra en otro lugar.

You can increase performance in a fill rate-limited project by reducing the amount of work the GPU has to do. You can do this by simplifying the shader (perhaps turn off expensive options if you are using a StandardMaterial3D), or reducing the number and size of textures used. Also, when using non-unshaded particles, consider forcing vertex shading in their material to decrease the shading cost.

Ver también

On supported hardware, Variable rate shading can be used to reduce shading processing costs without impacting the sharpness of edges on the final image.

Cuando te dirijas a dispositivos móviles, considera utilizar los shaders más simples que puedas permitirte razonablemente usar.

Leer texturas

Otro factor en los fragment shaders es el costo de leer las texturas. Leer texturas es una operación costosa, especialmente al leer de varias texturas en un solo fragment shader. Además, considera que el filtrado puede ralentizarlo aún más (filtrado trilineal entre mipmaps y promediando). Leer texturas también es costoso en términos de consumo de energía, lo cual es un gran problema en dispositivos móviles.

Si utilizas shaders de terceros o escribes tus propios shaders, intenta utilizar algoritmos que requieran la menor cantidad de lecturas de texturas posible.

Compresión de texturas

De forma predeterminada, Godot comprime las texturas de los modelos 3D cuando se importan mediante compresión de RAM de vídeo (VRAM). La compresión de la RAM de video no es tan eficiente en tamaño como PNG o JPG cuando se almacena, pero aumenta enormemente el rendimiento al dibujar texturas lo suficientemente grandes.

Esto se debe a que el objetivo principal de la compresión de texturas es la reducción del ancho de banda entre la memoria y la GPU.

En 3D, la forma de los objetos depende más de la geometría que de la textura, por lo que la compresión generalmente no se nota. En 2D, la compresión depende más de las formas dentro de las texturas, por lo que el resultado de la compresión es más visible.

A modo de advertencia, la mayoría de los dispositivos Android no admiten la compresión de texturas con transparencia (sólo opacas), así que tenlo en cuenta.

Nota

Incluso en entornos 3D, las texturas de "pixel art" deberían tener la compresión de VRAM desactivada, ya que esto afectará negativamente su apariencia sin mejorar significativamente el rendimiento debido a su baja resolución.

Postprocesamiento y sombras

Los efectos de posprocesamiento y las sombras también pueden ser costosos en términos de actividad de fragment shading. Siempre prueba el impacto de estos en diferentes hardware.

Reducir el tamaño de los shadowmaps puede aumentar el rendimiento, tanto en términos de escritura como de lectura de los shadowmaps. Además, la mejor manera de mejorar el rendimiento de las sombras es desactivar las sombras para tantas luces y objetos como sea posible. Las OmniLights/SpotLights más pequeñas o distantes a menudo pueden tener sus sombras desactivadas con un impacto visual mínimo.

Transparencia y mezcla

Los objetos transparentes presentan problemas particulares para la eficiencia del renderizado. Los objetos opacos (especialmente en 3D) pueden ser renderizados básicamente en cualquier orden y el Z-buffer se encargará de asegurar que solo los objetos más cercanos se sombreen. Sin embargo, los objetos transparentes o con mezcla son diferentes. En la mayoría de los casos, no pueden depender del Z-buffer y deben ser renderizados en "orden de pintor" (es decir, de atrás hacia adelante) para lucir correctamente.

Los objetos transparentes también son particularmente problemáticos en términos de fill rate, ya que cada elemento debe ser dibujado incluso si otros objetos transparentes se dibujarán encima más adelante.

Los objetos opacos no tienen que hacer esto. Por lo general, pueden aprovechar el Z-buffer al escribir en él solo en primer lugar, y luego realizar el fragment shader solo en el fragmento "ganador", es decir, en el objeto que se encuentra en primer plano en un píxel en particular.

La transparencia es particularmente costosa cuando se superponen varios objetos transparentes. Por lo general, es mejor utilizar áreas transparentes lo más pequeñas posible para minimizar los requisitos de fill rate, especialmente en dispositivos móviles, donde el fill rate es muy costoso. De hecho, en muchas situaciones, renderizar geometría opaca más compleja puede resultar más rápido que utilizar transparencia para "engañar".

Asesoramiento multiplataforma

Si tienes como objetivo lanzar en múltiples plataformas, realiza pruebas tempranas y frecuentes en todas tus plataformas, especialmente en dispositivos móviles. Desarrollar un juego en escritorio pero intentar realizar el port a móvil en el último momento es una receta para el desastre.

In general, you should design your game for the lowest common denominator, then add optional enhancements for more powerful platforms. For example, you may want to use the Compatibility rendering method for both desktop and mobile platforms where you target both.

Renderizadores móviles/tileados

Como se describió anteriormente, las GPUs en dispositivos móviles funcionan de manera dramáticamente diferente a las GPUs en computadoras de escritorio. La mayoría de los dispositivos móviles utilizan renderizadores de tipo tileado. Los renderizadores de tipo tileado dividen la pantalla en pequeños azulejos de tamaño regular que se ajustan en la memoria caché súper rápida, lo cual reduce la cantidad de operaciones de lectura/escritura en la memoria principal.

Sin embargo, existen algunas desventajas. El renderizado tileado puede hacer que ciertas técnicas sean mucho más complicadas y costosas de realizar. Los azulejos que dependen de los resultados del renderizado en diferentes azulejos o de que los resultados de operaciones anteriores se conserven pueden ser muy lentos. Ten mucho cuidado al probar el rendimiento de los shaders, las texturas del viewport y el procesamiento posterior.