Recomendaciones de rendimiento para Unity

Este artículo se basa en las recomendaciones de rendimiento para la realidad mixta, pero se centra en las mejoras específicas de Unity.

Recientemente lanzamos una aplicación denominada Quality Fundamentals que cubre problemas comunes de rendimiento, diseño y entorno y soluciones para aplicaciones de HoloLens 2. Esta aplicación es una gran demostración visual para el contenido que sigue.

El primer paso más importante para optimizar el rendimiento de las aplicaciones de realidad mixta en Unity es asegurarse de que está usando la configuración de entorno recomendada para Unity. En este artículo se incluye contenido con algunas de las configuraciones de escenas más importantes para crear aplicaciones de Mixed Reality de alto rendimiento. En la sección siguiente se resaltan algunas de estas configuraciones recomendadas.

Cómo generar perfiles con Unity

Unity proporciona el generador de perfiles de Unity integrado, que es un gran recurso para recopilar información valiosa sobre el rendimiento de su aplicación en particular. Aunque puede ejecutar el generador de perfiles en el editor, estas métricas no representan el entorno de tiempo de ejecución real, por lo que los resultados deben usarse con precaución. Genere perfiles remotos de la aplicación mientras se ejecuta en el dispositivo para obtener la información más precisa y procesable.

Unity proporciona una excelente documentación para:

  1. Cómo conectar el generador de perfiles de Unity a aplicaciones para UWP de forma remota
  2. Cómo diagnosticar eficazmente problemas de rendimiento con Unity Profiler

Generación de perfiles de GPU

Generador de perfiles de Unity

Con el Unity Profiler conectado y después de agregar el perfilador de GPU (consulte Agregar perfilador en la esquina superior derecha), se puede ver cuánto tiempo se pasa en la CPU & GPU respectivamente en el medio del perfilador. Esto permite al desarrollador obtener una aproximación rápida si su aplicación está limitada por CPU o GPU.

Unidad CPU vs GPU

Nota:

Para utilizar la generación de perfiles de GPU, debe deshabilitar los trabajos gráficos en la configuración del reproductor de Unity. Consulte el módulo GPU Usage Profiler de Unity para obtener más detalles.

Depurador de marcos de Unity

Frame Debugger de Unity también es una herramienta poderosa y perspicaz para usar. Le brinda una buena descripción general de lo que está haciendo la GPU en cada cuadro. Lo que hay que tener en cuenta son otros destinos de representación y los comandos blit para copiar entre ellos, ya que son costosos en HoloLens. Lo ideal es que no se usen destinos de representación fuera de la pantalla en HoloLens. Se agregan al habilitar funciones de renderizado costosas (por ejemplo, MSAA, HDR o efectos de pantalla completa como bloom) que se deben evitar.

Superposición de velocidad de fotogramas de HoloLens

La página Rendimiento del sistema del portal de dispositivos tiene un buen resumen del rendimiento de CPU y GPU del dispositivo. Puedes habilitar Mostrar contador de velocidad de fotogramas en auriculares y Mostrar gráfico de velocidad de fotogramas en auriculares. Estas opciones habilitan un contador de FPS y un gráfico, respectivamente, que te proporcionan información inmediata en cualquier aplicación en ejecución en el dispositivo.

PIX

PIX también se puede usar para perfilar aplicaciones de Unity. También hay instrucciones detalladas sobre cómo usar e instalar PIX para HoloLens 2. En una compilación de desarrollo, los mismos ámbitos que se ven en el depurador de marcos de Unity también se mostrarán en PIX y se pueden inspeccionar y perfilar con más detalle.

Nota:

Unity proporciona la capacidad de modificar fácilmente la resolución de destino de representación de la aplicación en tiempo de ejecución a través de la propiedad XRSettings.renderViewportScale . La imagen final que se presenta en el dispositivo tiene una resolución fija. La plataforma muestrea la salida de resolución más baja para crear una imagen de mayor resolución para su representación en pantallas.

