Raccomandazioni sulle prestazioni per Unity

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

Di recente è stata rilasciata un'applicazione denominata Quality Fundamentals che illustra i problemi comuni di prestazioni, progettazione e ambiente e le soluzioni per le app HoloLens 2. Questa app è un'ottima demo visiva per i contenuti che seguono.

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 di realtà mista performanti. La sezione seguente evidenzia alcune di queste impostazioni consigliate.

Come creare profili con Unity

Unity fornisce Unity Profiler integrato, una risorsa ottimale per raccogliere informazioni preziose sulle prestazioni di un'app specifica. Sebbene sia possibile eseguire il profiler nell'editor, queste metriche non rappresentano il vero ambiente di runtime, quindi i risultati devono essere usati con cautela. Profila in remoto la tua applicazione mentre è in esecuzione sul dispositivo per ottenere le informazioni più accurate e fruibili.

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 di Unity

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

Unity CPU vs GPU

Nota

Per utilizzare la profilazione GPU, è necessario disabilitare i processi grafici nelle impostazioni di Unity Player. Per altri dettagli, vedere il modulo Profiler utilizzo GPU di Unity.

Debugger Unity Frame

Frame Debugger di Unity è anche uno strumento potente e perspicace da usare. Ti dà una buona panoramica di ciò che la GPU sta facendo in ogni fotogramma. Gli elementi a cui prestare attenzione sono altri obiettivi di rendering e comandi blit da copiare tra di essi, in quanto sono costosi su HoloLens. Idealmente, in HoloLens non devono essere utilizzate destinazioni di rendering esterne allo schermo. Questi vengono aggiunti quando si abilitano costose funzionalità di rendering (ad esempio MSAA, HDR o effetti a schermo intero come la fioritura) che dovrebbero essere evitate.

Sovrapposizione della frequenza dei fotogrammi di HoloLens

La pagina Prestazioni di sistema di Device Portal contiene un riepilogo delle prestazioni della CPU e della GPU del dispositivo. Puoi abilitare Visualizza contatore frame rate nell'headset e Visualizza grafico frame rate nell'headset. Queste opzioni abilitano rispettivamente un contatore FPS e un grafico che forniscono un feedback immediato in qualsiasi applicazione in esecuzione sul dispositivo.

PIX

PIX può essere utilizzato anche per profilare le applicazioni Unity. Sono disponibili anche istruzioni dettagliate su come usare e installare PIX per HoloLens 2. In una build di sviluppo, gli stessi ambiti visualizzati nel Frame Debugger di Unity verranno visualizzati anche in PIX e potranno essere ispezionati e profilati in modo più dettagliato.

Nota

Unity consente di modificare facilmente la risoluzione di destinazione del rendering dell'applicazione in fase di esecuzione tramite la proprietà XRSettings.renderViewportScale . L'immagine finale presentata sul dispositivo ha una risoluzione fissa. La piattaforma campiona l'output a risoluzione più bassa per creare un'immagine a risoluzione più alta per il rendering sugli schermi.

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 agli oggetti GameObject al momento dell'inizializzazione perché le chiamate di funzione ripetute come GetComponent<T>() e Camera.main sono più costose rispetto al costo di memoria per archiviare un puntatore. Camera.main utilizza solo FindGameObjectsWithTag() sotto, che cerca in modo costoso 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(), ci sono una manciata di overload diversi. È importante usare sempre le implementazioni basate su tipo e mai l'overload di ricerca basato su stringhe. La ricerca per stringa nella scena è più costosa della ricerca per tipo.
(Buono) Componente: GetComponent(Type type)
(Buono) T GetComponent<T>()
(Cattivo) Componente GetComponent(string)>

