Comment améliorer les performances du traitement par lots


9

Je suis en train de développer un jeu 2D basé sur les sprites pour les plates-formes mobiles et j'utilise OpenGL (enfin, en fait Irrlicht) pour rendre les graphiques. J'ai d'abord implémenté le rendu de sprite de manière simple: chaque objet de jeu est rendu sous forme de quadruple avec son propre appel de dessin GPU, ce qui signifie que si j'avais 200 objets de jeu, je faisais 200 appels de dessin par image. Bien sûr, ce n'était pas un bon choix et mon jeu était complètement lié au processeur car il y a un petit supplément de processeur associé à chaque appel de GPU. Le GPU est resté inactif la plupart du temps.

Maintenant, je pensais pouvoir améliorer les performances en collectant des objets en grands lots et en rendant ces lots avec seulement quelques appels de dessin. J'ai implémenté le traitement par lots (pour que chaque objet de jeu partageant la même texture soit rendu dans le même lot) et j'ai pensé que mes problèmes avaient disparu ... seulement pour découvrir que ma fréquence d'images était encore plus basse qu'auparavant.

Pourquoi? Eh bien, j'ai 200 (ou plus) objets de jeu, et ils sont mis à jour 60 fois par seconde. Chaque image, je dois recalculer une nouvelle position (translation et rotation) pour les sommets du CPU (le GPU sur les plates-formes mobiles ne prend pas en charge l'instanciation donc je ne peux pas le faire là-bas), et faire ce calcul 48000 par seconde (200 * 60 * 4 depuis chaque sprite a 4 sommets) semble tout simplement trop lent.

Que puis-je faire pour améliorer les performances? Tous les objets du jeu se déplacent / tournent (presque) à chaque image, donc je dois vraiment recalculer les positions des sommets. La seule optimisation à laquelle je pouvais penser est une table de correspondance pour les rotations afin de ne pas avoir à les calculer. Les sprites ponctuels seraient-ils utiles? Des hacks méchants? Rien d'autre?

Merci.

Réponses:


5

Avez-vous utilisé mon port d'irrlicht pour Android? Pour les sprites 2D sur Android et iPhone, j'utilise les mêmes astuces que vous: le batching. J'essaie de nombreuses solutions dans OpenGL ES 1.x et 2.x:

  • trier par z (parallaxe) et par texture, faire les transformations sur le CPU et appeler glDrawArrays ou glDrawElements (moyen le plus rapide). Utilisez une grande texture si vous le pouvez.
  • même astuce avec VBO, pas plus rapide car pour chaque trame vous rafraîchissez toutes les informations. Il peut être utile pour les sprites statiques.
  • utiliser OpenGL ES 2.x et utiliser le shader Vertex pour calculer les positions (plus lent)
  • utilisez PointSprites (pas de solution si ce n'est pas un carré et trop de pixels transparents tuent le taux de remplissage)
  • utiliser l'extension gldrawtexoes ...
  • utiliser un drawcall pour chaque sprite (méthode la plus lente)

Donc, comme vous, toutes les transformations sont effectuées par le CPU pour OGLES 1.x ou OGLES 2.x. Si vous avez des instructions au néon, vous pouvez les utiliser pour accélérer vos calculs.

Ps: sur les appareils iPhone ou Android, je ne suis pas limité par le CPU mais le taux de remplissage est limité. Il est donc très important de limiter le dépassement.


Excellent, c'est quelque chose que je cherchais. Je n'étais pas au courant de votre port Irrlicht mais j'ai déjà ma version d'Irrlicht en cours d'exécution sur iOS. Vous dites que vous n'êtes pas limité en CPU - combien de sprites dessinez-vous? Et quels sont vos débits, par exemple, pour 100 sprites sur iPhone? Si j'ai 200 objets, je finis par faire 48 000 calculs par seconde. Votre point sur le taux de remplissage est bon.
user4241

Les sprites statiques (arrière-plan) sont en VBO. J'utilise un VBO par parallaxe. Sinon, j'ai 100 à 200 sprites sur Moblox. Sur tous les iPhone, y compris la 3G, j'ai plus de 30 images par seconde (si je me souviens bien). Mais les gros sprites sont très coûteux (problème de taux de remplissage) ....
Ellis

Je travaille sur un moteur de particules, que je peux utiliser jusqu'à 20 000 particules avec toutes les positions de calcul effectuées sur CPU et j'ai 10fps avec des paramètres extrêmes (sur 3GS et iPhone4). Donc, 1000 sprites doivent être possibles sur 3GS ou iPhone4 avec un bon framerate.
Ellis

Merci, très utile! Comment implémentez-vous votre moteur de particules? Je suppose que tu joues avec des shaders?
user4241

J'utilise des shaders car j'ai besoin de gl_PointSize pour configurer chaque taille de particule. Je ne travaille plus avec OGLES 1.x car les vieux téléphones ne sont pas ma cible. Tout d'abord, tout mon code était OGLES 1.x, puis OGLES 1.x et OGLES 2.x (pas d'amélioration des performances) et maintenant OGLES 2.x (amélioration du rendu).
Ellis

