Condividi tramite


Raccomandazioni sulle prestazioni per Unity

Questo articolo si basa sulle raccomandazioni sulle prestazioni per la realtà mista, ma è incentrato sui miglioramenti specifici di Unity.

Di recente è stata rilasciata un'applicazione denominata Quality Fundamentals che copre problemi comuni di prestazioni, progettazione e ambiente e soluzioni per le app HoloLens 2. Questa app è un'ottima demo visiva per il contenuto che segue.

Il primo passaggio più importante quando si ottimizzano le prestazioni delle app di realtà mista in Unity è assicurarsi di usare le impostazioni di ambiente consigliate per Unity. Questo articolo contiene contenuto con alcune delle configurazioni di scena più importanti per la creazione di app Realtà mista performanti. La sezione seguente evidenzia alcune di queste impostazioni consigliate.

Come profilare con Unity

Unity fornisce il profiler unity predefinito, che è un'ottima risorsa per raccogliere informazioni dettagliate sulle prestazioni preziose per la specifica app. Anche se è possibile eseguire il profiler nell'editor, queste metriche non rappresentano il vero ambiente di runtime, quindi i risultati devono essere usati con cautela. Profilare in remoto l'applicazione durante l'esecuzione sul dispositivo per ottenere informazioni dettagliate più accurate e interattive.

Unity offre un'ottima documentazione per:

  1. Come connettere il profiler Unity alle applicazioni UWP in remoto
  2. Come diagnosticare in modo efficace i problemi di prestazioni con Unity Profiler

Profilatura GPU

Profiler Unity

