Raccolta del pacing del frame Parte del Kit di sviluppo dei giochi Android.

La libreria di pacing dei frame Android, nota anche come Stackpy, fa parte delle Librerie AGDK. Aiuta i giochi OpenGL e Vulkan a ottenere un rendering fluido e un pacing corretto del frame su Android. Questo documento definisce il pacing dei frame, descrive le situazioni in cui è necessario il pacing dei frame e mostra in che modo la libreria risponde a queste situazioni. Se vuoi passare direttamente all'implementazione del pacing del frame nel gioco, consulta il Passaggio successivo.

Premessa

Il Pacing dei frame è la sincronizzazione della logica e del loop di rendering di un gioco con il sottosistema di visualizzazione di un sistema operativo e l'hardware di visualizzazione sottostante. Il sottosistema di display Android è stato progettato per evitare artefatti visivi (noti come tearing) che possono verificarsi quando l'hardware del display passa a un nuovo frame durante un aggiornamento. Per evitare questi artefatti, il sottosistema di visualizzazione:

  • Memorizza internamente i buffer oltre i frame
  • Rileva gli invii in ritardo dei frame
  • Consente di ripetere la visualizzazione dei frame precedenti quando vengono rilevati frame tardivi

Un gioco comunica a SurfaceFlinger, il compositore all'interno del sottosistema display, che ha inviato tutte le chiamate di disegno necessarie per un frame (chiamando eglSwapBuffers o vkQueuePresentKHR). SurfaceFlinger segnala la disponibilità di un frame all'hardware del display tramite un blocco. L'hardware del display mostra quindi il frame specificato. L'hardware del display continua a funzionare a una frequenza costante, ad esempio a 60 Hz, e se non è presente un nuovo frame quando l'hardware ne ha bisogno, viene visualizzato di nuovo quello precedente.

Spesso si verificano intervalli di rendering incoerenti quando il rendering di un loop di rendering di un gioco viene eseguito a una frequenza diversa rispetto all'hardware display nativo. Se un gioco con velocità di 30 f/s tenta di eseguire il rendering su un dispositivo che supporta i 60 f/s in modo nativo, il loop di rendering del gioco non si rende conto che sullo schermo rimane un fotogramma ripetuto per altri 16 millisecondi in più. Questa disconnessione di solito crea un'incoerenza sostanziale nei tempi dei frame, ad esempio: 49 millisecondi, 16 millisecondi, 33 millisecondi. Le scene eccessivamente complesse aggravano ulteriormente il problema, in quanto causano la perdita di frame.

Soluzioni non ottimali

Le seguenti soluzioni per il pacing dei frame sono state utilizzate in passato dai giochi e in genere comportano una frequenza fotogrammi incoerente e una maggiore latenza dell'input.

Invia frame il più velocemente consentito dall'API di rendering

Questo approccio collega un gioco all'attività variabile di SurfaceFlinger e introduce un ulteriore frame di latenza. La pipeline di visualizzazione contiene una coda di frame, di solito di dimensione 2, che si riempie se il gioco cerca di presentare frame troppo rapidamente. Poiché non c'è più spazio nella coda, il ciclo di gioco (o almeno il thread di rendering) viene bloccato da una chiamata OpenGL o Vulkan. Il gioco viene quindi costretto ad attendere che l'hardware del display mostri un frame e questa pressione inversa sincronizza i due componenti. Questa situazione è nota come buffer-stuffing o queue-stuffing. Il processo di rendering non si rende conto di cosa sta succedendo, quindi l'incoerenza della frequenza fotogrammi peggiora. Se gli esempi di gioco vengono inseriti prima del frame, la latenza di input peggiora.

Usa Android Choreographer in autonomia

I giochi utilizzano anche Android Choreographer per la sincronizzazione. Questo componente, disponibile in Java dall'API 16 e in C++ dall'API 24, pubblica segni di graduazione regolari alla stessa frequenza del sottosistema di visualizzazione. C'è ancora qualche dettaglio in merito al momento in cui questo segno di spunta viene inviato rispetto all'effettiva VSYNC dell'hardware, che varia a seconda del dispositivo. Per i frame lunghi, potrebbe verificarsi comunque il buffer stuffing.