Evitare operazioni costose

  1. Evitare l'uso di LINQ

    Sebbene LINQ possa essere pulito e facile da leggere e scrivere, in genere richiede più calcolo e memoria rispetto a quando si scrive manualmente l'algoritmo.

    // 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 comuni per unità

    Alcune API Unity, sebbene utili, possono essere costose da eseguire. La maggior parte di queste comporta la ricerca di un elenco di GameObject corrispondenti nell'intero grafico della scena. Queste operazioni possono essere generalmente 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() dovrebbero essere eliminati a tutti i costi. Queste funzioni possono essere dell'ordine di 1000 volte più lente delle chiamate di funzione dirette.

  1. Attenzione alla boxe

    La boxing è un concetto fondamentale del linguaggio e del runtime C#. È il processo di wrapping di variabili chartipizzate con valori come , int, , boolecc. in variabili tipizzate di riferimento. Quando una variabile tipizzata viene "inscatolata", viene racchiusa in un System.Object, che viene archiviato nell'heap gestito. La memoria viene allocata e alla fine, una volta smaltita, deve essere elaborata dal Garbage Collector. Queste allocazioni e deallocazioni comportano un costo in termini di prestazioni e in molti scenari non sono necessarie o possono essere facilmente sostituite da un'alternativa meno costosa.

    Per evitare la boxing, accertarsi che le variabili, i campi e le proprietà in cui si memorizzano i tipi numerici e le strutture (tra cui Nullable<T>) siano tipizzati come tipi specifici, ad esempio int, float? o MyStruct, anziché usare object. Se si inseriscono questi oggetti in un elenco, assicurarsi di usare un elenco fortemente tipizzato come List<int> piuttosto che List<object> o ArrayList.

    Esempio di boxe in C#

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

Ripetizione di percorsi di codice

Tutte le funzioni di callback ripetute di Unity (ad esempio Update) che vengono eseguite molte volte al secondo e/o in ogni frame devono essere scritte con attenzione. Qualsiasi operazione costosa qui ha un impatto enorme e costante sulle prestazioni.

  1. Funzioni di callback vuote

    Anche se il codice seguente può sembrare innocente da lasciare nell'applicazione, soprattutto perché ogni script di 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 ha centinaia di GameObject con componenti con callback Unity ripetuti vuoti.

    void Update()
    {
    }
    

Nota

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

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

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

    1. È buona norma avere una classe o un servizio Singleton dedicato per gestire il raycast dello sguardo nella scena e quindi riutilizzare questo risultato in tutti gli altri componenti della scena, invece di eseguire operazioni Raycast ripetute e identiche da ciascun componente. Alcune applicazioni possono richiedere raycast da origini diverse o contro LayerMask diversi.
        UnityEngine.Physics.Raycast()
        UnityEngine.Physics.RaycastAll()
    
    1. Evitare operazioni GetComponent() in callback Unity ripetuti come Update() memorizzando nella cache i riferimenti in Start() o Awake()
        UnityEngine.Object.GetComponent()
    
    1. È buona norma creare un'istanza di tutti gli oggetti, se possibile, al momento dell'inizializzazione e usare il pool di oggetti per riciclare e riutilizzare i GameObject durante il runtime dell'applicazione
        UnityEngine.Object.Instantiate()
    
  2. Evita interfacce e costrutti virtuali

    Richiamare chiamate di funzione tramite interfacce rispetto a oggetti diretti o chiamare funzioni virtuali può spesso essere molto più costoso rispetto all'uso di costrutti diretti o chiamate di funzione dirette. Se la funzione o l'interfaccia virtuale non è necessaria, deve essere rimossa. Tuttavia, l'impatto sulle prestazioni di questi approcci vale il compromesso se l'utilizzo semplifica la collaborazione allo sviluppo, la leggibilità del codice e la manutenibilità del codice.

    In generale, la raccomandazione è di non contrassegnare campi e funzioni come virtuali a meno che non si preveda chiaramente che questo membro debba essere sovrascritto. Si dovrebbe prestare particolare attenzione ai percorsi di codice ad alta frequenza che vengono chiamati molte volte per fotogramma o anche una volta per fotogramma, come un UpdateUI() metodo.

  3. Evita di passare le strutture per valore

    A differenza delle classi, le strutture sono tipi di valore e quando vengono passate direttamente a una funzione, il loro contenuto viene copiato in un'istanza appena creata. Questa copia aggiunge il costo della CPU e più memoria sullo stack. Per le strutture piccole, l'effetto è minimo e quindi accettabile. Tuttavia, per le funzioni richiamate ripetutamente ogni frame e le funzioni che accettano strutture di grandi dimensioni, se possibile modificare la definizione della funzione in modo che passi per riferimento. Altre informazioni sono disponibili qui

