Suggerimenti per l'ottimizzazione di CPU e GPU

Questo documento mostra come ottimizzare le prestazioni del gioco utilizzando strumenti per identificare e risolvere i colli di bottiglia della CPU e della GPU.

Ottimizzazione della CPU

Se l'analisi mostra che il gioco è CPU-bound, sono necessari ulteriori accertamenti. Ciò richiede l'identificazione dei thread o delle API specifici che causano colli di bottiglia e riducono gli FPS.

Per l'ottimizzazione della CPU, una soluzione universale in genere non è efficace. Devi invece identificare il carico di lavoro più impegnativo in base al gioco o alla scena e ottimizzare la logica e le funzioni pertinenti.

Strumenti di tracciamento della sincronizzazione del motore grafico

I seguenti strumenti possono aiutarti in questa analisi:

Approfondimenti irreali

All'interno dei progetti Unreal Engine, lo strumento Unreal Insight facilita l'analisi delle informazioni di traccia di temporizzazione per i singoli thread che compongono un frame.

Ad esempio, GameThread utilizza in genere la percentuale più elevata di tempo della CPU, principalmente attribuibile a Tick Time. Inoltre, una parte considerevole del tempo di utilizzo viene consumata dalle attività associate a FActorComponentTickFunction.

Per ottimizzare FActorComponentTick, è fondamentale escludere i calcoli e implementare l'eliminazione per i personaggi e gli oggetti posizionati al di fuori del campo visivo della videocamera. Inoltre, l'utilizzo di animazioni basate sul livello di dettaglio può portare a ulteriori miglioramenti del rendimento.

Cronologia della traccia di Unreal Insight che mostra i tempi di esecuzione di GameThread, RenderThread e RHIThread
Traccia di Unreal Insight con GameThread, RenderThread e RHIThread (fai clic per ingrandire).

Unity Profiler (Unity)

L'analisi tramite Unity Profiler rivela che il thread principale consuma oltre 45 ms, con PostLateUpdate.FinishFrameRendering che occupa 16,23 ms, il che lo rende l'operazione più dispendiosa in termini di tempo. All'interno di questo, vengono osservate più invocazioni di Inl_RenderCameraStack. È consigliabile verificare la necessità delle videocamere attive e ottimizzarle di conseguenza.

Sequenza temporale di Unity Profiler che mostra il thread principale in attesa di Gfx.WaitForPresentOnGfxThread
Esempio di GPU bound per Unity Profiler (fai clic per ingrandire).

Strumenti di profilazione a livello di sistema

Utilizza i seguenti strumenti di profilazione:

Perfetto

Utilizzando la traccia Perfetto, puoi determinare le assegnazioni dei core della CPU e i dettagli di esecuzione di ogni thread su un dispositivo Android. Ciò consente di identificare i colli di bottiglia delle prestazioni analizzando i dati di esecuzione dei thread.

Scenario di overhead della CPU

La traccia indica che il carico di lavoro su GameThread e RenderThread sta causando ritardi in QueuePresent di RHI Thread, il che porta a uno scenario CPU-bound, basato su VSync.

Traccia Perfetto che mostra i tempi di esecuzione di GameThread, RenderThread e RHIThread
Tracce Perfetto con dettagli di esecuzione della CPU (fai clic per ingrandire).

Scenario di overhead della GPU

La traccia indica che il completamento della GPU supera i 25 ms, il che indica uno scenario vincolato alla GPU.

Traccia Perfetto che mostra il blocco di completamento della GPU in attesa del completamento della GPU
Tracce Perfetto con dettagli sull'overhead della GPU (fai clic per ingrandire).

Simpleperf

Per identificare le funzioni con l'utilizzo attuale più elevato della CPU, è possibile utilizzare simpleperf. Per risultati ottimali, ti consigliamo di ordinare queste funzioni in modo da dare la priorità a quelle con il maggior utilizzo.

Output di Simpleperf che mostra le funzioni con il maggiore utilizzo della CPU
Profilazione della CPU Simpleperf: analisi della gerarchia delle chiamate di funzione e dell'utilizzo delle risorse (fai clic per ingrandire).