Vantaggi della libreria di pacing dei frame

La libreria di pacing del frame utilizza Android Choreographer per la sincronizzazione e gestisce la variabilità della pubblicazione dei segni di spunta. Utilizza i timestamp di presentazione per assicurarsi che i frame vengano presentati al momento giusto e i fecond di sincronizzazione per evitare buffer stuffing. La libreria utilizza l'NDK Choreographer se è disponibile e, in caso contrario, si affida alla Coreografa Java.

La raccolta gestisce più frequenze di aggiornamento se supportate dal dispositivo, il che offre al gioco maggiore flessibilità nel presentare un frame. Ad esempio, per un dispositivo che supporta una frequenza di aggiornamento di 60 Hz e 90 Hz, un gioco che non è in grado di produrre 60 frame al secondo può scendere a 45 FPS anziché 30 FPS per rimanere stabile. La libreria rileva la frequenza fotogrammi del gioco prevista e regola automaticamente i tempi di presentazione dei frame di conseguenza. La libreria di pacing del frame migliora anche la durata della batteria perché evita gli aggiornamenti del display non necessari. Ad esempio, se il rendering di un gioco a 60 f/s, ma il display si aggiorna a 120 Hz, lo schermo viene aggiornato due volte per ogni frame. La libreria di pacing del frame evita che questo accada impostando la frequenza di aggiornamento sul valore supportato dal dispositivo più vicino alla frequenza frame target.

Come funziona

Le seguenti sezioni mostrano in che modo la libreria del pacing frame gestisce i frame dei giochi lunghi e brevi per ottenere un pacing corretto.

Correggere il pacing dei fotogrammi a 30 Hz

Con un rendering a 30 Hz su un dispositivo a 60 Hz, la situazione ideale su Android è mostrata nella Figura 1. SurfaceFlinger blocca i nuovi buffer grafici, se presenti (NB nel diagramma indica "nessun buffer" presente e il precedente è ripetuto).

Pacing di frame ideale a 30 Hz su un dispositivo a 60 Hz

Figura 1. Pacing di frame ideale a 30 Hz su un dispositivo a 60 Hz

I frame brevi del gioco causano interruzioni

Sulla maggior parte dei dispositivi moderni, i motori di gioco si basano sul coreografo della piattaforma che inserisce i segni di graduazione per indirizzare l'invio dei frame. Tuttavia, esiste ancora la possibilità di un pacing dei frame scadente a causa di frame brevi, come mostrato nella Figura 2. I frame brevi seguiti da quelli lunghi vengono percepiti dal player come stuttering.

Fotogrammi brevi del gioco

Figura 2. Un frame di gioco breve C fa sì che per il frame B venga presentato un solo frame, seguito da più frame C

La libreria di pacing del frame risolve questo problema utilizzando i timestamp della presentazione. La libreria utilizza le estensioni del timestamp della presentazione EGL_ANDROID_presentation_time e VK_GOOGLE_display_timing per fare in modo che i frame non vengano presentati in anticipo, come mostrato nella Figura 3.

Timestamp della presentazione

Figura 3. Il frame del gioco B è stato presentato due volte per una visualizzazione più fluida

I frame lunghi causano interruzioni e latenza

Quando il carico di lavoro di visualizzazione richiede più tempo di quello dell'applicazione, alla coda vengono aggiunti frame extra. Questo porta, ancora una volta, a uno stuttering e può portare a un frame di latenza aggiuntivo dovuto al buffer stuffing (vedi figura 4). La libreria rimuove sia lo stuttering sia il frame aggiuntivo di latenza.

Frame di giochi lunghi

Figura 4. Il frame lungo B restituisce un pacing errato per 2 frame: A e B.

La libreria risolve questo problema utilizzando i fecond di sincronizzazione (EGL_KHR_fence_sync e VkFence) per inserire le attese nell'applicazione che consentano alla pipeline di visualizzazione di recuperare il ritardo, anziché consentire l'aumento della pressione. Il frame A presenta ancora un frame aggiuntivo, ma ora il frame B presenta correttamente, come mostrato nella Figura 5.

