Mejoras en los tiempos de compilación de los sombreadores y en el uso de la memoria en 2021 LTS

DAMIAN NACHMAN / UNITY TECHNOLOGIESSenior Technical Product Manager
Dec 28, 2022|13 minutos
Mejoras en los tiempos de compilación de los sombreadores y en el uso de la memoria en 2021 LTS
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.

A medida que el conjunto de funciones disponibles del Scriptable Render Pipeline (SRP) de Unity sigue creciendo, también lo hace la cantidad de variantes de sombreadores que se procesan y compilan en el momento de la compilación. Junto con la compatibilidad permanente con API gráficas adicionales y una selección cada vez mayor de plataformas de destino, las mejoras del SRP siguen ampliándose.

Los sombreadores se compilan y almacenan en caché tras una compilación inicial ("limpia"), lo que acelera las posteriores compilaciones incrementales ("calientes"). Aunque las compilaciones limpias suelen ser las que más tardan, los largos tiempos de compilación en caliente pueden ser un punto problemático habitual durante el desarrollo y la iteración de los proyectos.

Procesamiento y compilación de variantes de sombreado durante la compilación del proyecto

Para solucionar este problema, el equipo de gestión de sombreadores de Unity ha trabajado duro para ofrecer soluciones significativas y escalables. Esto ha dado lugar a una reducción significativa de los tiempos de compilación de sombreadores y del uso de memoria en tiempo de ejecución para los proyectos creados con Unity 2021 LTS y versiones posteriores.

Para obtener más información sobre estas nuevas optimizaciones, incluidas las versiones afectadas, los backports y las cifras de nuestras pruebas internas, vaya directamente a las secciones dedicadas al prefiltrado de variantes de sombreado y a la carga dinámica de sombreadores. Al final de esta entrada de blog, también abordamos nuestros planes futuros para refinar aún más la gestión de variantes de sombreado en su conjunto, a través de la creación de proyectos, la compilación y el tiempo de ejecución.

Antes de profundizar en las interesantes mejoras introducidas en el sistema de sombreado de Unity, vamos a aprovechar la oportunidad para repasar rápidamente los conceptos de compilación condicional de sombreado, variantes de sombreado y eliminación de variantes de sombreado.

Funciones de sombreado condicional

Las funciones de sombreado condicional permiten a los desarrolladores y artistas controlar y alterar cómodamente la funcionalidad de un sombreador mediante scripts, ajustes de material, así como ajustes de proyecto y gráficos. Estas características condicionales sirven para simplificar la creación de proyectos, permitiendo que los proyectos se amplíen eficientemente al minimizar el número de sombreadores que tendrás que crear y mantener.

Una función de material Clear Coat activada por el artista en el momento de la creación, mediante la activación de una palabra clave shader_feature.

Las funciones de sombreado condicional pueden implementarse de diferentes maneras:

  • Bifurcación estática (en tiempo de compilación)
  • Compilación de variantes de sombreado
  • Bifurcación dinámica (en tiempo de ejecución)

Aunque la bifurcación estática evita la sobrecarga de ejecución del sombreador relacionada con la bifurcación en tiempo de ejecución, se evalúa y bloquea en tiempo de compilación y no proporciona control en tiempo de ejecución. La compilación de variantes de sombreado, por su parte, es una forma de bifurcación estática que proporciona un control adicional en tiempo de ejecución. Esto funciona compilando un único programa de sombreado (variante) para cada combinación posible de ramas estáticas, con el fin de mantener un rendimiento óptimo de la GPU en tiempo de ejecución.

Estas variantes se crean declarando y evaluando condicionalmente la funcionalidad de los sombreadores mediante las palabras clave shader_feature y shader multi_compile. Las variantes de sombreado correctas se cargan en tiempo de ejecución en función de las palabras clave activas y la configuración de tiempo de ejecución. Declarar y evaluar palabras clave de sombreado adicionales puede aumentar el tiempo de compilación, el tamaño del archivo y el uso de memoria en tiempo de ejecución.

Al mismo tiempo, la bifurcación dinámica (basada en la uniformidad) evita por completo la sobrecarga de la compilación de variantes de sombreado, lo que se traduce en compilaciones más rápidas y una reducción tanto del tamaño de los archivos como del uso de memoria. Esto puede facilitar y acelerar la iteración durante el desarrollo.