Simpleperf ti aiuta a esaminare i dati sulle funzioni che utilizzano più tempo CPU. Per ottimizzare l'utilizzo della CPU, inizia con le funzioni che utilizzano più CPU. In questo esempio, USkeletalMeshComponent, associato all'animazione in ActorComponentTickFunctions, utilizza la maggior parte della CPU.

Ottimizzazione della GPU

Se l'analisi mostra che il gioco è vincolato alla GPU, è essenziale un'ulteriore indagine. Ciò richiede l'utilizzo di vari strumenti e tecniche per l'ottimizzazione e l'analisi della GPU.

Per ottimizzare la GPU, utilizza un debugger di frame per analizzare la pipeline di rendering e le chiamate di disegno per ogni scena. Inoltre, devi comprendere a fondo l'architettura della GPU e il comportamento della pipeline per identificare operazioni non necessarie o aree da ottimizzare.

Le sezioni seguenti descrivono metodi e strumenti per l'ottimizzazione della GPU.

Elimina i RenderPass non necessari

Per migliorare le prestazioni di rendering e ridurre il carico di lavoro della GPU, elimina i passaggi di rendering non necessari. Sono inclusi tutti i pass di rendering che non hanno chiamate di disegno o il cui output non viene utilizzato nel frame finale.

Utilizza un debugger GPU, ad esempio RenderDoc, per analizzare la pipeline di rendering e identificare le opportunità di ottimizzazione.

  1. Nessuna chiamata di disegno: controlla se la pass di rendering include chiamate di disegno. Se non ha chiamate di estrazione, rimuovi la tessera.

  2. Output inutilizzato:verifica se le passate successive accedono o visualizzano gli output delle passate di rendering, ad esempio colore o profondità. In caso contrario, rimuovi la tessera.

  3. Tessere unificabili:identifica le tessere che puoi unire:

    • Stesso framebuffer o allegati
    • Operazioni di caricamento o archiviazione compatibili
    • Nessuna barriera di dipendenza nel mezzo
Browser degli eventi RenderDoc che mostra i passaggi di rendering e le chiamate di disegno Vulkan
Sequenza di comandi RenderPass e GPU in RenderDoc (fai clic per ingrandire).

Ridurre al minimo le operazioni di caricamento o archiviazione

Le operazioni di caricamento o archiviazione richiedono molte risorse perché utilizzano molta memoria. Ridurre al minimo le operazioni di caricamento e archiviazione non necessarie. Esegui queste azioni solo quando sono necessari allegati all'interno di un RenderPass. In caso contrario, sostituiscili con operazioni Clear o Don't care per ridurre l'overhead.

Come ottimizzare

Utilizza un debugger GPU, ad esempio RenderDoc, per analizzare la pipeline di rendering e identificare le seguenti opportunità di ottimizzazione:

  1. Caricamento:se un allegato di pass di rendering non utilizza i dati di un pass o di un allegato precedente, un'operazione di caricamento non è necessaria. In questi casi, l'utilizzo di Don't care o Clear può ridurre il sovraccarico.

  2. Store:se un allegato di passaggio di rendering non viene utilizzato dopo il passaggio di rendering corrente, l'operazione di archiviazione non è necessaria. In questi casi, utilizza Don't care o Clear.

  3. Sostituisci:determina se le impostazioni di caricamento o memorizzazione correnti possono essere sostituite da Clear o Don't Care senza influire sul frame finale.

Browser degli eventi e ispettore delle risorse di RenderDoc che analizzano il layout delle immagini e le passate di rendering
Analisi della pipeline di rendering di RenderDoc (fai clic per ingrandire).

Evita l'eliminazione per attivare Early-Z

Early-Z migliora le prestazioni sulle piattaforme mobile. Tuttavia, un'istruzione discard all'interno di uno shader disattiva automaticamente Early-Z. Se l'istruzione discard non è essenziale, rimuovila.

Accelerazione Early-Z

Questa ottimizzazione riduce notevolmente le operazioni dello shader di framenti e migliora le prestazioni della GPU.

Early-Z Test di profondità e stencil

Tabella che confronta le metriche di prestazioni di CPU e GPU quando Early-Z è abilitato e disabilitato
Impatto sulle prestazioni dell'accelerazione Early-Z (fai clic per ingrandire).

Come ottimizzare