Con Unity Profiler connesso e dopo aver aggiunto il profiler GPU (vedere Aggiungere profiler nell'angolo in alto a destra), è possibile vedere quanto tempo viene impiegato rispettivamente per la CPU & GPU al centro del profiler. Ciò consente allo sviluppatore di ottenere una rapida approssimazione se l'applicazione è limitata da CPU o GPU.

CPU Unity e GPU

Nota

Per usare la profilatura GPU, è necessario disabilitare i processi grafici nelle impostazioni di Unity Player. Per altri dettagli, vedere il modulo GPU Usage Profiler di Unity.

Debugger di frame Unity

Frame Debugger di Unity è anche uno strumento potente e intuitivo da usare. Offre una buona panoramica di ciò che la GPU sta facendo ogni fotogramma. Gli aspetti da cercare sono altre destinazioni di rendering e comandi blit da copiare tra di loro perché sono costosi in HoloLens. Idealmente, non è consigliabile usare destinazioni di rendering fuori schermo in HoloLens. Questi vengono aggiunti quando si abilitano funzionalità di rendering costose (ad esempio MSAA, HDR o effetti a schermo intero come bloom) che devono essere evitate.

Sovrapposizione della frequenza dei fotogrammi di HoloLens

La pagina Prestazioni del sistema del portale dispositivi contiene un buon riepilogo delle prestazioni della CPU e della GPU del dispositivo. È possibile abilitare Il contatore della frequenza dei fotogrammi nel visore auricolare e Il grafico della frequenza dei fotogrammi nel visore auricolare. Queste opzioni abilitano rispettivamente un contatore FPS e un grafo che offrono feedback immediato in qualsiasi applicazione in esecuzione nel dispositivo.

PIX

È possibile usare PIX anche per profilare le applicazioni Unity. Sono inoltre disponibili istruzioni dettagliate su come usare e installare PIX per HoloLens 2. In una compilazione di sviluppo, gli stessi ambiti visualizzati nel debugger frame di Unity verranno visualizzati anche in PIX e possono essere controllati e profilati in modo più dettagliato.

Nota

Unity consente di modificare facilmente la risoluzione della destinazione di rendering dell'applicazione in fase di esecuzione tramite la proprietà XRSettings.renderViewportScale . L'immagine finale presentata sul dispositivo ha una risoluzione fissa. La piattaforma esegue un esempio dell'output con risoluzione inferiore per creare un'immagine a risoluzione superiore per il rendering nei display.

UnityEngine.XR.XRSettings.renderViewportScale = 0.7f;

Consigli sulle prestazioni della CPU

Il contenuto seguente illustra procedure di prestazioni più approfondite, destinate in particolare allo sviluppo di Unity & C#.

Riferimenti alla cache

È consigliabile memorizzare nella cache i riferimenti a tutti i componenti pertinenti e a GameObject durante l'inizializzazione perché le chiamate di funzione ripetute, ad esempio GetComponent<T>() e Camera.main , sono più costose rispetto al costo della memoria per archiviare un puntatore. . Camera.main usa semplicemente FindGameObjectsWithTag() sotto, che cerca nel grafico della scena un oggetto fotocamera con il tag "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

Evitare GetComponent(string)
Quando si usa GetComponent(), sono presenti alcuni overload diversi. È importante usare sempre le implementazioni basate su tipi e mai l'overload di ricerca basato su stringa. La ricerca in base alla stringa nella scena è più costosa rispetto alla ricerca in base al tipo.
(Buono) Component GetComponent(Type type)
(Buono) T GetComponent<T>()
(Non valido) Component GetComponent(string)>

Evitare operazioni costose

  1. Evitare l'uso di LINQ

    Anche se LINQ può essere pulito e facile da leggere e scrivere, in genere richiede più calcolo e memoria rispetto a se l'algoritmo è stato scritto 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 Unity comuni

    Alcune API Unity, anche se utili, possono essere costose da eseguire. La maggior parte di questi comporta la ricerca di un elenco corrispondente di GameObjects nell'intero grafico della scena. Queste operazioni possono essere in genere evitate memorizzando nella cache i riferimenti o implementando un componente di gestione per GameObjects per tenere traccia dei riferimenti in fase di esecuzione.

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

Nota

SendMessage() e BroadcastMessage() devono essere eliminati a tutti i costi. Queste funzioni possono essere nell'ordine di 1000 volte più lente rispetto alle chiamate di funzione diretta.

  1. Attenzione alla boxe

    Il boxing è un concetto fondamentale del linguaggio e del runtime C#. Si tratta del processo di wrapping di variabili tipiizzate da valore, ad charesempio , int, boole così via, in variabili tipizzate di riferimento. Quando una variabile tipizzata da valore è "boxed", viene sottoposta a wrapping in un System.Objectoggetto , archiviato nell'heap gestito. La memoria viene allocata e, infine, quando eliminata deve essere elaborata dal Garbage Collector. Queste allocazioni e deallocazione comportano un costo di prestazioni e in molti scenari non sono necessarie o possono essere facilmente sostituite da un'alternativa meno costosa.

    Per evitare la conversione boxing, assicurarsi che le variabili, i campi e le proprietà in cui vengono archiviati tipi numerici e struct (incluso Nullable<T>) siano tipizzati come tipi specifici, ad intesempio , float? o MyStruct, anziché utilizzare l'oggetto . Se questi oggetti vengono inseriti in un elenco, assicurarsi di usare un elenco fortemente tipizzato, ad List<int> esempio anziché List<object> o ArrayList.

    Esempio di boxing in C#

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

Percorsi di codice ripetuti

Tutte le funzioni di callback di Unity ripetute (ad esempio Update) eseguite più volte al secondo e/o frame devono essere scritte con attenzione. Qualsiasi operazione costosa in questo caso ha un impatto enorme e coerente sulle prestazioni.

  1. Funzioni di callback vuote

    Anche se il codice seguente può sembrare innocente nell'applicazione, soprattutto perché ogni script Unity viene inizializzato automaticamente con un metodo Update, questi callback vuoti possono diventare costosi. Unity opera avanti e indietro tra un limite di codice non gestito e gestito, tra il codice UnityEngine e il codice dell'applicazione. Il cambio di contesto su questo bridge è piuttosto costoso, anche se non c'è nulla da eseguire. Ciò diventa particolarmente problematico se l'app include 100 gameobject con componenti con callback Unity ripetuti vuoti.

    void Update()
    {
    }
    

Nota

Update() è la manifestazione più comune di questo problema di prestazioni, ma altri callback di Unity ripetuti, ad esempio i seguenti possono essere ugualmente negativi, se non peggiori: FixedUpdate(), LateUpdate(), OnPostRender", OnPreRender(), OnRenderImage(), e così via.

  1. Operazioni per favorire l'esecuzione una volta per fotogramma

    Le API Unity seguenti sono operazioni comuni per molte app holographic. Anche se non sempre possibile, i risultati di queste funzioni possono essere comunemente calcolati una volta e i risultati riutilizzati nell'applicazione per un determinato frame.

    a) È consigliabile avere una classe o un servizio Singleton dedicato per gestire lo sguardo fisso di Raycast nella scena e quindi riutilizzare questo risultato in tutti gli altri componenti della scena, invece di eseguire operazioni Raycast ripetute e identiche da ogni componente. Alcune applicazioni possono richiedere raycast di origini diverse o su diversi LayerMasks.

        UnityEngine.Physics.Raycast()
        UnityEngine.Physics.RaycastAll()
    

    b) Evitare le operazioni GetComponent() nei callback di Unity ripetuti come Update() memorizzando nella cache i riferimenti in Start() o Awake()

        UnityEngine.Object.GetComponent()
    

    c) È consigliabile creare un'istanza di tutti gli oggetti, se possibile, durante l'inizializzazione e usare il pool di oggetti per riciclare e riutilizzare GameObject durante il runtime dell'applicazione

        UnityEngine.Object.Instantiate()
    
  2. Evitare interfacce e costrutti virtuali

    Richiamare chiamate di funzione tramite interfacce o oggetti diretti o chiamare funzioni virtuali può spesso essere molto più costoso rispetto all'uso di costrutti diretti o chiamate di funzione diretta. Se la funzione virtuale o l'interfaccia non è necessaria, è necessario rimuoverla. Tuttavia, il successo delle prestazioni per questi approcci vale la pena di essere compromesso se l'uso di questi approcci semplifica la collaborazione allo sviluppo, la leggibilità del codice e la gestibilità del codice.

    In genere, è consigliabile non contrassegnare campi e funzioni come virtuali a meno che non vi sia una chiara aspettativa che questo membro debba essere sovrascritto. È consigliabile prestare particolare attenzione ai percorsi di codice ad alta frequenza chiamati più volte per fotogramma o anche una volta per fotogramma, ad esempio un UpdateUI() metodo.

  3. Evitare di passare struct in base al valore

    A differenza delle classi, gli struct sono tipi valore e quando vengono passati direttamente a una funzione, il relativo contenuto viene copiato in un'istanza appena creata. Questa copia aggiunge il costo della CPU, oltre a una maggiore quantità di memoria nello stack. Per gli struct di piccole dimensioni, l'effetto è minimo e quindi accettabile. Tuttavia, per le funzioni richiamate ripetutamente ogni frame e funzioni che accettano struct di grandi dimensioni, se possibile modificare la definizione della funzione da passare per riferimento. Altre informazioni sono disponibili qui