UnityEngine.XR.XRSettings.renderViewportScale = 0.7f;

Recomendaciones de rendimiento de la CPU

El contenido siguiente cubre prácticas de rendimiento más detalladas, especialmente dirigidas al desarrollo de Unity & C#.

Referencias de caché

Se recomienda almacenar en caché las referencias a todos los componentes relevantes y GameObjects en la inicialización, ya que las llamadas de función repetidas, como GetComponent<, T>() y Camera.main , son más costosas en relación con el costo de memoria para almacenar un puntero. Camera.main solo usa FindGameObjectsWithTag() debajo, que busca costosamente en su gráfico de escenas un objeto de cámara con la etiqueta "MainCamera".

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    private Camera cam;
    private CustomComponent comp;

    void Start() 
    {
        cam = Camera.main;
        comp = GetComponent<CustomComponent>();
    }

    void Update()
    {
        // Good
        this.transform.position = cam.transform.position + cam.transform.forward * 10.0f;

        // Bad
        this.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 10.0f;

        // Good
        comp.DoSomethingAwesome();

        // Bad
        GetComponent<CustomComponent>().DoSomethingAwesome();
    }
}

Nota:

Avoid GetComponent(string)
Al usar GetComponent(), hay un puñado de sobrecargas diferentes. Es importante usar siempre las implementaciones basadas en tipos y nunca la sobrecarga de búsqueda basada en cadenas. La búsqueda por cadena en la escena es más costosa que la búsqueda por tipo.
(Bueno) Component GetComponent(Type type)
(Bueno) T GetComponent<T>()
(Malo) Component GetComponent(string)>

Evitar operaciones costosas

  1. Evitar el uso de LINQ

    Aunque LINQ puede ser limpio y fácil de leer y escribir, generalmente requiere más cálculo y memoria que si escribiera el algoritmo manualmente.

    // Example Code
    using System.Linq;
    
    List<int> data = new List<int>();
    data.Any(x => x > 10);
    
    var result = from x in data
                 where x > 10
                 select x;
    
  2. API comunes de Unity

    Ciertas API de Unity, aunque útiles, pueden ser costosas de ejecutar. La mayoría de estos implican buscar en todo su gráfico de escena alguna lista coincidente de GameObjects. Por lo general, estas operaciones se pueden evitar almacenando en caché las referencias o implementando un componente de administrador para GameObjects para realizar un seguimiento de las referencias en tiempo de ejecución.

        GameObject.SendMessage()
        GameObject.BroadcastMessage()
        UnityEngine.Object.Find()
        UnityEngine.Object.FindWithTag()
        UnityEngine.Object.FindObjectOfType()
        UnityEngine.Object.FindObjectsOfType()
        UnityEngine.Object.FindGameObjectsWithTag()
        UnityEngine.Object.FindGameObjectsWithTag()
    

Nota:

SendMessage() y BroadcastMessage() deben eliminarse a toda costa. Estas funciones pueden ser del orden de 1000 veces más lentas que las llamadas directas.

  1. Cuidado con el boxeo

    El boxing es un concepto básico del lenguaje C# y del tiempo de ejecución. Es el proceso de ajustar variables de tipo de valor, como char, int, bool, etc., en variables de tipo de referencia. Cuando se produce una "caja" en una variable de tipo valor, se encapsula en un System.Object, que se almacena en el montón administrado. La memoria se asigna y, finalmente, cuando se elimina, debe ser procesada por el recolector de basura. Estas asignaciones y desasignaciones incurren en un costo de rendimiento y, en muchos escenarios, son innecesarias o pueden reemplazarse fácilmente por una alternativa menos costosa.

    Para evitar el boxing, asegúrese de que las variables, campos y propiedades en los que almacena tipos numéricos y estructuras (incluido Nullable<T>) se escriban como tipos específicos, como int, float? o MyStruct, en lugar de usar object. Si coloca estos objetos en una lista, asegúrese de usar una lista fuertemente tipada, como List<int> en lugar de List<object> o .ArrayList

    Ejemplo de boxeo en C#

    // boolean value type is boxed into object boxedMyVar on the heap
    bool myVar = true;
    object boxedMyVar = myVar;
    