Utilizza un debugger GPU, ad esempio RenderDoc, per analizzare la pipeline di rendering e identificare le seguenti opportunità di ottimizzazione:

  1. Utilizzo di discard negli shader di frammenti: la parola chiave discard impedisce alla GPU di eseguire test di profondità anticipati perché la visibilità del frammento non è nota in anticipo.

  2. Modifica di gl_FragDepth: la modifica dinamica di gl_FragDepth cambia la profondità di un frammento, il che disattiva l'ottimizzazione Early-Z perché la profondità finale è sconosciuta prima dell'elaborazione del frammento.

  3. Alpha-to-coverage attivato:quando alpha-to-coverage è attivato (spesso utilizzato nel rendering MSAA), la copertura dei frammenti dipende dai valori alfa. Ciò può ritardare il test della profondità e disattivare Early-Z.

Confronto dei frammenti per pixel con e senza la parola chiave dello shader di eliminazione
Debugger GPU RenderDoc per l'analisi (fai clic per ingrandire).

Ottimizzare il formato della texture

La selezione ottimale del formato della texture riduce il consumo di memoria, migliora l'efficienza della larghezza di banda e le prestazioni di rendering. L'utilizzo di formati con precisione eccessivamente elevata può sprecare risorse GPU senza fornire vantaggi visivi.

Come ottimizzare

Utilizza un debugger GPU, ad esempio RenderDoc, per analizzare la pipeline di rendering e identificare le seguenti opportunità di ottimizzazione:

  1. Utilizza D24S8 anziché D32S8 per i buffer di stencil di profondità:l'utilizzo di D24S8 per i buffer di stencil di profondità riduce il consumo di memoria del 20% rispetto a D32S8, con una differenza minima o nulla nella qualità visiva nella maggior parte delle applicazioni.
  2. Utilizza la compressione ASTC per le texture a colori:la compressione ASTC riduce significativamente l'utilizzo della memoria delle texture, fino a 8 volte rispetto ai formati non compressi, preservando al contempo un'elevata qualità visiva.
  3. Utilizza formati a metà precisione anziché a precisione singola: utilizza R16F o RG16F per ridurre la larghezza di banda della memoria e il consumo di spazio di archiviazione. Questi formati sono adatti ai buffer di post-elaborazione.

Ottimizzare la complessità della geometria

Ridurre al minimo la complessità geometrica migliora le prestazioni di rendering, in particolare sui dispositivi mobili con funzionalità GPU limitate. Ciò comporta l'utilizzo di un numero ridotto di vertici e triangoli, il consolidamento degli oggetti per ridurre le chiamate di disegno e l'eliminazione di geometrie non sottoposte a rendering o non necessarie. Tecniche come la semplificazione della mesh, il livello di dettaglio (LOD) e l'eliminazione del frustum o dell'occlusione possono ridurre significativamente il carico di lavoro della GPU e aumentare i frame rate.

Come ottimizzare

Utilizza strumenti di profilazione e debugger GPU, come RenderDoc, Android GPU Inspector o altri analizzatori delle prestazioni, per identificare i colli di bottiglia delle prestazioni correlati alla geometria.

  1. Riduci il conteggio dei triangoli:riduci al minimo l'utilizzo di poligoni, in particolare per gli oggetti piccoli o distanti.

  2. Utilizza il livello di dettaglio (LOD): in base alla distanza della videocamera, vengono utilizzate automaticamente mesh più semplici.

  3. Unisci mesh piccoli:consolida gli oggetti statici per ridurre le chiamate di disegno e il sovraccarico della CPU.

  4. Tronco di piramide e eliminazione dell'occlusione: evita di eseguire il rendering di oggetti che si trovano al di fuori della visuale o che sono oscurati da altri elementi.

Rimuovere gli allegati non necessari

Gli allegati dei pass di rendering (ad esempio colore, profondità, stencil) consumano larghezza di banda della memoria e risorse GPU, anche se non vengono utilizzati. La rimozione di allegati inutili o ridondanti migliora le prestazioni e riduce il consumo energetico, in particolare sulle piattaforme mobile.

Come ottimizzare

