Come migliorare le prestazioni di batch


9

Sto sviluppando un gioco 2D basato su sprite per piattaforme mobili e sto usando OpenGL (beh, in realtà Irrlicht) per il rendering della grafica. Per prima cosa ho implementato il rendering sprite in modo semplice: ogni oggetto di gioco è reso come un quad con la sua chiamata di disegno GPU, il che significa che se avessi 200 oggetti di gioco, avrei fatto 200 chiamate di disegno per frame. Ovviamente questa è stata una scelta sbagliata e il mio gioco è stato completamente vincolato alla CPU perché c'è un piccolo sovraccarico della CPU associato ad ogni richiamo della GPU. La GPU è rimasta inattiva per la maggior parte del tempo.

Ora, ho pensato di poter migliorare le prestazioni raccogliendo oggetti in lotti di grandi dimensioni e eseguendo il rendering di questi lotti con poche chiamate di disegno. Ho implementato il batch (in modo che ogni oggetto di gioco che condividesse la stessa trama sia riprodotto nello stesso batch) e ho pensato che i miei problemi fossero spariti ... solo per scoprire che il mio frame rate era persino più basso di prima.

Perché? Bene, ho 200 (o più) oggetti di gioco e vengono aggiornati 60 volte al secondo. Ogni frame che devo ricalcolare nuova posizione (traduzione e rotazione) per i vertici nella CPU (GPU su piattaforme mobili non supporta l'istanziamento, quindi non posso farlo lì), e fare questo calcolo 48000 al secondo (200 * 60 * 4 dal ogni sprite ha 4 vertici) sembra semplicemente essere troppo lento.

Cosa potrei fare per migliorare le prestazioni? Tutti gli oggetti di gioco si muovono / ruotano (quasi) ogni fotogramma, quindi devo davvero ricalcolare le posizioni dei vertici. L'unica ottimizzazione che mi viene in mente è una tabella di ricerca per le rotazioni in modo da non doverle calcolare. Point Sprites aiuterebbe? Qualche brutto trucco? Qualunque altra cosa?

Grazie.

Risposte:


5

Hai usato il mio porto di Irrlicht per Android? Per gli sprite 2D su Android e iPhone, uso gli stessi trucchi per te: il batch. Provo molte soluzioni in OpenGL ES 1.xe 2.x:

  • ordina per z (parallasse) e per trama, esegui le trasformazioni sulla CPU e chiama glDrawArrays o glDrawElements (modo più veloce). Usa una grande trama se puoi.
  • stesso trucco con VBO, non più veloce perché per ogni frame aggiorni tutte le informazioni. Può essere utile per gli sprite statici.
  • usa OpenGL ES 2.x e usa lo shader Vertex per calcolare le posizioni (più lentamente)
  • usa PointSprites (nessuna soluzione se non è un quadrato e troppi pixel trasparenti uccidono il fillrate)
  • usa l'estensione gldrawtexoes ...
  • usa un drawcall per ogni sprite (metodo più lento)

In questo modo, tutte le trasformazioni vengono eseguite dalla CPU per OGLES 1.xo OGLES 2.x. Se hai istruzioni al neon, puoi usarle per velocizzare i tuoi calcoli.

Ps: su dispositivi iPhone o Android, non sono limitato alla CPU ma il tasso di riempimento è limitato. Quindi è molto importante limitare il sovraccarico.


Eccellente, questo è qualcosa che stavo cercando. Non ero a conoscenza della tua porta Irrlicht ma ho già la mia versione di Irrlicht in esecuzione su iOS. Dici di non essere limitato alla CPU: quanti sprite stai disegnando? E quali sono i tuoi framerate, diciamo, per 100 sprite su iPhone? Se ho 200 oggetti finisco per fare 48000 calcoli al secondo. Il tuo punto su fillrate è buono.
user4241

Gli sprite statici (sullo sfondo) sono in VBO. Uso un VBO per parallasse. Altrimenti, ho 100-200 sprite su Moblox. Su tutti gli iPhone, incluso il 3G, ho più di 30 fps (come ricordo). Ma i grandi sprite sono molto costosi (problema di fillrate) ....
Ellis

Sto lavorando su un motore per particelle, che posso usare fino a 20.000 particelle con tutte le posizioni calcolate sulla CPU e ho 10 fps con impostazioni estreme (su 3GS e iPhone4). Quindi 1000 sprite devono essere possibili su 3GS o iPhone4 con un buon framerate.
Ellis

Grazie, molto utile! Come stai implementando il tuo motore particellare? Suppongo che stai giocando con gli shader?
user4241

Uso gli shader perché ho bisogno di gl_PointSize per impostare ogni dimensione delle particelle. Non lavoro più con OGLES 1.x perché i vecchi telefoni non sono il mio obiettivo. Innanzitutto, tutto il mio codice era OGLES 1.x, quindi OGLES 1.xe OGLES 2.x (nessun miglioramento delle prestazioni) e ora OGLES 2.x (miglioramento del rendering).
Ellis

1

Consiglierei di avere un VBO, con ogni vertice contenente la posizione / rotazione di ciascun oggetto renderizzato e il batch in base alla trama come stai facendo. Non ho molta familiarità con ogl ES, quindi non sono sicuro di quale versione di glsl supporta, ma potresti anche essere in grado di eseguire il batch in base a una serie di trame e memorizzare quale delle 4 o più trame stai passando in useresti all'interno del vertice. Gli sprite dei punti migliorerebbero sicuramente le tue prestazioni perché ridurrebbe drasticamente la quantità di dati che invii e il batch non dovrebbe mai ridurre le prestazioni se lo stai facendo correttamente. Inoltre, potresti migliorare un po 'le prestazioni calcolando la rotazione sullo shader e passando solo un valore int / float nei parametri o all'interno del vertice stesso. (i parametri sarebbero più veloci,


La ringrazio per la risposta. Il tuo suggerimento su come eseguire il calcolo della rotazione nello shader è eccellente, ma sfortunatamente sto usando OpenGL ES 1 che non supporta gli shader, quindi sono bloccato con una pipeline fissa. Proverò gli sprite dei punti ma non li posso usare in tutti i casi perché esiste un limite superiore per le loro dimensioni. Sono ancora un po 'pessimista su VBO, se sto ricalcolando la posizione di ciascun vertice su ogni frame, come può aiutare VBO?
user4241,

consente ai tuoi dati di vertice di rimanere sulla GPU, il che riduce la quantità di dati che devi inviare alla GPU ogni frame. non hai bisogno di shader per approfittare di questo, non dovresti cambiare i dati del vertice, se hai una posizione di base (come l'origine) per ogni sprite, puoi semplicemente cambiare la matrice mondiale di si trasforma prima di chiamare draw. tuttavia, questo potrebbe essere difficile durante il batch. usando la funzione fissa, probabilmente sarebbe più vantaggioso passare ai VBO e abbandonare il batch almeno per ora, che sicuramente ti darà una spinta.
sringer,

Vedo il tuo punto. Quindi, dopo tutto, non stai parlando del batch ma semplicemente usando una chiamata di disegno per disegnare un oggetto di gioco. Sicuramente testerò come il VBO senza il batch influisce sugli FPS nel mio gioco, ma ancora 200 chiamate di disegno per frame sembrano troppo grandi ... ma immagino che dovrò conviverci. Accetterò la tua risposta se non verranno visualizzate altre risposte.
user4241

1

Lei menziona piattaforme mobili che non hanno istanziamenti. Ma hai ancora shader di vertici, vero?

In tal caso, puoi ancora fare lo pseudo instance, che è anche molto veloce. Crea un VBO (GL_STATIC_DRAW) con i punti d'angolo (relativi al punto centrale dello sprite, ad esempio -1 / -1, 1 / -1, 1/1, -1/1) e tutte le coordinate di trama necessarie, in esso .
Quindi imposta uno degli attributi di vertice generici per ogni chiamata di disegno sul punto centrale dello sprite e disegna i due triangoli con il buffer associato. All'interno del vertex shader, leggi l'attributo vertice generico e aggiungi le coordinate del vertice.

Ciò ti farà risparmiare il blocco su un trasferimento di dati per ogni sprite e dovrebbe essere molto più veloce. Il numero effettivo di richiami non è così tremendamente importante, lo è il blocco / stallo in mezzo.


Sembra un'ottima soluzione per OpenGL ES 2.0. Sfortunatamente sto usando ES 1 che non ha affatto shader.
user4241

0

Il problema risiede nella quantità di dati che si inviano alla GPU per ogni frame. Basta creare un VBO per ogni batch e popolarlo una volta, quindi applicare le matrici di trasformazione corrispondenti (tramite glMultMatrix o uno shader se si utilizza ES 2.0) quando si disegnano i batch.


Non capisco come può essere d'aiuto quando ho 200 oggetti di gioco separati con trasformazioni uniche? L'uso di glMultMatrix applicherà la stessa trasformazione a tutti gli oggetti che non è quello che voglio. Inoltre, l'invio di dati alla GPU non è un collo di bottiglia; se rimuovo le trasformazioni sul lato CPU la prestazione è molto buona.
user4241,

Sì, ma un VBO potrebbe comunque migliorare le prestazioni se applicato correttamente. Come stai attualmente eseguendo il rendering dei tuoi 200 oggetti? Stai usando glBegin / glEnd?
TheBuzzSaw

1
Sto usando il motore 3D di Irrlicht con il nodo di scena personalizzato, quindi non sto usando OpenGL direttamente (ma suppongo che stia usando glBegin / glEnd in questo caso). VBO sarebbe davvero d'aiuto dal momento che avrei dovuto modificare l'intero buffer ogni frame? Inoltre, questo non risolve il problema fondamentale dell'essere vincolato alla CPU a causa dei calcoli della trasformazione del vertice. Ma grazie comunque per le tue risposte!
user4241,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.