Varie

  1. Fisica

    1. In generale, il modo più semplice per migliorare la fisica è limitare la quantità di tempo dedicato alla fisica o il numero di iterazioni al secondo. Ciò riduce l'accuratezza della simulazione. Vedi TimeManager in Unity.

    2. I tipi di collisori in Unity hanno caratteristiche prestazionali molto diverse. L'ordine seguente elenca i collisori più performanti e i collisori meno performanti da sinistra a destra. È importante evitare i Mesh Collider, che sono sostanzialmente più costosi dei collisori primitivi.

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

  2. Azioni

    Disabilita le animazioni inattive disabilitando il componente Animator (disabilitare l'oggetto di gioco non avrà lo stesso effetto). Evita i modelli di progettazione in cui un animatore si trova in un ciclo impostando un valore sullo stesso valore. C'è un notevole sovraccarico per questa tecnica, senza alcun effetto sull'applicazione. Scopri di più qui.

  3. Algoritmi complessi

    Se la tua applicazione utilizza algoritmi complessi come la cinematica inversa, la ricerca del percorso, ecc., cerca di trovare un approccio più semplice o regola le impostazioni pertinenti per le loro prestazioni.

Consigli sulle prestazioni da CPU a GPU

Generalmente, le prestazioni da CPU a GPU dipendono dalle chiamate di disegno inviate alla scheda grafica. Per migliorare le prestazioni, le chiamate di estrazione devono essere strategicamente a) ridotte o b) ristrutturate per ottenere risultati ottimali. Poiché le chiamate di estrazione stesse richiedono molte risorse, ridurle ridurrà il lavoro complessivo richiesto. Inoltre, i cambiamenti di stato tra le chiamate di disegno richiedono costosi passaggi di convalida e traduzione nel driver grafico e, pertanto, la ristrutturazione delle chiamate di disegno dell'applicazione per limitare i cambiamenti di stato (ad esempio materiali diversi, ecc.) può aumentare le prestazioni.

Unity ha un ottimo articolo che fornisce una panoramica e si tuffa nelle chiamate di estrazione in batch per la loro piattaforma.

Rendering con istanza a passaggio singolo

Il rendering con istanza a passaggio singolo in Unity consente di ridurre le chiamate di estrazione per ciascun occhio a una chiamata di estrazione 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. Apri le impostazioni di OpenXR (vai a Modifica>impostazioni> progettoGestione plug-in> XROpenXR).
  2. Selezionare Istanza a passaggio singolo dal menu a discesa Modalità di rendering .

Leggi gli articoli seguenti di Unity per informazioni dettagliate su questo approccio di rendering.

Nota

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

Batch statico

Unity è in grado di eseguire il batch di 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 tutti contrassegnati come statici (seleziona un oggetto in Unity e seleziona la casella di controllo in alto a destra dell'ispezione). I GameObject contrassegnati come statici non possono essere spostati nel runtime dell'applicazione. Pertanto, l'invio in batch statico può risultare difficile da applicare in HoloLens, in cui praticamente ogni oggetto deve essere posizionato, spostato, ridimensionato e così via. Per i visori VR immersivi, l'invio statico in batch può ridurre notevolmente le chiamate di pescaggio e quindi migliorare le prestazioni.

Per altri dettagli, vedere Static Batching in Draw Call Batching in Unity .

Batch dinamico

Poiché è problematico contrassegnare gli oggetti come statici per lo sviluppo di HoloLens, l'invio dinamico in batch può essere un ottimo strumento per compensare questa mancanza. Può essere utile anche su visori VR immersivi. Tuttavia, l'invio dinamico in batch in Unity può essere difficile da abilitare perché i GameObject devono a) condividere lo stesso materiale e b) soddisfare un lungo elenco di altri criteri.