Repetición de rutas de acceso de código

Cualquier función de devolución de llamada de Unity repetitiva (es decir, Update) que se ejecute muchas veces por segundo y/o fotograma debe escribirse con cuidado. Cualquier operación costosa aquí tiene un impacto enorme y constante en el rendimiento.

  1. Funciones de devolución de llamada vacías

    Aunque el código siguiente puede parecer inocente de dejar en la aplicación, especialmente porque cada script de Unity se inicializa automáticamente con un método Update, estas devoluciones de llamada vacías pueden resultar costosas. Unity opera hacia adelante y hacia atrás entre un límite de código administrado y no administrado, entre el código de UnityEngine y el código de la aplicación. El cambio de contexto a través de este puente es bastante costoso, incluso si no hay nada que ejecutar. Esto resulta especialmente problemático si la aplicación tiene cientos de GameObjects con componentes que tienen devoluciones de llamada de Unity vacías y repetidas.

    void Update()
    {
    }
    

Nota:

Update() es la manifestación más común de este problema de rendimiento, pero otras devoluciones de llamada de Unity que se repiten, como las siguientes, pueden ser igual de malas, si no peores: FixedUpdate(), LateUpdate(), OnPostRender", OnPreRender(), OnRenderImage(), etc.

  1. Operaciones para favorecer la ejecución una vez por fotograma

    Las siguientes API de Unity son operaciones comunes para muchas aplicaciones holográficas. Aunque no siempre es posible, los resultados de estas funciones se pueden calcular comúnmente una vez y los resultados se pueden reutilizar en la aplicación para un marco determinado.

    1. Es una buena práctica tener una clase o servicio Singleton dedicado para manejar su mirada Raycast en la escena y luego reutilizar este resultado en todos los demás componentes de la escena, en lugar de realizar operaciones de Raycast repetidas e idénticas por cada componente. Algunas aplicaciones pueden requerir difusiones de rayos de diferentes orígenes o contra diferentes LayerMasks.
        UnityEngine.Physics.Raycast()
        UnityEngine.Physics.RaycastAll()
    
    1. Evite las operaciones de GetComponent() en devoluciones de llamada repetidas de Unity como Update() almacenando en caché las referencias en Start() o Awake()
        UnityEngine.Object.GetComponent()
    
    1. Si es posible, se recomienda crear una instancia de todos los objetos en el momento de la inicialización y usar la agrupación de objetos para reciclar y reutilizar GameObjects durante el tiempo de ejecución de la aplicación
        UnityEngine.Object.Instantiate()
    
  2. Evitar interfaces y construcciones virtuales

    Invocar llamadas de función a través de interfaces frente a objetos directos o llamar a funciones virtuales a menudo puede ser mucho más costoso que usar construcciones directas o llamadas de función directas. Si la función o interfaz virtual no es necesaria, debe quitarse. Sin embargo, vale la pena compensar el impacto en el rendimiento de estos enfoques si su uso simplifica la colaboración en el desarrollo, la legibilidad y la capacidad de mantenimiento del código.

    Por lo general, la recomendación es no marcar campos y funciones como virtuales, a menos que haya una expectativa clara de que este miembro necesita ser sobrescrito. Se debe tener especial cuidado con las rutas de código de alta frecuencia que se llaman muchas veces por fotograma o incluso una vez por fotograma, como un UpdateUI() método.

  3. Evite pasar estructuras por valor

    A diferencia de las clases, las estructuras son tipos de valor y, cuando se pasan directamente a una función, su contenido se copia en una instancia recién creada. Esta copia agrega el costo de CPU, así como más memoria en la pila. Para estructuras pequeñas, el efecto es mínimo y, por lo tanto, aceptable. Sin embargo, para las funciones invocadas repetidamente cada fotograma y las funciones que toman estructuras grandes, si es posible, modifique la definición de la función para pasar por referencia. Más información aquí