1

Je recommanderais d'avoir un VBO, chaque sommet contenant la position / rotation de chaque objet rendu et le traitement par lots basé sur la texture comme vous le faites. Je ne suis pas très familier avec ogl ES, donc je ne sais pas quelle version de glsl il prend en charge, mais vous pourriez même être en mesure de créer un lot basé sur un ensemble de textures, et de stocker laquelle des 4 textures que vous passez en vous utiliseriez à l'intérieur du sommet. Les sprites ponctuels amélioreraient certainement vos performances, car cela réduirait considérablement la quantité de données que vous envoyez, et le traitement par lots ne devrait jamais diminuer les performances si vous le faites correctement. En outre, vous pouvez améliorer un peu les performances en calculant la rotation sur le shader et en passant uniquement une valeur int / float dans les paramètres ou à l'intérieur du sommet lui-même. (les paramètres seraient plus rapides,


Merci pour votre réponse. Votre suggestion concernant le calcul de la rotation dans le shader est excellente, mais malheureusement j'utilise OpenGL ES 1 qui ne prend pas en charge les shaders, donc je suis coincé avec un pipeline fixe. Je vais essayer des sprites ponctuels mais je ne peux pas les utiliser dans tous les cas car il y a une limite supérieure pour leur taille. Je suis toujours un peu pessimiste à propos de VBO, si je recalcule la position de chaque sommet à chaque image, comment VBO aide-t-il?
user4241

cela permet à vos données de sommet de rester sur le GPU, ce qui diminue la quantité de données que vous devez envoyer au GPU à chaque trame. vous n'avez pas besoin de shaders pour profiter de ce tho, vous ne devriez pas du tout changer les données de sommet, si vous avez une position de base (comme l'origine) pour chaque sprite, vous pouvez simplement modifier la matrice du monde en c'est transformer avant d'appeler draw. cependant, cela peut être difficile lors de la mise en lots. en utilisant une fonction fixe, il serait probablement plus avantageux de simplement passer aux VBO et de supprimer le traitement par lots pour le moment, ce qui vous donnera certainement un coup de pouce.
chanteur

Je vois ce que tu veux dire. Donc, après tout, vous ne parlez pas de traitement par lots, mais utilisez simplement un appel de tirage pour dessiner un objet de jeu. Je vais certainement tester comment le VBO sans traitement par lots affecte les FPS dans mon jeu, mais toujours 200 appels de tirage par image semblent trop gros ... mais je suppose que je dois vivre avec. J'accepterai votre réponse si aucune autre réponse n'apparaît.
user4241

1

Vous mentionnez des plateformes mobiles qui n'ont pas d'instanciation. Mais, vous avez toujours des vertex shaders, n'est-ce pas?

Dans ce cas, vous pouvez toujours faire une pseudo instanciation, ce qui est également très rapide. Faire un VBO (GL_STATIC_DRAW) avec les points d'angle (par rapport au point central du sprite, par exemple -1 / -1, 1 / -1, 1/1, -1/1) et toutes les coordonnées de texture dont vous avez besoin, en lui .
Ensuite, définissez l'un des attributs de sommet génériques pour chaque appel de dessin au point central du sprite et dessinez les deux triangles avec le tampon lié. À l'intérieur du vertex shader, lisez l'attribut générique du sommet et ajoutez les coordonnées du sommet.

Cela vous évitera de bloquer un transfert de données pour chaque sprite et devrait être beaucoup plus rapide. Le nombre réel d'appels de tirage n'est pas si important, le blocage / le blocage entre les deux l'est.


Cela semble une bonne solution pour OpenGL ES 2.0. Malheureusement, j'utilise ES 1 qui n'a pas du tout de shaders.
user4241

0

Le problème réside dans la quantité de données que vous envoyez au GPU à chaque trame. Créez simplement un VBO pour chaque lot et remplissez-le une fois, puis appliquez les matrices de transformation correspondantes (via glMultMatrix ou un shader si vous utilisez ES 2.0) lors du dessin des lots.


Je ne comprends pas comment cela aide quand j'ai 200 objets de jeu séparés avec des transformations uniques? L'utilisation de glMultMatrix appliquerait la même transformation à tous les objets, ce qui n'est pas ce que je veux. En outre, l'envoi de données au GPU n'est pas un goulot d'étranglement; si je supprime les transformations côté processeur, les performances sont très bonnes.
user4241

Oui, mais un VBO pourrait encore améliorer les performances s'il est appliqué correctement. Comment rendez-vous actuellement vos 200 objets? Utilisez-vous glBegin / glEnd?
TheBuzzSaw

1
J'utilise le moteur Irrlicht 3D avec un nœud de scène personnalisé, donc je n'utilise pas OpenGL directement (mais je suppose qu'il utilise un simple glBegin / glEnd dans ce cas). Est-ce que VBO aiderait vraiment puisque je devrais modifier le tampon entier à chaque image? En outre, cela ne résout pas le problème fondamental lié à la limitation du processeur en raison des calculs de transformation de vertex. Mais merci quand même pour vos réponses!
user4241
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.