Per l'elenco completo, vedere Dynamic Batching in Draw Call Batching in Unity . Più comunemente, i GameObject non sono più validi per essere raggruppati in batch in modo dinamico, perché i dati 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, questo viene bloccato dalla necessità per GameObjects di avere una texture univoca per il rispettivo materiale. È comune combinare le texture in un'unica grande texture, un metodo noto come atlantizzazione delle texture.

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

Nota

La modifica delle proprietà di Renderer.material in fase di esecuzione crea una copia del materiale e quindi potrebbe interrompere il batching. Usa Renderer.sharedMaterial per modificare le proprietà dei materiali condivisi tra GameObjects.

Consigli sulle prestazioni della GPU

Larghezza di banda e velocità di riempimento

Durante il rendering di un frame sulla GPU, un'applicazione è associata alla larghezza di banda della memoria o alla velocità di riempimento.

  • La larghezza di banda della memoria è la velocità di lettura e scrittura che la GPU può eseguire dalla memoria
  • Il tasso 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à dell'ologramma. Quando si abilita la riproiezione 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 la potenza) associata al traffico del buffer di profondità. Questo può essere un grande miglioramento sia nella riduzione della potenza che nelle prestazioni. Tuttavia, ci sono due possibili risultati negativi usando il formato di profondità a 16 bit.

Combattimento Z

La fedeltà ridotta dell'intervallo di profondità rende più probabile che si verifichi z-fighting a 16 bit rispetto a 24 bit. Per evitare questi artefatti, modifica i piani di ritaglio vicino/lontano della fotocamera Unity per tenere conto della precisione inferiore. Per le applicazioni basate su HoloLens, un piano di ritaglio lontano di 50 m invece dei 1.000 m predefiniti di Unity può in genere eliminare qualsiasi combattimento z.

Buffer stencil disabilitato

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

Evitare gli effetti a schermo intero

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

Impostazioni di illuminazione ottimali

L'illuminazione globale in tempo reale in Unity può fornire risultati visivi eccezionali, ma comporta costosi calcoli di illuminazione. È consigliabile disabilitare l'illuminazione globale in tempo reale per ogni file di scena di Unity tramitele impostazioni> di illuminazionedel rendering delle>finestre> Deselezionare l'illuminazione globale in tempo reale.

Inoltre, si consiglia di disabilitare tutta la proiezione di ombre in quanto questi aggiungono anche costosi passaggi GPU su una scena Unity. Le ombre possono essere disabilitate per ogni luce, ma possono anche essere controllate in modo olistico tramite le impostazioni di qualità.

Modifica>Impostazioni progetto, quindi seleziona la categoria >Qualità e seleziona Qualità bassa per la piattaforma UWP. Si può anche semplicemente impostare la proprietà Ombre su Disabilita ombre.

Ti consigliamo di usare l'illuminazione baked con i tuoi modelli in Unity.

Ridurre il numero di poligoni

Il numero di poligoni viene ridotto di

  1. Rimozione di oggetti da una scena
  2. Decimazione delle risorse, che riduce il numero di poligoni per una data mesh
  3. Implementazione di un sistema Level of Detail (LOD) 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 le prestazioni degli shader consiste nell'identificare il numero medio di operazioni eseguite ciascuno in fase di esecuzione. Questa operazione può essere eseguita facilmente in Unity.

  1. Seleziona la risorsa shader o seleziona un materiale, quindi nell'angolo in alto a destra della finestra di controllo. Seleziona l'icona a forma di 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 di controllo.

    Compilare il codice shader in Unity

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

    Operazioni di shader Standard Unity