Utilizza strumenti di profilazione e debugger GPU, come RenderDoc, Android GPU Inspector o altri analizzatori delle prestazioni, per identificare i colli di bottiglia delle prestazioni correlati alla geometria.

  1. Controlla l'utilizzo effettivo:ci sono chiamate di disegno o shader che scrivono o leggono dall'allegato?
  2. Analizza l'output dei frame:utilizza RenderDoc o utilità simili per determinare se l'allegato contribuisce all'immagine finale.
  3. Prendi in considerazione allegati temporanei o fittizi:gli allegati temporanei o un'operazione di negozio "Non mi interessa" devono essere utilizzati per i dati temporanei che non richiedono un'archiviazione permanente.

Ottimizza la precisione degli shader

L'utilizzo di una precisione eccessivamente elevata (ad esempio highp anziché mediump o lowp) all'interno degli shader aumenta il carico di lavoro della GPU, il consumo energetico e la pressione dei registri, in particolare sulle GPU mobile. Utilizzando la precisione adeguata più bassa per le variabili (ad esempio posizioni, colori, UV), puoi migliorare le prestazioni senza un impatto visivo percepibile.

Tabella che confronta le metriche di rendimento della CPU e della GPU quando si utilizza la precisione dello shader mediump anziché highp
Impatto sulle prestazioni della precisione dello shader (fai clic per ingrandire).

Come ottimizzare

Utilizza strumenti di profilazione e debugger GPU come RenderDoc, Android GPU Inspector o altri analizzatori delle prestazioni per identificare i colli di bottiglia delle prestazioni correlati alla geometria.

  1. Esamina il codice dello shader:valuta le variabili dello shader e verifica che l'alta precisione venga utilizzata solo quando necessario, ad esempio per i calcoli di profondità o spazio schermo. Utilizza una precisione media o bassa per colori, coordinate UV o valori che non richiedono un'elevata precisione.

  2. Utilizza debugger GPU:le utilità di diagnostica, come RenderDoc o i profiler GPU per dispositivi mobili (ad esempio AGI, Mali/GPU Inspector), identificano l'utilizzo elevato dei registri o gli stalli degli shader associati a problemi di precisione.

Profiler di utilizzo variabile Mali che mostra l'interpolazione a 16 bit insieme al codice shader che utilizza mediump
Esempio di strumenti di profilazione e debugger GPU (fai clic per ingrandire).

Attivare il back-face culling

Il rendering dei triangoli rivolti verso l'esterno della videocamera (facce posteriori) spesso non è necessario per gli oggetti solidi.

Come ottimizzare

L'utilizzo di VK_CULL_MODE_NONE può influire negativamente sulle prestazioni perché costringe la GPU a eseguire il rendering delle facce anteriore e posteriore, il che aumenta il carico di lavoro di rendering.

Log dei comandi Vulkan che mostra vkCmdSetCullMode impostato su VK_CULL_MODE_NONE
Log di debug con eliminazione delle facce posteriori (fai clic per ingrandire).

Ridurre al minimo l'overdraw nelle scene dell'interfaccia utente

Elimina le chiamate di disegno e i passaggi di rendering non necessari, in particolare nelle scene dell'interfaccia utente, per migliorare le prestazioni di rendering e ridurre il carico di lavoro della GPU. Ad esempio, in una scena dell'interfaccia utente in cui viene eseguito il rendering del mondo intero prima di sovrapporre l'interfaccia utente sullo schermo, il rendering del mondo diventa ridondante.

Come ottimizzare

Utilizza un debugger GPU, ad esempio RenderDoc, per analizzare la pipeline di rendering e identificare le seguenti opportunità di ottimizzazione:

  1. Verifica l'assenza di scoperti superflui. Nei contesti dell'interfaccia utente, dove potrebbe essere visualizzata l'intera schermata, verifica che i passaggi di rendering precedenti non siano inutilmente sovradisegnati.
  2. Abilita il test di profondità e l'eliminazione per ottimizzare le prestazioni.
  3. Valuta la possibilità di eseguire il rendering in ordine dalla parte anteriore a quella posteriore.
Visualizzatore di eventi e visualizzatore di texture di RenderDoc che identificano un passaggio di rendering di overdraw non necessario
Esempio per eliminare le chiamate di disegno e i passaggi di rendering superflui (fai clic per ingrandire).