Ottimizzazione Vulkan di Godot Engine per Android

L'immagine della mascotte di Godot Engine

Panoramica

Godot Engine è un popolare motore di gioco open source multipiattaforma con un solido supporto per Android. Godot può essere utilizzato per creare giochi di quasi tutti i generi ed è in grado di gestire sia la grafica 2D che quella 3D. La versione 4 di Godot ha introdotto un nuovo sistema di rendering con funzionalità avanzate per una grafica ad alta fedeltà. Il visualizzatore Godot 4 è progettato per API di grafica moderne come Vulkan.

La Godot Foundation ha coinvolto gli esperti di ottimizzazione della grafica di The Forge Interactive e ha collaborato con Google per analizzare e migliorare ulteriormente il renderer Vulkan di Godot 4 e unire queste ottimizzazioni nel repository del progetto. Le ottimizzazioni aiutano gli sviluppatori a migliorare i visualizzatori Vulkan personalizzati su Android.

Metodologia e risultati dell'ottimizzazione

Il processo di ottimizzazione ha utilizzato due diverse scene 3D in Godot come target di benchmarking. Il tempo di rendering delle scene è stato misurato su più dispositivi durante ogni iterazione dell'ottimizzazione. Per poter essere incluse, le modifiche al visualizzatore dovevano mostrare miglioramenti delle prestazioni su almeno alcuni dei dispositivi testati e non potevano introdurre regressioni delle prestazioni su nessun dispositivo.

Durante i test sono state utilizzate diverse architetture GPU Android di uso comune. Sebbene molte ottimizzazioni abbiano apportato miglioramenti generali, alcune hanno avuto un impatto maggiore su architetture GPU specifiche. La somma totale di tutto il lavoro di ottimizzazione ha comportato una riduzione generale del 10-20% dei tempi di frame della GPU.

Ottimizzazione generale di Vulkan

The Forge ha eseguito il refactoring dell'architettura generale sul backend di rendering Godot Vulkan per migliorare le prestazioni e aiutare il backend a scalare con l'aumento della domanda di rendering dei contenuti. Le ottimizzazioni non sono specifiche per l'hardware mobile, ma sono vantaggiose per tutte le piattaforme Godot Vulkan.

Supporto dell'offset UBO dinamico

Quando esegui il binding di un insieme di descrittori contenente un oggetto buffer uniforme dinamico (UBO), Vulkan consente di specificare gli offset dinamici nell'UBO nei parametri di binding. Questa funzionalità può essere utilizzata per imballare i dati per più operazioni di rendering in un unico UBO, ricollegando l'insieme di descrittori con un offset dinamico diverso per selezionare i dati appropriati per lo shader. Il visualizzatore Godot Vulkan è stato aggiornato per poter utilizzare gli offset dinamici anziché inizializzare sempre l'offset su zero. Questo miglioramento consente di ottimizzare l'efficienza in futuro.

Pool di set di descrittori lineari

In precedenza, il comportamento predefinito nel renderer Godot Vulkan era la creazione di tutti i pool di set di descrittori utilizzando il flag VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, il che significa che le allocazioni del set di descrittori potevano essere liberate nel pool e che potevano essere effettuate riassegnazioni dal pool. Questa modalità impone un ovverhead aggiuntivo rispetto ai pool di set di descrittori che consentono solo l'allocazione lineare seguita da un ripristino totale del pool.

Ove possibile, i pool di set di descrittori vengono ora creati come pool lineari senza impostare VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT. I pool lineari vengono quindi resettati nel loro insieme, se necessario. Questo lavoro include anche un'ottimizzazione aggiuntiva per la associazione in batch degli insiemi di descrittori, se possibile, riducendo il numero di chiamate discrete a vkCmdBindDescriptorSets().

Supporto per i sampler immutabili

Gli oggetti sampler che contengono dati di configurazione del campionamento sono tradizionalmente associati come parte dei dati dell'insieme di descrittori. Questo metodo consente di eseguire lo scambio dinamico degli oggetti sampler nei dati del set di descrittori. Vulkan supporta anche i campionatori immutabili, che codificano i dati del campionatore direttamente nel layout dell'insieme di descrittori. Questa configurazione del sampler viene vincolata durante la creazione del set di descrittori e dello stato della pipeline e non può essere modificata dopo la creazione.

I sampler immutabili sacrificano la flessibilità per non dover più gestire e associare oggetti sampler discreti. Il renderer Vulkan di Godot è stato aggiornato per supportare l'utilizzo di campionatori immutabili. L'utilizzo dei campionatori è stato modificato in modo da utilizzare campionatori immutabili, ove possibile.

Ottimizzazione incentrata sui dispositivi mobili

Sono state implementate ottimizzazioni aggiuntive per migliorare in modo specifico le prestazioni del rendering sull'hardware grafico mobile. In genere, le ottimizzazioni non sono pertinenti per l'hardware grafico di classe desktop a causa di design architettonici diversi.

Le ottimizzazioni includono:

  • Sostituire l'utilizzo di costanti push di grandi dimensioni
  • Allocazione del buffer lazy
  • Supporto del buffer permanente
  • Modifica della modalità di decodifica ASTC
  • Pre-rotazione dello schermo

Sostituire l'utilizzo di costanti push di grandi dimensioni

Le costanti push sono una funzionalità che consente di iniettare valori costanti per il programma shader attivo nell'area dati dei comandi. Le costanti push sono pratiche perché non richiedono la creazione e la compilazione di un buffer e non sono legate ai descrittori. Tuttavia, le costanti push hanno una dimensione massima limitata e possono influire negativamente sulle prestazioni sull'hardware mobile.