Ottimizza i pixel shader

Esaminando i risultati delle statistiche compilati 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 vertice di tutte le mesh disegnate sullo schermo.

Pertanto, non solo i fragment shader hanno più istruzioni dei vertex shader a causa di tutti i calcoli di illuminazione, ma 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 di volte. Se si esegue il rendering di due occhi, questo numero raddoppia perché ci sono 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 miglioramenti delle prestazioni molto maggiori rispetto alle ottimizzazioni nel vertex shader.

Alternative shader Unity Standard

Invece di utilizzare un Physically Based Rendering (PBR) o un altro shader di alta qualità, cerca di utilizzare uno shader più performante ed economico. Il toolkit per realtà mista fornisce lo shader standard MRTK ottimizzato per i progetti di realtà mista.

Unity fornisce anche opzioni di shader non illuminato, vertice, diffuso e altre opzioni di shader semplificate che sono più veloci rispetto allo shader Unity Standard. Per ulteriori informazioni, vedere Utilizzo e prestazioni degli shader predefiniti.

Precaricamento shader

Usa il precaricamento dello shader e altri accorgimenti per ottimizzare il tempo di caricamento dello shader. In particolare, il precaricamento dello shader significa che non si verificheranno intoppi dovuti alla compilazione dello shader di runtime.

Limita lo scoperto

In Unity, è possibile visualizzare l'overdraw per la propria scena, attivando o disattivando il menu della modalità di disegno nell'angolo in alto a sinistra della vista Scena e selezionando Overdraw.

In genere, l'overdraw può essere mitigato eliminando gli oggetti in anticipo prima che vengano inviati alla GPU. Unity fornisce informazioni dettagliate sull'implementazione dell'eliminazione dell'occlusione per il motore.

Suggerimenti per la memoria

Le operazioni di allocazione & deallocazione della memoria eccessive possono avere effetti negativi sull'applicazione olografica, con conseguente prestazioni incoerenti, fotogrammi bloccati e altri comportamenti dannosi. È particolarmente importante comprendere le considerazioni sulla memoria quando si sviluppa in Unity poiché la gestione della memoria è controllata dal Garbage Collector.

Garbage Collection

Le app olografiche perdono il tempo di elaborazione del calcolo a favore del 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 e le deallocazioni costanti richiedono in genere che il Garbage Collector venga eseguito più frequentemente, danneggiando così le prestazioni e l'esperienza utente.

Una delle procedure più comuni che porta a un'eccessiva operazione di Garbage Collection è non 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 come Update() o LateUpdate().

Altri suggerimenti rapidi:

  • Usare la classe StringBuilder C# per compilare dinamicamente stringhe complesse in fase di esecuzione.
  • Rimuovere le chiamate a Debug.Log() quando non sono più necessarie, poiché vengono ancora eseguite in tutte le versioni della build 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 di transizione.

Pooling di oggetti

Il pooling di oggetti è una tecnica popolare per ridurre il costo dell'allocazione e della deallocazione continua degli oggetti. Questa operazione viene eseguita allocando un grande pool di oggetti identici e riutilizzando le istanze inattive disponibili di questo pool invece di generare ed eliminare 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

Prendi in considerazione l'idea di iniziare l'app con una scena più piccola, quindi di usare SceneManager.LoadSceneAsync per caricare il resto della scena. Ciò consente all'app di raggiungere uno stato interattivo il più rapidamente possibile. È possibile che si verifichi un picco della CPU durante l'attivazione della nuova scena e che il contenuto di cui viene eseguito il rendering possa causare interruzioni o intoppi. Un modo per risolvere il problema consiste nell'impostare la proprietà AsyncOperation.allowSceneActivation su "false" nella scena in fase di caricamento, attendere il caricamento della scena, cancellare lo schermo in nero e quindi impostarla nuovamente su "true" per completare l'attivazione della scena.

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