Varie

  1. Fisica

    a) In genere, il modo più semplice per migliorare la fisica consiste nel limitare la quantità di tempo dedicato alla fisica o il numero di iterazioni al secondo. In questo modo si riduce l'accuratezza della simulazione. Vedere TimeManager in Unity

    b) I tipi di collisori in Unity hanno caratteristiche di prestazioni molto diverse. L'ordine seguente elenca i collisori più performanti per i collisori meno performanti da sinistra a destra. È importante evitare collisori mesh, che sono sostanzialmente più costosi rispetto ai collisori primitivi.

    Mesh Sphere < Capsule < Box <<< Mesh (Convex) < (non Convex)

  2. Animazioni

    Disabilitare le animazioni inattive disabilitando il componente Animator (la disabilitazione dell'oggetto gioco non avrà lo stesso effetto). Evitare modelli di progettazione in cui un animatore si trova in un ciclo impostando un valore sulla stessa cosa. Questa tecnica comporta un sovraccarico notevole, senza alcun effetto sull'applicazione. Scopri di più qui.

  3. Algoritmi complessi

    Se l'applicazione usa algoritmi complessi, ad esempio cinematica inversa, ricerca di percorsi e così via, cercare un approccio più semplice o modificare le impostazioni pertinenti per le prestazioni

Consigli sulle prestazioni da CPU a GPU

In genere, le prestazioni da CPU a GPU si riduce alle chiamate di estrazione inviate alla scheda grafica. Per migliorare le prestazioni, le chiamate di disegno devono essere strategicamente ridotte o b) ristrutturate per ottenere risultati ottimali. Poiché le chiamate di disegno sono a elevato utilizzo di risorse, ridurle ridurrà il lavoro complessivo richiesto. Inoltre, le modifiche dello stato tra le chiamate di disegno richiedono costosi passaggi di convalida e conversione nel driver di grafica e, di conseguenza, la ristrutturazione delle chiamate di disegno dell'applicazione per limitare le modifiche dello stato (ad esempio materiali diversi e così via) può migliorare le prestazioni.

Unity ha un ottimo articolo che offre una panoramica e approfondisce le chiamate di disegno in batch per la piattaforma.

Rendering con istanza a passaggio singolo