Varios

  1. Física

    1. En general, la forma más fácil de mejorar la física es limitar la cantidad de tiempo dedicado a la física o el número de iteraciones por segundo. Esto reduce la precisión de la simulación. Ver TimeManager en Unity.

    2. Los tipos de colisionadores en Unity tienen características de rendimiento muy diferentes. El siguiente orden enumera los colisionadores de mayor rendimiento a los colisionadores de menor rendimiento de izquierda a derecha. Es importante evitar los colisionadores de malla, que son sustancialmente más caros que los colisionadores primitivos.

    Malla de caja de cápsula < de esfera < (convexa) < Malla (no convexa)<<<

  2. Animaciones

    Deshabilita las animaciones inactivas deshabilitando el componente Animator (deshabilitar el objeto de juego no tendrá el mismo efecto). Evite los patrones de diseño en los que un animador se sienta en un bucle estableciendo un valor para la misma cosa. Hay una sobrecarga considerable para esta técnica, sin efecto en la aplicación. Obtenga más información aquí.

  3. Algoritmos complejos

    Si su aplicación utiliza algoritmos complejos como cinemática inversa, búsqueda de rutas, etc., busque un enfoque más simple o ajuste la configuración relevante para su rendimiento.

Recomendaciones de rendimiento de CPU a GPU

Generalmente, el rendimiento de CPU a GPU se reduce a las llamadas de dibujo enviadas a la tarjeta gráfica. Para mejorar el rendimiento, las llamadas de sorteo deben reducirse estratégicamente a) reducirse o b) reestructurarse para obtener resultados óptimos. Dado que las llamadas de extracción en sí mismas consumen muchos recursos, reducirlas reducirá el trabajo general requerido. Además, los cambios de estado entre llamadas de dibujo requieren costosos pasos de validación y traducción en el controlador de gráficos y, por lo tanto, la reestructuración de las llamadas de dibujo de su aplicación para limitar los cambios de estado (es decir, diferentes materiales, etc.) puede aumentar el rendimiento.

Unity tiene un gran artículo que brinda una descripción general y se sumerge en las llamadas de dibujo por lotes para su plataforma.

Representación por instancias de un solo paso

Single Pass Instanced Rendering en Unity permite reducir las llamadas de dibujo de cada ojo a una llamada de dibujo instanciada. Debido a la coherencia de caché entre dos llamadas de dibujo, también hay algunas mejoras de rendimiento en la GPU.

Para habilitar esta característica en su proyecto de Unity

  1. Abra la configuración de OpenXR (vaya a Editar>configuración del> proyectoXR Plugin Management>OpenXR).
  2. Seleccione Single Pass Instanced en el menú desplegable Modo de renderización .

Lee los siguientes artículos de Unity para obtener más detalles con este enfoque de representación.

Nota:

Un problema común con la representación por instancias de un solo paso se produce si los desarrolladores ya tienen sombreadores personalizados existentes que no están escritos para la creación de instancias. Después de habilitar esta característica, es posible que los desarrolladores observen que algunos GameObjects solo se representan en un ojo. Esto se debe a que los sombreadores personalizados asociados no tienen las propiedades adecuadas para crear instancias.

Procesamiento por lotes estático

Unity puede procesar por lotes muchos objetos estáticos para reducir las llamadas de dibujo a la GPU. El procesamiento por lotes estático funciona para la mayoría de los objetos Renderer en Unity que 1) comparten el mismo material y 2) están marcados como estáticos (seleccione un objeto en Unity y seleccione la casilla de verificación en la parte superior derecha del inspector). GameObjects marcados como estáticos no se pueden mover durante el tiempo de ejecución de la aplicación. Por lo tanto, la propagación estática por lotes puede ser difícil de aplicar en HoloLens, donde prácticamente todos los objetos deben colocarse, moverse, escalarse, etc. Para los cascos envolventes, el procesamiento por lotes estático puede reducir drásticamente las llamadas de dibujo y, por lo tanto, mejorar el rendimiento.