Por otro lado, la bifurcación dinámica puede tener un fuerte impacto en el rendimiento de la ejecución del sombreador en función de la complejidad del sombreador y del dispositivo de destino. Las ramas asimétricas, en las que un lado de la rama es mucho más complejo que el otro, pueden afectar negativamente al rendimiento. Esto se debe a que la ejecución de una ruta más sencilla puede seguir incurriendo en las penalizaciones de rendimiento de la ruta más compleja.

Cuando introduzcas funciones de sombreado condicional en tus propios sombreadores, debes tener en cuenta estos enfoques y ventajas y desventajas. Para obtener información más detallada, consulte la documentación sobre condicionales de sombreado, bifurcación de sombreado y variantes de sombreado.

Eliminación de variantes de sombreado

Para mitigar el aumento del tiempo de procesamiento y compilación del sombreador, se utiliza la eliminación de variantes del sombreador. Su objetivo es excluir de la compilación las variantes de sombreado innecesarias basándose en factores como:

  • Materiales incluidos y palabras clave habilitadas
  • Configuración del proyecto y del proceso de renderizado
  • Eliminación mediante script

Al enumerar las variantes de los sombreadores, el editor filtrará automáticamente cualquier palabra clave declarada con shader_feature que no esté activada por los materiales referenciados e incluidos en la compilación. Como resultado, estas palabras clave no generarán variantes adicionales.

Por ejemplo, si la propiedad de material Clear Coat no está activada por ningún material que utilice el sombreador URP Complex Lit, todas las variantes del sombreador que implementen la funcionalidad Clear Coat se eliminarán de forma segura en el momento de la compilación.

Mientras tanto, las palabras clave multi_compile permiten a los desarrolladores y jugadores controlar libremente la funcionalidad del sombreador en tiempo de ejecución basándose en los ajustes y scripts disponibles del jugador. La otra cara de la moneda es que estas palabras clave no pueden ser eliminadas automáticamente por el Editor en la misma medida que las palabras clave shader_feature. Por eso suelen producir un mayor número de variantes.

Scriptable stripping es una API de C# que permite excluir variantes de shaders de la compilación en tiempo de compilación mediante palabras clave y combinaciones no necesarias en tiempo de ejecución. Los conductos de renderizado utilizan la eliminación mediante scripts para eliminar las variantes innecesarias de acuerdo con la configuración del conducto de renderizado del proyecto y los recursos de calidad incluidos en la compilación.

Baja calidad Alta calidad Multiplicador de variantes Luz principal/Sombras fundidas: Apagado Encendido 2x Luz principal/Sombras proyectadas: Encendido Encendido 1x Luz principal/Sombras proyectadas: Apagado Apagado 1x

Para maximizar los efectos de la eliminación de variantes de sombreado del editor, recomendamos desactivar todas las funciones relacionadas con los gráficos y los ajustes del conducto de renderizado que no se utilicen en tiempo de ejecución. Consulte la documentación oficial para obtener más información sobre la eliminación de variantes de sombreado.

Prefiltrado de variantes de sombreado

La eliminación de variantes de sombreado reduce en gran medida la cantidad de variantes de sombreado compiladas, en función de factores como los activos de calidad del conducto de renderizado en la compilación. Sin embargo, actualmente la eliminación se realiza al final de la etapa de procesamiento del sombreador. Enumerar todas las variantes posibles puede llevar mucho tiempo, independientemente de la compilación.

Con el fin de reducir los tiempos de procesamiento de variantes de sombreado (y de creación de proyectos), estamos introduciendo una importante optimización en la eliminación de variantes de sombreado integrada en el motor. Con el prefiltrado de variantes de sombreado, se reducen significativamente los tiempos de construcción tanto en limpio como en caliente.

La optimización funciona introduciendo la exclusión anticipada de las palabras clave multi_compile, de acuerdo con los Atributos de Prefiltrado controlados por la configuración de Render Pipeline. Esto reduce la cantidad de variantes que se enumeran para su posible eliminación y compilación, lo que a su vez reduce el tiempo de procesamiento de los sombreadores, con una reducción de los tiempos de compilación en caliente de hasta el 90% en los ejemplos más drásticos.

El prefiltrado de variantes de sombreado apareció por primera vez en 2023.1.0a14, y se ha actualizado en 2022.2.0b15 y 2021.3.15f1.

Reducción del tiempo de procesamiento de sombreado para construcciones de proyectos en caliente en URP Boat Attack
Reducción del tiempo de procesamiento de los sombreadores para las construcciones de proyectos cálidos en URP Terrain Demo.

El prefiltrado de variantes también ayuda a reducir los tiempos de construcción inicial/limpieza aplicando el mismo principio.

Reducción del tiempo de procesamiento de los sombreadores para las construcciones de proyectos limpios en la demostración de terreno URP.
Reducción del tiempo de procesamiento de los sombreadores para las construcciones de proyectos cálidos en URP Terrain Demo.
Carga dinámica de sombreadores

Históricamente, el tiempo de ejecución de Unity cargaba por adelantado todos los objetos de sombreado del disco a la memoria de la CPU durante la carga de la escena y los recursos. En la mayoría de los casos, un proyecto y una escena construidos incluyen muchas más variantes de shaders de las necesarias en un momento dado durante el tiempo de ejecución de la aplicación. Para los proyectos que utilizan una gran cantidad de sombreadores, esto a menudo resulta en un alto uso de memoria de sombreadores en tiempo de ejecución.

La carga dinámica de sombreadores resuelve el problema proporcionando al usuario un mayor control sobre el comportamiento de la carga de sombreadores y el uso de la memoria. Esta optimización facilita el flujo de trozos de datos de sombreado a la memoria, así como el desalojo de los datos de sombreado que ya no se necesitan en tiempo de ejecución, basándose en un presupuesto de memoria controlado por el usuario. Esto permite reducir significativamente el uso de memoria de sombreado en plataformas con presupuestos de memoria limitados.

Ahora se puede acceder a los nuevos ajustes de carga de variantes de sombreado desde los ajustes del reproductor del editor. Utilízalos para anular el número máximo de shader chunks cargados y el tamaño de chunk por shader (MB).

Editor > Project Settings > Player > Shader Variant Loading Settings

Con la siguiente API de C# ahora disponible, puede anular la configuración de carga de variantes de sombreado utilizando scripts del editor, como:

También puede anular la cantidad máxima de shader chunks cargados en tiempo de ejecución utilizando la API de C# mediante Shader.maximumChunksOverride. Esto le permite anular el presupuesto de memoria de sombreado basado en factores tales como el sistema total disponible y la memoria gráfica consultada en tiempo de ejecución.

La carga dinámica de sombreadores aterrizó en 2023.1.0a11 y ha sido trasladada a 2022.2.0b10, 2022.1.21f1 y 2021.3.12f. En el caso del Ataque al Barco de Universal Render Pipeline (URP), observamos una reducción del 78,8% en el uso de memoria en tiempo de ejecución para shaders, de 315 MiB (por defecto) a 66,8 MiB (carga dinámica). Puede obtener más información sobre esta optimización en el anuncio oficial.

La carga dinámica de shaders utilizada en URP Boat Attack reduce en un 78,8% el uso de memoria de shaders en tiempo de ejecución.
¿Qué sucede después?

Además de los cambios críticos mencionados, estamos trabajando para mejorar la generación y eliminación de variantes de sombreado de Universal Render Pipeline. También estamos investigando mejoras adicionales en la gestión de variantes de sombreado de Unity en general. El objetivo final es facilitar el aumento de las funciones del motor, garantizando al mismo tiempo una sobrecarga mínima en la creación de sombreadores y en el tiempo de ejecución.

Algunas de nuestras investigaciones en curso tienen que ver con la deduplicación de recursos de sombreado entre variantes similares, así como con mejoras generales de las palabras clave de sombreado y las API de recopilación de variantes de sombreado. El objetivo es ofrecer más flexibilidad y control sobre el procesamiento de variantes de sombreado y el rendimiento en tiempo de ejecución.

De cara al futuro, también estamos explorando la posibilidad de crear herramientas en el editor para el rastreo y el análisis de variantes de sombreado con el fin de proporcionar los siguientes detalles sobre el uso de variantes de sombreado:

  • ¿Qué sombreadores y palabras clave producen más variantes?
  • ¿Qué variantes se compilan pero no se utilizan en tiempo de ejecución?
  • ¿Qué variantes se eliminan pero se solicitan en tiempo de ejecución?

Sus comentarios han sido decisivos hasta ahora, ya que nos ayudan a priorizar las soluciones más significativas. Consulte nuestra hoja de ruta pública para votar las funciones que mejor se adapten a sus necesidades. Si hay cambios adicionales que te gustaría ver, no dudes en enviar una solicitud de características, o ponte en contacto con el equipo directamente en este foro de shaders.