Single Pass Instanced Rendering in Unity consente di ridurre le chiamate di disegno per ogni occhio a una chiamata di disegno con istanza. A causa della coerenza della cache tra due chiamate di disegno, c'è anche un miglioramento delle prestazioni della GPU.

Per abilitare questa funzionalità nel progetto Unity

  1. Aprire Impostazioni OpenXR (passare a Modifica>impostazioni> progettoXR Plugin Management>OpenXR).
  2. Selezionare Single Pass Instanced dal menu a discesa Modalità rendering .

Per informazioni dettagliate su questo approccio di rendering, leggere gli articoli seguenti di Unity.

Nota

Un problema comune con il rendering con istanza a passaggio singolo si verifica se gli sviluppatori hanno già shader personalizzati esistenti non scritti per la creazione di istanze. Dopo aver abilitato questa funzionalità, gli sviluppatori possono notare alcuni GameObject di cui viene eseguito il rendering solo con un occhio. Ciò è dovuto al fatto che gli shader personalizzati associati non hanno le proprietà appropriate per l'istanza.

Invio in batch statico

Unity è in grado di inviare in batch molti oggetti statici per ridurre le chiamate di disegno alla GPU. L'invio in batch statico funziona per la maggior parte degli oggetti Renderer in Unity che 1) condividono lo stesso materiale e 2) sono contrassegnati come statici (selezionare un oggetto in Unity e selezionare la casella di controllo in alto a destra del controllo). Gli oggetti GameObject contrassegnati come statici non possono essere spostati nel runtime dell'applicazione. Pertanto, l'invio in batch statico può essere difficile da applicare in HoloLens, dove praticamente ogni oggetto deve essere posizionato, spostato, ridimensionato e così via. Per le cuffie immersive, l'invio in batch statico può ridurre notevolmente le chiamate di disegno e migliorare così le prestazioni.

Per altri dettagli, vedere Batch staticoin Draw Call Batching in Unity .

Invio in batch dinamico

Poiché è problematico contrassegnare gli oggetti come statici per lo sviluppo holoLens, il batch dinamico può essere un ottimo strumento per compensare questa funzionalità mancante. Può essere utile anche nelle cuffie immersive. Tuttavia, l'invio in batch dinamico in Unity può essere difficile da abilitare perché GameObjects deve a ) condividere lo stesso materiale e b) soddisfare un lungo elenco di altri criteri.

Leggere Batching dinamicoin Draw Call Batching in Unity per l'elenco completo. In genere, gameobject diventano non validi per essere raggruppati dinamicamente, perché i dati della mesh associati non possono essere più di 300 vertici.

Altre tecniche

L'invio in batch può verificarsi solo se più GameObject sono in grado di condividere lo stesso materiale. In genere, questa operazione verrà bloccata dalla necessità che GameObjects disponga di una trama univoca per il rispettivo materiale. È comune combinare trame in una trama grande, un metodo noto come Atlante delle trame.

Inoltre, è preferibile combinare le mesh in un unico GameObject, dove possibile e ragionevole. Ogni renderer in Unity ha la chiamata di disegno associata rispetto all'invio di una mesh combinata in un renderer.

Nota

La modifica delle proprietà di Renderer.material in fase di esecuzione crea una copia del materiale e quindi potenzialmente interrompe l'invio in batch. Usare Renderer.sharedMaterial per modificare le proprietà del materiale condiviso in GameObjects.

Consigli sulle prestazioni della GPU

Larghezza di banda e frequenza di riempimento

Quando si esegue il rendering di un frame nella GPU, un'applicazione è associata alla larghezza di banda della memoria o alla frequenza di riempimento.

  • La larghezza di banda della memoria è la frequenza di letture e scritture che la GPU può eseguire dalla memoria
    • In Unity modificare Qualità trama in Modifica>impostazioni progetto Impostazioni>qualità.
  • La frequenza di riempimento si riferisce ai pixel che possono essere disegnati al secondo dalla GPU.

Ottimizzare la condivisione del buffer di profondità

È consigliabile abilitare la condivisione del buffer di profondità per ottimizzare la stabilità degli ologrammi. Quando si abilita la riprogettazione in fase avanzata basata sulla profondità con questa impostazione, è consigliabile selezionare il formato di profondità a 16 bit anziché il formato di profondità a 24 bit . I buffer di profondità a 16 bit ridurranno drasticamente la larghezza di banda (e quindi l'alimentazione) associata al traffico del buffer di profondità. Questo può essere un grande miglioramento sia nella riduzione della potenza che nelle prestazioni. Esistono tuttavia due possibili risultati negativi usando il formato di profondità a 16 bit.

Z-Fighting

La fedeltà dell'intervallo di profondità ridotto rende più probabile che si verifichi il combattimento z con 16 bit rispetto a 24 bit. Per evitare questi artefatti, modificare i piani di clip vicini/lontani della fotocamera Unity per tenere conto della precisione inferiore. Per le applicazioni basate su HoloLens, un piano di clip lontano di 50 m invece dei 1000 m predefiniti di Unity può in genere eliminare qualsiasi combattimento z.

Buffer stencil disabilitato

Quando Unity crea una trama di rendering con profondità a 16 bit, non viene creato alcun buffer stencil. Selezionando il formato di profondità a 24 bit, come descritto nella documentazione di Unity, verranno creati un buffer z a 24 bit e un buffer stencil a 8 bit (se 32 bit è applicabile in un dispositivo, ad esempio HoloLens), come in genere accade.

Evitare effetti a schermo intero

Le tecniche che operano a schermo intero possono essere costose perché il loro ordine di grandezza è di milioni di operazioni ogni fotogramma. È consigliabile evitare effetti post-elaborazione , ad esempio anti-aliasing, bloom e altro ancora.

Impostazioni di illuminazione ottimali

L'illuminazione globale in tempo reale in Unity può offrire risultati visivi eccezionali, ma comporta costosi calcoli di illuminazione. È consigliabile disabilitare l'illuminazione globale in tempo reale per ogni file di scena unity tramite impostazioni > diilluminazionedel rendering> della finestra> Deselezionare Illuminazione globale in tempo reale.

Inoltre, è consigliabile disabilitare tutti i cast di ombreggiatura perché aggiungono anche passaggi GPU costosi in una scena unity. Le ombre possono essere disabilitate per ogni luce, ma possono anche essere controllate in modo olistico tramite le impostazioni di qualità.

Redigere>Impostazioni progetto, quindi selezionare la categoria >Qualità selezionare Bassa qualità per la piattaforma UWP. È anche possibile impostare la proprietà Shadows su Disable Shadows (Disabilita ombreggiature).

È consigliabile usare l'illuminazione cotta con i modelli in Unity.

Ridurre il numero di poli

Il numero di poligoni viene ridotto di uno dei due

  1. Rimozione di oggetti da una scena
  2. Decimazione delle risorse, che riduce il numero di poligoni per una determinata mesh
  3. Implementazione di un sistema LOD (Level of Detail) nell'applicazione, che esegue il rendering di oggetti lontani con una versione poligonale inferiore della stessa geometria

Informazioni sugli shader in Unity

Un'approssimazione semplice per confrontare gli shader nelle prestazioni consiste nell'identificare il numero medio di operazioni eseguite in fase di esecuzione. Questa operazione può essere eseguita facilmente in Unity.

  1. Seleziona l'asset shader o seleziona un materiale, quindi nell'angolo in alto a destra della finestra del controllo. Seleziona l'icona a ingranaggio seguita da "Seleziona shader"

    Selezionare lo shader in Unity

  2. Con l'asset shader selezionato, seleziona il pulsante "Compila e mostra codice" nella finestra del controllo

    Compilare codice shader in Unity

  3. Dopo la compilazione, cercare la sezione statistiche nei risultati con il numero di operazioni diverse sia per il vertex shader che per il pixel shader (Nota: i pixel shader sono spesso chiamati anche fragment shader)

    Operazioni shader Standard Unity

Ottimizzare i pixel shader

Esaminando i risultati delle statistiche compilate usando il metodo precedente, il fragment shader eseguirà in genere più operazioni rispetto al vertex shader, in media. Il fragment shader, noto anche come pixel shader, viene eseguito per pixel sull'output dello schermo, mentre il vertex shader viene eseguito solo per ogni vertice di tutte le mesh disegnate sullo schermo.

Di conseguenza, non solo i fragment shader hanno più istruzioni rispetto ai vertex shader a causa di tutti i calcoli di illuminazione, i fragment shader vengono quasi sempre eseguiti su un set di dati più grande. Ad esempio, se l'output dello schermo è un'immagine 2k per 2k, il fragment shader può essere eseguito 2.000*2.000 = 4.000.000 volte. Se si esegue il rendering di due occhi, questo numero raddoppia poiché sono presenti due schermi. Se un'applicazione di realtà mista ha più passaggi, effetti di post-elaborazione a schermo intero o rendering di più mesh nello stesso pixel, questo numero aumenta notevolmente.

Pertanto, la riduzione del numero di operazioni nel fragment shader può in genere offrire maggiori miglioramenti delle prestazioni rispetto alle ottimizzazioni nel vertex shader.

Alternative agli shader Standard Unity

Invece di usare un rendering basato fisicamente (PBR) o un altro shader di alta qualità, esaminare l'uso di uno shader più efficiente ed economico. Il toolkit Realtà mista fornisce lo shader standard MRTK ottimizzato per i progetti di realtà mista.

Unity offre anche opzioni di shader non illuminate, con vertex illuminate, diffuse e altre opzioni di shader semplificate che sono più veloci rispetto allo shader Standard Unity. Per informazioni più dettagliate, vedi Utilizzo e prestazioni degli shader predefiniti .

Precaricamento dello shader

Usa il precaricamento degli shader e altri trucchi per ottimizzare il tempo di caricamento dello shader. In particolare, il precaricamento dello shader significa che non vedrai alcun hitches a causa della compilazione dello shader di runtime.

Limitare l'overdraw

In Unity è possibile visualizzare l'overdraw per la scena attivando il menu della modalità disegno nell'angolo superiore sinistro della visualizzazione Scena e selezionando Sovrascriva.

In genere, l'overdraw può essere attenuato eliminando gli oggetti in anticipo prima che vengano inviati alla GPU. Unity fornisce informazioni dettagliate sull'implementazione di Occlusion Culling per il motore.

Consigli sulla memoria

Un'allocazione eccessiva della memoria & operazioni di deallocazione può avere effetti negativi sull'applicazione olografica, con conseguente incoerenza di prestazioni, frame bloccati e altri comportamenti dannosi. È particolarmente importante comprendere le considerazioni sulla memoria durante lo sviluppo in Unity poiché la gestione della memoria è controllata dal Garbage Collector.

Garbage Collection

Le app olografiche perdono il tempo di elaborazione per il Garbage Collector (GC) quando il gc viene attivato per analizzare gli oggetti che non sono più nell'ambito durante l'esecuzione e la relativa memoria deve essere rilasciata, in modo che possa essere resa disponibile per il riutilizzo. Le allocazioni costanti e le allocazioni richiedono in genere un'esecuzione più frequente del Garbage Collector, danneggiando così le prestazioni e l'esperienza utente.

Una delle procedure più comuni che comporta un'eccessiva garbage collection non consiste nel memorizzare nella cache i riferimenti a componenti e classi nello sviluppo di Unity. Tutti i riferimenti devono essere acquisiti durante Start() o Awake() e riutilizzati in funzioni successive, ad esempio Update() o LateUpdate().

Altri suggerimenti rapidi:

  • Usare la classe C# StringBuilder per compilare in modo dinamico stringhe complesse in fase di esecuzione
  • Rimuovere le chiamate a Debug.Log() quando non sono più necessarie, perché vengono comunque eseguite in tutte le versioni di compilazione di un'app
  • Se l'app olografica richiede in genere molta memoria, è consigliabile chiamare System.GC.Collect() durante le fasi di caricamento, ad esempio quando si presenta una schermata di caricamento o transizione

Pool di oggetti

Il pool di oggetti è una tecnica comune per ridurre il costo dell'allocazione continua degli oggetti e delle deallocazione. Questa operazione viene eseguita allocando un pool di grandi dimensioni di oggetti identici e riutilizzando le istanze disponibili inattive di questo pool invece di generare e distruggere costantemente oggetti nel tempo. I pool di oggetti sono ideali per i componenti riutilizzabili che hanno una durata variabile durante un'app.

Prestazioni di avvio

È consigliabile avviare l'app con una scena più piccola, quindi usare SceneManager.LoadSceneAsync per caricare il resto della scena. In questo modo l'app può raggiungere uno stato interattivo il più velocemente possibile. Durante l'attivazione della nuova scena potrebbe verificarsi un picco elevato della CPU e che qualsiasi contenuto sottoposto a rendering potrebbe balbuziere o intoppi. Un modo per ovviare a questo errore consiste nell'impostare la proprietà AsyncOperation.allowSceneActivation su "false" nella scena caricata, attendere il caricamento della scena, cancellare lo schermo su nero e quindi impostarlo di nuovo su "true" per completare l'attivazione della scena.

Tenere presente che durante il caricamento della scena di avvio, la schermata iniziale olografica viene visualizzata all'utente.