Lea Procesamiento por lotes estático en Dibujar llamadas por lotes en Unity para obtener más detalles.

Procesamiento por lotes dinámico

Puesto que es problemático marcar objetos como estáticos para el desarrollo de HoloLens, el procesamiento por lotes dinámico puede ser una gran herramienta para compensar esta carencia. También puede ser útil en auriculares envolventes. Sin embargo, el procesamiento por lotes dinámico en Unity puede ser difícil de habilitar porque GameObjects debe a) compartir el mismo Material y b) cumplir una larga lista de otros criterios.

Lea Procesamiento por lotes dinámico en Draw Call Procesamiento por lotes en Unity para obtener la lista completa. Normalmente los GameObjects no son válidos para el procesamiento por lotes de forma dinámica, ya que los datos de malla asociados no pueden tener más de 300 vértices.

Otras técnicas

El procesamiento por lotes solo puede producirse si varios GameObjects pueden compartir el mismo material. Normalmente, esto se bloqueará por la necesidad de que los GameObjects tengan una textura única para su Material respectivo. Es común combinar texturas en una textura grande, un método conocido como Texture Atlasing.

Además, es preferible combinar mallas en un GameObject siempre que sea posible y razonable. Cada renderizador en Unity tiene su llamada de dibujo asociada frente al envío de una malla combinada bajo un renderizador.

Nota:

La modificación de las propiedades de Renderer.material en tiempo de ejecución crea una copia del material y, por lo tanto, potencialmente rompe el procesamiento por lotes. Usa Renderer.sharedMaterial para modificar las propiedades de material compartido en GameObjects.

Recomendaciones de rendimiento de la GPU

Ancho de banda y velocidades de llenado

Al representar un fotograma en la GPU, una aplicación está limitada por el ancho de banda de la memoria o la velocidad de relleno.

  • El ancho de banda de memoria es la velocidad de lecturas y escrituras que la GPU puede hacer desde la memoria
  • La tasa de relleno se refiere a los píxeles que puede dibujar la GPU por segundo.

Optimizar el uso compartido del búfer

Se recomienda habilitar el uso compartido del búfer de profundidad para optimizar la estabilidad del holograma. Al habilitar la reproyección en profundidad en la fase tardía con esta configuración, se recomienda seleccionar el formato de profundidad de 16 bits en lugar del formato de profundidad de 24 bits . Los búferes de profundidad de 16 bits reducirán drásticamente el ancho de banda (y, por lo tanto, la potencia) asociado con el tráfico del búfer de profundidad. Esto puede suponer una gran mejora tanto en la reducción de potencia como en el rendimiento. Sin embargo, hay dos posibles resultados negativos al usar el formato de profundidad de 16 bits.

Lucha Z

La fidelidad de rango de profundidad reducida hace que la lucha en Z sea más probable que ocurra con 16 bits que con 24 bits. Para evitar estos artefactos, modifique los planos de clip cercanos/lejanos de la cámara Unity para tener en cuenta la menor precisión. Para las aplicaciones basadas en HoloLens, un plano de recorte lejano de 50 m en lugar de los 1000 m predeterminados de Unity generalmente puede eliminar cualquier z-fighting.

Búfer de galería de símbolos deshabilitado

Cuando Unity crea una textura de renderizado con profundidad de 16 bits, no se crea ningún búfer de galería de símbolos. Al seleccionar el formato de profundidad de 24 bits, como se describe en la documentación de Unity, se creará un búfer z de 24 bits y un búfer de galería de símbolos de 8 bits (si se aplica a 32 bits en un dispositivo (por ejemplo, HoloLens), que suele ser el caso).

Evita los efectos de pantalla completa

Las técnicas que operan en pantalla completa pueden ser costosas ya que su orden de magnitud es de millones de operaciones en cada fotograma. Se recomienda evitar efectos de posprocesamiento , como suavizado, floración y más.

Configuración de iluminación óptima

La iluminación global en tiempo real en Unity puede proporcionar resultados visuales sobresalientes, pero implica costosos cálculos de iluminación. Se recomienda deshabilitar la iluminación global en tiempo real para cada archivo de escena de Unity a través de larepresentación de ventanas >>Configuración> de iluminación Desactive Iluminación global en tiempo real.

Además, se recomienda deshabilitar toda la proyección de sombras, ya que estos también agregan costosos pases de GPU a una escena de Unity. Las sombras se pueden desactivar por luz, pero también se pueden controlar de forma holística mediante la configuración de calidad.

Editar>Configuración del proyecto, a continuación, selecciona la categoría >Calidad y selecciona Baja calidad para la plataforma UWP. También se puede establecer la propiedad Shadows en Disable Shadows.

Se recomienda usar iluminación integrada con los modelos en Unity.

Reducir el número de polígonos

El número de polígonos se reduce en

  1. Quitar objetos de una escena
  2. Diezmado de activos, que reduce el número de polígonos para una malla determinada
  3. Implementación de un sistema de nivel de detalle (LOD) en su aplicación, que representa objetos lejanos con una versión de polígono inferior de la misma geometría

Descripción de los sombreadores en Unity

Una aproximación fácil para comparar sombreadores en rendimiento es identificar el número promedio de operaciones que cada uno ejecuta en tiempo de ejecución. Esto se puede hacer fácilmente en Unity.

  1. Seleccione el recurso de sombreador o seleccione un material y, a continuación, en la esquina superior derecha de la ventana del inspector. Seleccione el icono de engranaje seguido de "Seleccionar sombreador".

    Seleccionar sombreador en Unity

  2. Con el activo de sombreador seleccionado, seleccione el botón "Compilar y mostrar código" en la ventana del inspector.

    Compilar código del sombreador en Unity

  3. Después de compilar, busque la sección de estadísticas en los resultados con el número de operaciones diferentes para el sombreador de vértices y píxeles (Nota: los sombreadores de píxeles a menudo también se denominan sombreadores de fragmentos).

    Operaciones de sombreador de Unity Standard

Optimizar sombreadores de píxeles

Al observar los resultados estadísticos compilados mediante el método anterior, el sombreador de fragmentos generalmente ejecutará más operaciones que el sombreador de vértices, en promedio. El sombreador de fragmentos, también conocido como sombreador de píxeles, se ejecuta por píxel en la salida de la pantalla, mientras que el sombreador de vértices solo se ejecuta por vértice de todas las mallas que se dibujan en la pantalla.

Por lo tanto, los sombreadores de fragmentos no solo tienen más instrucciones que los sombreadores de vértices debido a todos los cálculos de iluminación, los sombreadores de fragmentos casi siempre se ejecutan en un conjunto de datos más grande. Por ejemplo, si la salida de pantalla es una imagen de 2k por 2k, entonces el sombreador de fragmentos se puede ejecutar 2,000 * 2,000 = 4,000,000 de veces. Si se representan dos ojos, este número se duplica ya que hay dos pantallas. Si una aplicación de realidad mixta tiene varios pasos, efectos de posprocesamiento de pantalla completa o representación de varias mallas en el mismo píxel, este número aumenta considerablemente.

Por lo tanto, reducir el número de operaciones en el sombreador de fragmentos generalmente puede proporcionar ganancias de rendimiento mucho mayores que las optimizaciones en el sombreador de vértices.

Alternativas de sombreador de Unity Standard