Attesa aggiunta al livello dell'applicazione

Figura 5. I frame C e D attendono la presentazione

Modalità operative supportate

Puoi configurare la libreria di pacing del frame in modo che operi in una delle tre modalità seguenti:

  • Modalità automatica disattivata + pipeline
  • Modalità automatica on + pipeline
  • Modalità automatica attivata + Modalità pipeline automatica (pipeline/senza pipeline)

Puoi sperimentare le modalità in modalità automatica e pipeline, ma puoi iniziare disattivandole e includendo quanto segue dopo aver inizializzato Stackpy:

  swappyAutoSwapInterval(false);
  swappyAutoPipelineMode(false);
  swappyEnableStats(false);
  swappySwapIntervalNS(1000000000L/yourPreferredFrameRateInHz);

Modalità pipeline

Per coordinare i carichi di lavoro del motore, la libreria in genere utilizza un modello di pipeline che separa i carichi di lavoro di CPU e GPU oltre i confini della VSYNC.

Modalità pipeline

Figura 6. Modalità pipeline

Modalità senza pipeline

In generale, questo approccio comporta una latenza della schermata di input più bassa e prevedibile. Nei casi in cui un gioco ha una durata frame molto ridotta, sia i carichi di lavoro di CPU che di GPU possono rientrare in un singolo intervallo di scambio. In questo caso, un approccio senza pipeline garantirebbe in realtà una latenza inferiore nella schermata di input.

Modalità senza pipeline

Figura 7. Modalità senza pipeline

Modalità Automatica

La maggior parte dei giochi non sa come scegliere l'intervallo di scambio, ovvero la durata per cui viene presentato ogni frame (ad esempio 33, 3 ms per 30 Hz). Su alcuni dispositivi il rendering di un gioco può essere eseguito a 60 FPS, mentre su un altro potrebbe essere necessario scendere a un valore inferiore. La modalità automatica misura i tempi di CPU e GPU per:

  • Seleziona automaticamente intervalli di scambio. I giochi con frequenza di 30 Hz in alcune scene e 60 Hz in altre possono consentire alla libreria di regolare questo intervallo in modo dinamico.
  • Disattiva pipeline per frame ultraveloci: offre una latenza ottimale della schermata di input in tutti i casi.

Più frequenze di aggiornamento

I dispositivi che supportano più frequenze di aggiornamento offrono una maggiore flessibilità nella scelta di un intervallo di scambio più uniforme:

  • Su dispositivi a 60 Hz: 60 f/s/30 f/s/20 f/s
  • Su dispositivi a 60 Hz + 90 Hz: 90 f/s/60 f/s/45 f/s/30 f/s
  • Su dispositivi a 60 Hz + 90 Hz + 120 Hz: 120 f/s/90 f/s/60 f/s/45 f/s/40 f/s/30 f/s

La libreria sceglie la frequenza di aggiornamento più adatta all'effettiva durata di rendering dei frame di un gioco, per un'esperienza visiva migliore.

Per ulteriori informazioni sul pacing di più frame con frequenza di aggiornamento, consulta il post del blog su Rendering della frequenza di aggiornamento elevata su Android.

Statistiche frame

La libreria di pacing del frame offre le seguenti statistiche per scopi di debug e profilazione:

  • Un istogramma del numero di schermate aggiorna un frame in attesa nella coda del composito al termine del rendering.
  • Un istogramma del numero di aggiornamenti dello schermo passati tra l'ora di presentazione richiesta e l'ora attuale effettiva.
  • Un istogramma del numero di aggiornamenti della schermata passati tra due frame consecutivi.
  • Un istogramma del numero di aggiornamenti dello schermo passati tra l'inizio del lavoro della CPU per questo frame e il tempo attuale effettivo.

Passaggio successivo

Consulta una delle seguenti guide per integrare la libreria di pacing dei frame Android nel tuo gioco: