Recomendaciones de rendimiento para Unity
Este artículo se basa en las recomendaciones de rendimiento para la realidad mixta, pero se centra en las características específicas de Unity.
Recientemente lanzamos una aplicación denominada Aspectos básicos de la calidad que trata problemas comunes de rendimiento, diseño y entorno y soluciones para aplicaciones HoloLens 2. Esta aplicación es una excelente demostración visual del contenido siguiente.
Uso de la configuración del proyecto de Unity
El primer paso más importante al 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 la creación de aplicaciones de realidad mixta de gran rendimiento. Algunas de estas configuraciones recomendadas también se indican a continuación.
Cómo generar perfiles con Unity
Unity proporciona una instancia integrada de Unity Profiler, que es un excelente recurso para recopilar información valiosa sobre el rendimiento de tu aplicación en particular. Aunque se puede ejecutar Profiler en el editor, estas métricas no representan el entorno real en tiempo de ejecución, por lo que los resultados se deben usar con precaución. Se recomienda generar perfiles de la aplicación de forma remota mientras se ejecuta en el dispositivo para obtener una información más precisa y útil.
Unity proporciona una documentación excelente sobre:
- Cómo conectar Unity Profiler a aplicaciones para UWP de forma remota
- Cómo diagnosticar los problemas de rendimiento con Unity Profiler de forma eficaz
Generación de perfiles de GPU
Generador de perfiles de Unity
Con Unity Profiler conectado y después de agregar el generador de perfiles de GPU (consulta Agregar generador de perfiles en la esquina superior derecha), puedes ver cuánto tiempo se dedica a CPU y a GPU, respectivamente, en medio del generador de perfiles. Esto permite al desarrollador obtener una aproximación rápida de si su aplicación está vinculada a la CPU o la GPU.
Nota:
Para usar la generación de perfiles de GPU, debe deshabilitar Graphics Jobs en Player Settings de Unity. Vea el módulo Generador de perfiles de uso de GPU de Unity para obtener más detalles.
Depurador de fotograma de Unity
Frame Debugger de Unity también es una eficaz e informativa herramienta que se puede usar. Proporciona una buena visión general de lo que la GPU está haciendo en cada fotograma. Las cosas que se deben tener en cuenta son los destinos de representación adicionales y los comandos blit para copiar entre ellos, ya que son muy costosos en HoloLens. Idealmente, no se deben usar destinos de representación fuera de la pantalla en HoloLens. Normalmente se agregan al habilitar características de representación 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. Puede habilitar el contador de velocidad de fotogramas display en el casco y mostrar gráfico de velocidad de fotogramas en el casco. Estas opciones habilitan un contador de FPS y un gráfico, respectivamente, que le proporcionan comentarios inmediatos en cualquier aplicación en ejecución en el dispositivo.
PIX
PIX se puede usar para generar perfiles de aplicaciones de Unity. También hay instrucciones detalladas sobre cómo usar e instalar PIX para HoloLens 2. En una compilación de desarrollo, en PIX también se muestran los mismos ámbitos que se ven en Frame Debugger de Unity, y se pueden inspeccionar y es posible generar perfiles de ellos con más detalle.
Nota:
Unity ofrece la posibilidad de modificar fácilmente la resolución de destino de representación de la aplicación en tiempo de ejecución mediante la propiedad XRSettings.renderViewportScale. La imagen final presentada en el dispositivo tiene una resolución fija. La plataforma realizará una muestra de la salida de resolución inferior para crear una imagen de resolución superior para su representación en las pantallas.
UnityEngine.XR.XRSettings.renderViewportScale = 0.7f;
Recomendaciones de rendimiento de CPU
El siguiente contenido abarca prácticas de rendimiento más detalladas, especialmente destinadas al desarrollo en Unity y C#.
Almacenamiento en caché de referencias
Se recomienda almacenar en caché las referencias a todos los componentes y GameObjects relevantes en la inicialización, porque las llamadas de función repetitivas, como GetComponent<T>() y Camera.main, son más caras con relación al costo de memoria para almacenar un puntero. . Camera.main solo usa FindGameObjectsWithTag() debajo, que busca de un modo costoso en el gráfico de la escena 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:
Evitar GetComponent(string)
Al usar GetComponent(), se producen unas cuantas sobrecargas diferentes. Es importante usar siempre las implementaciones basadas en tipo y nunca la sobrecarga de búsqueda basada en cadena. La búsqueda por cadena en la escena es significativamente más costosa que la búsqueda por tipo.
(Bueno) Componente GetComponent(tipo de tipo)
(Bueno) T GetComponent<T>()
(Incorrecto) Component GetComponent(string)>
Evita operaciones costosas
Evita el uso de LINQ
Aunque LINQ puede ser limpio y fácil de leer y escribir, normalmente requiere más cálculos y memoria que si se escribe 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;
API comunes de Unity
Ciertas API de Unity, aunque resultan útiles, pueden ser costosas de ejecutar. La mayoría de ellas implican buscar, en todo el gráfico de escena, una lista de GameObjects coincidentes. Por lo general, estas operaciones se pueden evitar mediante el almacenamiento en caché de las referencias o la implementación de un componente administrador para los GameObjects, a fin de realizar el 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 a funciones directas.
Ten cuidado con la conversión boxing
La conversión boxing es un concepto básico del lenguaje C# y del tiempo de ejecución. Es el proceso de encapsular variables con tipo de valor como
char
,int
,bool
, etc. en variables con tipo de referencia. Cuando a una variable con tipo de valor se le aplica la "conversión boxing", se encapsula en un objetoSystem.Object
que se almacena en el montón administrado. Se asigna memoria y, en el momento en el que se elimina, el recolector de elementos no utilizados la debe procesar. Estas asignaciones y anulaciones de asignación incurren en un costo de rendimiento y en muchos escenarios no son necesarias o se pueden reemplazar fácilmente por una alternativa menos costosa.Para evitar la conversión boxing, asegúrese de que las variables, los campos y las propiedades en los que almacena los tipos numéricos y las estructuras (incluido
Nullable<T>
) están fuertemente tipados como tipos específicos, comoint
,float?
oMyStruct
, en lugar de utilizar el objeto. Si coloca estos objetos en una lista, asegúrese de usar una lista fuertemente tipada, comoList<int>
, en lugar deList<object>
oArrayList
.Ejemplo de conversión boxing en C#
// boolean value type is boxed into object boxedMyVar on the heap bool myVar = true; object boxedMyVar = myVar;
Rutas de acceso al código repetitivas
Las funciones de devolución de llamada de Unity repetidas (es decir, Update) que se ejecutan muchas veces por segundo o en el marco deben escribirse cuidadosamente. Cualquier operación costosa tendrá un impacto enorme y consistente en el rendimiento.
Funciones de devolución de llamada vacías
Aunque pueda parecer inofensivo dejar el código siguiente en la aplicación, sobre todo porque cada script de Unity se inicializa automáticamente con un método de actualización, estas devoluciones de llamada vacías pueden llegar a ser muy costosas. Unity opera de un lado para otro entre un límite de código administrado y no administrado, entre el código UnityEngine y el código de la aplicación. El cambio de contexto sobre este puente es bastante caro, incluso si no hay nada que ejecutar. Resulta especialmente problemático si la aplicación tiene cientos de objetos GameObject con componentes que tienen devoluciones de llamada vacías de Unity repetitivas.
void Update() { }
Nota:
Update() es la manifestación más común de este problema de rendimiento, pero otras devoluciones de llamada de Unity repetidas, como las siguientes, pueden ser igualmente incorrectas, si no es peor: FixedUpdate(), LateUpdate(), OnPostRender", OnPreRender(), OnRenderImage(), etc.
Operaciones para favorecer una ejecución 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 suelen calcular una vez y los resultados se reutilizan en la aplicación para un fotograma determinado.
a) Es una buena práctica tener una clase o un servicio Singleton dedicado para controlar la mirada Raycast de la escena y, a continuación, volver a usar este resultado en todos los demás componentes de la escena, en lugar de realizar operaciones Raycast repetidas e idénticas para cada componente. Es posible que algunas aplicaciones requieran Raycast de distintos orígenes o con diferentesLayerMasks.
UnityEngine.Physics.Raycast() UnityEngine.Physics.RaycastAll()
b) Evita las operaciones GetComponent() en las devoluciones de llamada de Unity repetitivas, como Update(), mediante el almacenamiento en caché de referencias en Start() o Awake()
UnityEngine.Object.GetComponent()
c) Es una buena práctica crear instancias de todos los objetos, si es posible, en la inicialización y usar la agrupación de objetos para reciclar y volver a usar GameObjects durante todo el tiempo de ejecución de la aplicación.
UnityEngine.Object.Instantiate()
Evita interfaces y construcciones virtuales
La invocación de llamadas a funciones a través de interfaces frente a objetos directos o la llamada a funciones virtuales pueden ser a menudo mucho más costosas que usar construcciones directas o llamadas a funciones directas. Si la interfaz o función virtual no es necesaria, se debe quitar. Sin embargo, el impacto en el rendimiento de estos enfoques merece la pena si su uso simplifica la colaboración en el desarrollo, la legibilidad del código y el mantenimiento del código.
Por lo general, la recomendación es no marcar los campos y las funciones como virtuales, a menos que haya una expectativa clara de que sea necesario sobrescribir este miembro. Se debe ser especialmente cauto en torno a las rutas de acceso del código de alta frecuencia a las que se llama muchas veces por fotograma, o incluso una vez por fotograma, como un método
UpdateUI()
.Evita 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 costo de CPU, así como memoria adicional a la pila. En el caso de las estructuras pequeñas, el efecto es mínimo y, por tanto, aceptable. Sin embargo, para las funciones invocadas repetidamente en cada fotograma y las funciones que toman estructuras grandes, si es posible, modifica la definición de función para que pase por referencia. Obtén más información aquí
Varios
Física
a) En general, la manera más fácil de mejorar la física es limitar la cantidad de tiempo empleado en la física o el número de iteraciones por segundo. Esta acción disminuirá la precisión de la simulación. Consulta TimeManager en Unity.
b) Los tipos de colisionadores en Unity tienen características de rendimiento muy diferentes. En el orden siguiente se indican los colisionadores de mayor rendimiento a los colisionadores con menor rendimiento, de izquierda a derecha. Es importante evitar los colisionadores de malla, que son mucho más caros que los colisionadores primitivos.
Esfera < Cápsula < Cuadro <<< Malla (convexa) < Malla (no convexa)
Consulta Buenas prácticas de la física de Unity para obtener más información.
Animaciones
Deshabilita las animaciones inactivas deshabilitando el componente animador (la deshabilitación del objeto de juego no tendrá el mismo efecto). Evita patrones de diseño en los que un animador se encuentre en un bucle estableciendo un valor en lo mismo. Esta técnica conlleva una sobrecarga considerable, sin ningún efecto en la aplicación. Obtenga más información aquí.
Algoritmos complejos
Si tu aplicación usa algoritmos complejos, como la cinemática inversa, la búsqueda de rutas de acceso, etc., busca un enfoque más sencillo o ajusta los valores de configuración pertinentes para su rendimiento.
Recomendaciones de rendimiento de CPU a GPU
En general, el rendimiento de CPU a GPU baja hasta las llamadas a draw enviadas a la tarjeta gráfica. Para mejorar el rendimiento, las llamadas a draw se deben a) reducir o b) reestructurar estratégicamente para obtener resultados óptimos. Dado que las llamadas a draw en sí mismas usan una gran cantidad de recursos, reducirlas también reducirá el trabajo general necesario. Además, los cambios de estado entre llamadas a draw requieren pasos de validación y traducción costosos en el controlador de gráficos y, por lo tanto, la reestructuración de las llamadas a draw de la aplicación para limitar los cambios de estado (es decir, materiales diferentes, etc.) puede mejorar el rendimiento.
Unity dispone de un excelente artículo que proporciona información general y profundiza en el procesamiento por lotes de las llamadas a draw para su plataforma.
Representación con instancia de un solo paso
La representación con instancia de un solo paso en Unity permite que las llamadas a draw de cada ojo se reduzcan a una llamada a draw con instancia. Debido a la coherencia de la memoria caché entre dos llamadas a draw, también hay una mejora del rendimiento de la GPU.
Para habilitar esta característica en el proyecto de Unity
- Abra Configuración de OpenXR (vaya a Editar>Configuración del proyecto>Administración del complemento XR>OpenXR).
- Seleccione Instancia de un solo paso en el menú desplegable Modo de representación.
Lee los siguientes artículos de Unity para obtener más información sobre este enfoque de representación.
- Cómo maximizar el rendimiento de AR y VR con la representación en estéreo avanzada
- Creación de instancias de un solo paso
Nota:
Un problema común con la representación con instancia de un solo paso se produce si los desarrolladores ya tienen sombreadores personalizados no escritos para la creación de instancias. Después de habilitar esta característica, los desarrolladores pueden observar que algunos objetos GameObjects solo se representan en un ojo. Esto se debe a que los sombreadores personalizados asociados no tienen las propiedades adecuadas para la creación de instancias.
Procesamiento por lotes estático
Unity puede procesar por lotes muchos objetos estáticos para reducir las llamadas draw a la GPU. El procesamiento por lotes estático funciona en 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 la casilla de la parte superior derecha del inspector). Los objetos GameObject marcados como estáticos no se pueden mover en tiempo de ejecución de la aplicación. Por lo tanto, el procesamiento por lotes estáticos puede ser difícil de aprovechar en HoloLens, donde prácticamente todos los objetos se deben colocar, mover, escalar, etc. Para cascos envolventes, el procesamiento por lotes estáticos puede reducir drásticamente las llamadas de dibujo y, por tanto, mejorar el rendimiento.
Lee Procesamiento por lotes estático en Procesamiento por lotes de llamadas a draw en Unity para obtener más detalles.
Procesamiento por lotes dinámico
Dado que es problemático marcar objetos como estáticos para el desarrollo de HoloLens, el procesamiento por lotes dinámico puede ser una herramienta excelente para compensar esta característica que falta. También puede ser útil en los cascos envolventes. Sin embargo, el procesamiento por lotes dinámico en Unity puede ser difícil de habilitar porque los objetos GameObject deben a) compartir el mismo material y b) cumplir una larga lista de otros criterios.
Lee Procesamiento por lotes dinámico en Procesamiento por lotes de llamadas a draw en Unity para obtener la lista completa. Normalmente, los objetos GameObject dejan de ser válidos para el procesamiento por lotes dinámico, porque los datos de malla asociados no pueden ser más de 300 vértices.
Otras técnicas
El procesamiento por lotes solo puede producirse si varios objetos GameObject pueden compartir el mismo material. Normalmente, se bloqueará por la necesidad de que los objetos GameObject tengan una textura única para su material respectivo. Es habitual combinar texturas en una sola gran textura, un método conocido como Creación de atlas de texturas.
Además, es preferible combinar las mallas en un objeto GameObject siempre que sea posible y razonable. Cada Renderer de Unity tendrá sus llamadas a draw asociadas y enviará una malla combinada bajo un Renderer.
Nota:
La modificación de las propiedades de Renderer.material en tiempo de ejecución creará una copia del material y, por tanto, puede interrumpir el procesamiento por lotes. Usa Renderer.sharedMaterial para modificar las propiedades de material compartido en los objetos GameObject.
Recomendaciones de rendimiento de GPU
Más información acerca de la optimización de la representación de gráficos en Unity
Ancho de banda y velocidades de relleno
Al representar un fotograma en la GPU, una aplicación estará limitada por el ancho de banda de 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.
- En Unity, cambie Texture Quality (Calidad de textura) a Editar>Configuración del proyecto>Configuración de calidad.
- La velocidad de relleno hace referencia a los píxeles que la GPU puede dibujar por segundo.
- En Unity, use la propiedad XRSettings.renderViewportScale.
Optimización del uso compartido del búfer de profundidad
Se recomienda que habilite el uso compartido del búfer de profundidad para optimizar la estabilidad del holograma. Al habilitar la reproyección en etapa posterior basada en la profundidad 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 energía) asociado con el tráfico del búfer de profundidad. Esto puede suponer una gran mejora en la reducción de energía y en el rendimiento. Sin embargo, hay dos posibles resultados negativos al usar el formato de profundidad de 16 bits.
Z-Fighting
La fidelidad del intervalo de profundidad reducida hace que sea más probable que se produzca z-fighting con 16 bits que con 24 bits. Para evitar estos artefactos, modifica los planos de recortes cercanos o lejanos de la cámara de Unity para tener en cuenta la precisión inferior. En el caso de las aplicaciones basadas en HoloLens, un plano de recorte lejano de 50 m, en lugar del valor predeterminado de Unity de 1000 m, puede eliminar cualquier z-fighting.
Búfer de galería de símbolos deshabilitado
Cuando Unity crea una representación de textura con profundidad de 16 bits, no se crea ningún búfer de galería de símbolos. Al seleccionar el formato de 24 bits de profundidad, como se describe en la documentación de Unity, se crearán un búfer Z de 24 bits y un búfer de galería de símbolos de 8 bits (si se pueden aplicar 32 bits en un dispositivo, por ejemplo HoloLens, que es lo que suele pasar).
Evita efectos de pantalla completa
Las técnicas que operan en la pantalla completa pueden resultar costosas, ya que su orden de magnitud es millones de operaciones en cada fotograma. Se recomienda evitar efectos posteriores al procesamiento, como el suavizado de contorno, la eclosión, etc.
Configuración de iluminación óptima
La iluminación global en tiempo real de Unity puede proporcionar resultados visuales excelentes, pero implica cálculos de iluminación costosos. Se recomienda deshabilitar la iluminación global en tiempo real para cada archivo de escena de Unity a través de Ventana>Representación>Configuración de iluminación> Desmarcar Iluminación global en tiempo real.
Además, se recomienda deshabilitar todas las proyecciones de sombras, ya que también agregan pases de GPU costosos a una escena de Unity. Las sombras se pueden deshabilitar por iluminación, pero también se pueden controlar holísticamente a través de la configuración de la calidad.
Editar>Configuración del proyecto, luego seleccione la categoría Calidad> y seleccione Calidad baja para la Plataforma UWP. También puedes establecer solo la propiedad Sombras en Disable Shadows (Deshabilitar sombras).
Se recomienda usar la iluminación posterior con los modelos en Unity.
Reducción del recuento de polígonos
Se reduce el número de polígonos mediante alguna de las acciones siguientes
- Eliminación de objetos de una escena
- Supresión de recursos, lo que reduce el número de polígonos de una malla determinada
- Implementación de un sistema de nivel de detalle (LOD) en la aplicación, que represente los objetos alejados con una versión de polígono inferior de la misma geometría
Descripción de los sombreadores en Unity
Una aproximación sencilla para comparar los sombreadores en el 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.
Seleccione el recurso de sombreador o 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".
Con el recurso de sombreador seleccionado, seleccione el botón "Compilar y mostrar código" en la ventana del inspector.
Después de la compilación, busca la sección de estadísticas en los resultados con el número de las diferentes operaciones para el sombreador de vértices y píxeles. (Nota: Los sombreadores de píxeles se suelen denominar sombreadores de fragmentos).
Optimización de los sombreadores de píxeles
Si se examinan los resultados de las estadísticas compiladas con el método anterior, el sombreador de fragmentos ejecutará normalmente más operaciones que el sombreador de vértices, como 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, no solo los sombreadores de fragmentos 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 mayor. Por ejemplo, si la salida de la pantalla es una imagen de 2 K por 2 K, el sombreador de fragmentos puede ejecutarse 2000 * 2000 = 4 000 000 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 procesamiento posterior de pantalla completa o representación de varias mallas para el mismo píxel, este número aumentará drásticamente.
Por lo tanto, reducir el número de operaciones en el sombreador de fragmentos normalmente puede proporcionar mejoras de rendimiento mucho mayores que las optimizaciones del sombreador de vértices.
Alternativas del sombreador estándar de Unity
En lugar de usar una representación basada físicamente (PBR) u otro sombreador de alta calidad, consulta el uso de 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 opciones sin iluminación, iluminación de vértice, iluminación difusa y otras opciones simplificadas de sombreador que son más rápidas en comparación con el sombreador estándar de Unity. Consulta Uso y rendimiento de los sombreadores integrados para obtener información más detallada.
Precarga de sombreador
Usa la precarga del sombreador y otros trucos para optimizar el tiempo de carga del sombreador. En concreto, la precarga del sombreador significa que no verás ningún problema debido a la compilación del sombreador en tiempo de ejecución.
Sobredibujo del límite
En Unity, se puede mostrar el sobredibujo de la escena, alternando el menú de modo de dibujo en la esquina superior izquierda de Scene view (Vista de escena) y seleccionando Overdraw (Sobredibujar).
Por lo general, el sobredibujo se puede mitigar eliminando los objetos antes de enviarlos a la GPU. Unity proporciona información detallada sobre la implementación de la Eliminación de oclusión para su motor.
Recomendaciones de memoria
Un número excesivo de operaciones de asignación y anulación de asignación de memoria puede tener efectos negativos en la aplicación holográfica, lo que produce un rendimiento incoherente, fotogramas inmovilizados y otro comportamiento perjudicial. Es especialmente importante comprender las consideraciones de memoria al desarrollar en Unity, ya que la administración de la memoria se controla mediante el recolector de elementos no utilizados.
Recolección de elementos no utilizados
Las aplicaciones holográficas perderán tiempo de cálculo de procesamiento en el recolector de elementos no utilizados (GC) cuando se active el GC para analizar los objetos que ya no están en el ámbito durante la ejecución y se deba liberar su memoria, a fin de que pueda estar disponible para su reutilización. Normalmente, las constantes asignaciones y anulaciones de asignación requerirán que el recolector de elementos no utilizados se ejecute con más frecuencia, con lo que se perjudica el rendimiento y la experiencia del usuario.
Unity ha proporcionado una página excelente que explica con detalle cómo funciona el recolector de elementos no utilizados y las sugerencias para escribir código más eficaz en lo que respecta a la administración de memoria.
Una de las prácticas más comunes que conduce a una recolección excesiva de elementos no utilizados es no almacenar en caché las referencias para componentes y clases del desarrollo de Unity. Las referencias deben capturarse durante las funciones Start() o Awake() y se pueden volver a usar en funciones posteriores como Update() o LateUpdate().
Otras sugerencias rápidas:
- Usa la clase StringBuilder de C# para generar dinámicamente cadenas complejas en tiempo de ejecución.
- Quita las llamadas a Debug.log() cuando ya no sean necesarias, ya que se ejecutan en todas las versiones de compilación de una aplicación.
- Si la aplicación holográfica normalmente requiere una gran cantidad de memoria, considere la posibilidad de 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 costo de asignaciones y anulaciones de asignación continuas de objetos. Para ello, se asigna un grupo grande de objetos idénticos y se reutilizan instancias disponibles inactivas de este grupo en lugar de generar y destruir objetos constantemente a lo largo del tiempo. Los grupos de objetos son excelentes para los componentes reutilizables que tienen una duración variable en una aplicación.
Rendimiento de inicio
Considere la posibilidad de iniciar la aplicación con una escena más pequeña y, a continuación, usar SceneManager.LoadSceneAsync para cargar el resto de la escena. Esto permite a la aplicación acceder a un estado interactivo lo más rápido posible. Puede haber un gran pico de CPU mientras se activa la nueva escena y cualquier contenido representado se puede trabar o enganchar. Una manera de solucionar este problema es establecer la propiedad AsyncOperation.allowSceneActivation en "false" en la escena que se está cargando, esperar a que se cargue, fundir a negro la pantalla y volver a establecerla en "true" para completar la activación de la escena.
Recuerda que mientras se carga la escena de inicio, se mostrará la pantalla de presentación holográfica al usuario.