En lugar de usar un renderizado basado en física (PBR) u otro sombreador de alta calidad, considere la posibilidad de utilizar un sombreador más eficaz y económico. Mixed Reality Toolkit proporciona el sombreador estándar de MRTK que se ha optimizado para proyectos de realidad mixta.

Unity también proporciona un sombreador apagado, iluminado con vértices, difuso y otras simplificadas que son más rápidas en comparación con el sombreador Unity Standard. Para obtener más información, consulte Uso y rendimiento de los sombreadores integrados.

Precarga del sombreador

Use la precarga de sombreadores y otros trucos para optimizar el tiempo de carga del sombreador. En concreto, la precarga de shaders significa que no verás ningún problema debido a la compilación de shaders en tiempo de ejecución.

Límite de sobregiros

En Unity, se puede mostrar una sobreposición para su escena, alternando el menú de modo de dibujo en la esquina superior izquierda de la vista de escena y seleccionando Sobredibujar.

Por lo general, el sobregiro se puede mitigar eliminando los objetos con antelación antes de enviarlos a la GPU. Unity proporciona detalles sobre la implementación de Occlusion Culling para su motor.

Recomendaciones de memoria

Las operaciones de asignación & desasignación de memoria excesivas pueden tener efectos adversos en la aplicación holográfica, lo que da lugar a un rendimiento incoherente, fotogramas inmovilizados y otros comportamientos perjudiciales. Es especialmente importante comprender las consideraciones de memoria al desarrollar en Unity, ya que la administración de la memoria está controlada por el recolector de basura.

Recolección de elementos no utilizados

Las aplicaciones holográficas pierden tiempo de proceso de procesamiento en el recolector de elementos no utilizados (GC) cuando el GC se activa para analizar objetos que ya no están en el ámbito durante la ejecución y es necesario liberar su memoria, para que pueda estar disponible para su reutilización. Las asignaciones y desasignaciones constantes generalmente requerirán que el recolector de elementos no utilizados se ejecute con más frecuencia, lo que perjudicará el rendimiento y la experiencia del usuario.

Una de las prácticas más comunes que conduce a la recolección excesiva de elementos no utilizados no es almacenar en caché las referencias a componentes y clases en el desarrollo de Unity. Las referencias deben capturarse durante Start() o Awake() y reutilizarse en funciones posteriores como Update() o LateUpdate().

Otras sugerencias rápidas:

  • Use la clase StringBuilder C# para crear dinámicamente cadenas complejas en tiempo de ejecución.
  • Quite las llamadas a Debug.Log() cuando ya no sean necesarias, ya que aún se ejecutan en todas las versiones de compilación de una aplicación
  • Si tu aplicación holográfica generalmente requiere mucha memoria, considera llamar a System.GC.Collect() durante las fases de carga, como cuando se presenta una pantalla de carga o transición.

Agrupación de objetos

La agrupación de objetos es una técnica popular para reducir el coste de la asignación y desasignación continua de objetos. Esto se hace asignando un gran grupo de objetos idénticos y reutilizando las instancias inactivas disponibles de este grupo en lugar de generar y destruir objetos constantemente a lo largo del tiempo. Los grupos de objetos son excelentes para componentes reutilizables que tienen una duración variable durante una aplicación.

Rendimiento de inicio

Considera la posibilidad de iniciar la aplicación con una escena más pequeña y luego usar SceneManager.LoadSceneAsync para cargar el resto de la escena. Esto permite que la aplicación llegue a un estado interactivo lo más rápido posible. Puede haber un gran pico de CPU mientras se activa la nueva escena y que cualquier contenido renderizado pueda tartamudear o engancharse. Una manera de evitar esto es establecer la propiedad AsyncOperation.allowSceneActivation en "false" en la escena que se está cargando, esperar a que se cargue la escena, borrar la pantalla a negro y, luego, volver a establecerla en "true" para completar la activación de la escena.

Recuerda que mientras se carga la escena de inicio, se muestra la pantalla de presentación holográfica al usuario.