Durante i test sui dispositivi Android, le prestazioni sono state migliorate sostituendo l'utilizzo costante di push di più di 16 byte con buffer uniformi. Gli shader che utilizzavano fino a 16 byte di dati costanti avevano un rendimento migliore con le costanti push. Oltre alle considerazioni sul rendimento, alcune schede grafiche hanno requisiti minimi di allineamento di 64 byte per gli buffer uniformi, il che riduce l'efficienza della memoria a causa del padding inutilizzato rispetto all'utilizzo delle costanti push.

Allocazione del buffer lazy

La maggior parte dell'hardware grafico mobile utilizza un'architettura di rendering differito basato su riquadri (TBDR). Le GPU che utilizzano TBDR suddividono la regione dello schermo più grande in una griglia di riquadri più piccoli e eseguono il rendering su base riquadro. Ogni riquadro è supportato da una piccola quantità di RAM ad alta velocità utilizzata per lo spazio di archiviazione dalla GPU quando la GPU esegue il rendering di un riquadro. Con TBDR, i target di rendering che non vengono mai campionati da un altro target al di fuori del loro passaggio di rendering possono rimanere interamente nella RAM dei riquadri e non richiedono un buffer per un archivio di supporto della memoria principale.

VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT è stato aggiunto durante la creazione di target di rendering appropriati, come i target di colore e profondità principali, per evitare di allocare memoria del buffer che non verrebbe mai utilizzata. È stato misurato che il risparmio di memoria dell'allocazione lazy nelle scene di esempio può arrivare a circa 50 megabyte di RAM.

Supporto del buffer permanente

L'hardware mobile utilizza un'architettura di memoria unificata (UMA) anziché una differenziazione hardware tra RAM principale e RAM grafica. Quando la RAM principale e la RAM grafica sono separate, i dati devono essere trasferiti dalla RAM principale alla RAM grafica per essere utilizzati dalla GPU. Godot implementa già questo processo di trasferimento nel suo renderer Vulkan tramite l'uso di buffer di staging. Su hardware UMA,un buffer di staging non è necessario per molti tipi di dati; la memoria può essere utilizzata sia dalla CPU che dalla GPU. Forge ha implementato il supporto per i buffer condivisi permanenti sull'hardware supportato per eliminare lo staging, se possibile.

Immagini di una scena Godot che mostrano informazioni di profilazione con e senza attivati i buffer permanenti.
Figura 1. Profilazione delle differenze tra i buffer permanenti attivati e disattivati in una scena di esempio.

Modifica della modalità di decodifica ASTC

La compressione delle texture adattabile e scalabile (ASTC) è il formato di compressione delle texture moderno preferito sull'hardware mobile. Durante la decompressione, per impostazione predefinita la GPU potrebbe decodificare i texels in un valore intermedio con una precisione maggiore di quella necessaria per la fedeltà visiva, con una conseguente perdita di efficienza nella creazione delle texture. Se supportata dall'hardware di destinazione, l'estensione VK_EXT_astc_decode_mode viene utilizzata per specificare valori non normalizzati a 8 bit per componente durante la decodifica anziché valori in virgola mobile a 16 bit.

Pre-rotazione dello schermo

Per prestazioni ottimali quando si utilizza Vulkan su Android, i giochi devono riconciliare l'orientamento dello schermo del dispositivo con l'orientamento della superficie di rendering. Questa procedura è nota come pre-rotazione. La mancata rotazione preliminare può portare a un rendimento ridotto perché il sistema operativo Android deve aggiungere un passaggio del compositore per ruotare manualmente le immagini. Il renderer Godot ora supporta la prerotazione su Android.

Miglioramenti al debug

Oltre a ottimizzare le prestazioni, The Forge ha migliorato l'esperienza di debug dei problemi di grafica nel renderer di Godot con le seguenti aggiunte:

  • Estensione per guasti del dispositivo
  • Breadcrumb
  • Indicatori di debug

Estensione per guasti del dispositivo

Quando la GPU riscontra un problema durante le operazioni di rendering, il driver Vulkan può restituire un risultato VK_ERROR_DEVICE_LOST da una chiamata all'API Vulkan. Per impostazione predefinita, non vengono fornite ulteriori informazioni sul contesto del motivo per cui il conducente ha restituitoVK_ERROR_DEVICE_LOST. L'estensione VK_EXT_device_fault offre un meccanismo per consentire al conducente di fornire informazioni aggiuntive sulla natura del guasto. Godot ha aggiunto il supporto per l'attivazione dell'estensione di errore del dispositivo (se disponibile) e per la segnalazione delle informazioni restituite dal driver.

Il debug di un arresto anomalo della GPU o di un blocco dell'esecuzione può essere complicato. Per aiutare a identificare i contenuti grafici che potrebbero essere stati visualizzati al momento di un errore, è stato aggiunto il supporto dei breadcrumb al motore di rendering di Godot. I breadcrumb sono valori definiti dall'utente che possono essere associati ai contenuti negli elenchi di disegno nel grafo di rendering. I dati del breadcrumb vengono scritti prima dell'avvio di un nuovo passaggio di rendering. Se si verifica un arresto anomalo o un blocco dell'esecuzione, il valore corrente del breadcrumb può essere utilizzato per determinare quali dati potrebbero aver causato il problema.

Indicatori di debug

Gli indicatori di debug, se supportati dal driver, vengono utilizzati per assegnare un nome alle risorse. In questo modo, è possibile associare stringhe leggibili dall'utente a operazioni come le pass di rendering e a risorse come buffer e texture quando si utilizza uno strumento grafico come RenderDoc. È stato aggiunto il supporto delle annotazioni degli indicatori di debug al visualizzatore Godot Vulkan.

Blog di Godot Engine - Aggiornamento sulla collaborazione con Google e The Forge

Pull request di collaborazione Vulkan per Godot Engine