Je travaille sur un petit jeu PC à base de tuiles / sprites avec une équipe de personnes et nous rencontrons des problèmes de performances. La dernière fois que j'ai utilisé OpenGL, c'était autour de 2004. Je me suis donc appris à utiliser le profil principal, et je me trouve un peu confus.
Je dois dessiner environ 250-750 dalles 48x48 sur l’écran à chaque image, ainsi qu’environ 50 images-objets. Les tuiles ne changent que lorsqu'un nouveau niveau est chargé et les sprites changent tout le temps. Certaines des tuiles sont composées de quatre pièces 24x24, et la plupart (mais pas toutes) des images-objets ont la même taille que les tuiles. Beaucoup de tuiles et d'images-objets utilisent le mélange alpha.
En ce moment, je fais tout cela en mode immédiat, ce qui, je le sais, est une mauvaise idée. Néanmoins, quand l'un des membres de notre équipe essaie de l'exécuter, il obtient des taux de trame très mauvais (~ 20-30 ips), et c'est bien pire quand il y a plus de tuiles, en particulier lorsque beaucoup de ces tuiles sont du genre sont coupés en morceaux. Tout cela me fait penser que le problème est le nombre d'appels de tirage effectués.
J'ai pensé à quelques solutions possibles à ce problème, mais je voulais les présenter à des personnes qui savent de quoi elles parlent, pour que je ne perde pas mon temps en une chose stupide:
CARRELAGE:
- Lorsqu'un niveau est chargé, dessinez toutes les tuiles une fois dans un tampon d'images associé à une grande texture de klaxon, puis dessinez un grand rectangle avec cette texture sur chaque image.
- Placez toutes les tuiles dans un tampon de sommets statique lorsque le niveau est chargé et dessinez-les de cette façon. Je ne sais pas s'il existe un moyen de dessiner des objets avec différentes textures avec un seul appel à glDrawElements, ou si c'est même quelque chose que j'aimerais faire. Peut-être que juste mettre toutes les tuiles dans une grande texture géante et utiliser des coordonnées de texture amusantes dans le VBO?
SPRITES:
- Dessinez chaque image-objet avec un appel séparé à glDrawElements. Cela semble impliquer beaucoup de changement de texture, ce qui me dit que c'est mauvais. Les tableaux de texture sont-ils utiles ici?
- Utilisez un VBO dynamique en quelque sorte. Même question de texture que le numéro 2 ci-dessus.
- Point sprites? C'est probablement idiot.
Certaines de ces idées sont-elles raisonnables? Y a-t-il une bonne mise en œuvre quelque part sur laquelle je pourrais regarder?
Réponses:
Le moyen le plus rapide de rendre les tuiles est de compresser les données de sommet dans un VBO statique avec des index (comme l'indique glDrawElements). L'écrire dans une autre image est totalement inutile et ne nécessite que beaucoup plus de mémoire. Le changement de texture est TRÈS coûteux, vous voudrez donc probablement regrouper toutes les tuiles dans un " Atlas de textures" et donner à chaque triangle du VBO les bonnes coordonnées de texture. Sur cette base, le rendu de 1000, voire de 100 000 tuiles ne devrait pas poser de problème, selon votre matériel.
La seule différence entre le rendu par mosaïque et le rendu par sprite est probablement que les sprites sont dynamiques. Donc, pour des performances optimales et faciles à réaliser, vous pouvez simplement mettre les coordonnées des sommets des sprites dans un flux dessiner VBO chaque image et dessiner avec glDrawElements. Emballez également toutes les textures dans un Atlas de textures. Si vos images-objets se déplacent rarement, vous pouvez également essayer de créer un VBO dynamique et de le mettre à jour quand une image-objet se déplace, mais c'est une surexploitation totale ici, car vous ne voulez que restituer certaines images-objets.
Vous pouvez regarder un petit prototype que j'ai fait en C ++ avec OpenGL: Particulate
Je rend environ 10000 points sprites, je suppose, avec une moyenne de 400 fps sur une machine habituelle (Quad Core @ 2.66GHz). C’est un processeur limité, ce qui signifie que la carte graphique pourrait générer encore plus de rendu. Notez que je n'utilise pas d'atlas de texture ici, car je n'ai qu'une seule texture pour les particules. Les particules sont rendues avec GL_POINTS et les shaders calculent alors la taille réelle du quad, mais je pense qu’il existe également un Quad Renderer.
Oh, et oui, à moins que vous ayez un carré et que vous utilisiez des shaders pour le mappage de texture, GL_POINTS est assez ridicule. ;)
la source
Même avec ce nombre d'appels de tirage au sort, vous ne devriez pas voir ce genre de baisse de performances - le mode immédiat peut être lent mais il n'est pas si lent (pour référence, même le vieil ancien Quake peut gérer plusieurs milliers d'appels en mode immédiat par image sans tomber si mal).
Je soupçonne qu'il se passe quelque chose de plus intéressant. La première chose à faire est d’investir un peu de temps dans le profilage de votre programme, sinon vous courez un risque énorme de réarchitecture sur la base d’une hypothèse qui pourrait entraîner un gain de performance nul. Alors, parcourez même quelque chose d'aussi basique que GLIntercept et voyez où va votre temps. Sur la base des résultats, vous serez en mesure de résoudre le problème avec des informations réelles sur ce que sont vos principaux goulots d’étranglement.
la source
D'accord, depuis que ma dernière réponse a un peu échappé des mains, voici une nouvelle qui est peut-être plus utile.
A propos de la performance 2D
Tout d'abord quelques conseils d'ordre général: la 2D n'est pas exigeante pour le matériel actuel, même un code largement non optimisé fonctionnera. Cela ne signifie pas pour autant que vous deviez utiliser le mode intermédiaire, assurez-vous au moins de ne pas modifier les états lorsque cela est inutile (par exemple, ne liez pas une nouvelle texture avec glBindTexture lorsque la même texture est déjà liée, une vérification du processeur est considérable. plus rapide qu'un appel glBindTexture) et de ne pas utiliser quelque chose d'aussi totalement faux et stupide que glVertex (même glDrawArrays sera bien plus rapide, et ce n'est pas plus difficile à utiliser, ce n'est pas très "moderne" cependant). Avec ces deux règles très simples, le temps de trame devrait être au moins 10 ms (100 fps). Maintenant, pour obtenir encore plus de vitesse, la prochaine étape logique est le traitement en lots, par exemple, en regroupant autant d'appels de dessin en un, pour cela, vous devriez envisager de mettre en œuvre des atlas de texture, afin que vous puissiez minimiser la quantité de liaisons de texture et ainsi augmenter le nombre de rectangles que vous pouvez dessiner avec un seul appel. Si vous n'êtes pas à environ 2ms (500fps), vous faites quelque chose de mal :)
Cartes de tuiles
Implémenter le code de dessin pour les cartes de tuiles consiste à trouver un équilibre entre flexibilité et rapidité. Vous pouvez utiliser des VBO statiques mais cela ne fonctionnera pas avec des mosaïques animées ou vous pouvez simplement générer les données de sommet de chaque image et appliquer les règles que j'ai expliquées ci-dessus, ce qui est très flexible mais de loin moins rapide.
Dans ma réponse précédente, j'avais introduit un modèle différent dans lequel le fragment shader prenait soin de l'ensemble de la texturation, mais il a été souligné qu'il nécessitait une recherche de texture dépendante et pouvait donc ne pas être aussi rapide que les autres méthodes. (L'idée est essentiellement de ne charger que les indications de pavé et de calculer les coordonnées de texture dans le fragment shader, ce qui signifie que vous pouvez dessiner la carte entière avec un seul rectangle)
Sprites
Les sprites nécessitent beaucoup de flexibilité, ce qui rend l’optimisation très difficile, en dehors de ceux décrits dans la section "A propos des performances 2D". Et à moins que vous vouliez des dizaines de milliers d'images-objets à l'écran en même temps, cela ne vaut probablement pas la peine.
la source
Si tout échoue ...
Mettre en place une méthode de dessin de bascule. Ne mettez à jour que les autres sprites à la fois. Même avec VisualBasic6 et des méthodes simples bit-blit, vous pouvez activement dessiner des milliers d'images-objets par image. Peut-être devriez-vous vous pencher sur ces méthodes, car votre méthode directe consistant à dessiner des sprites semble échouer. (On dirait plutôt que vous utilisez une "méthode de rendu", mais essayez de l'utiliser comme une "méthode de jeu". Le rendu est une question de clarté et non de rapidité.)
Les chances sont, vous redessinez constamment tout l'écran, encore et encore. Au lieu de simplement redessiner uniquement les zones modifiées. C'est BEAUCOUP de frais généraux. Le concept est simple, mais pas facile à comprendre.
Utilisez un tampon pour l'arrière-plan statique vierge. Ceci n'est jamais rendu, à moins qu'il n'y ait pas de sprites à l'écran. Ceci est constamment utilisé pour "revenir" à l'endroit où un sprite a été dessiné, pour dessiner le sprite lors du prochain appel. Vous avez également besoin d'un tampon pour "dessiner", qui n'est pas l'écran. Vous y dessinez, puis, une fois tous dessinés, vous les retournez une fois à l’écran. Cela devrait être un appel à l'écran par tous vos sprites. (Au lieu de dessiner chaque image-objet à l'écran, une à la fois, ou d'essayer de tout faire en même temps, votre fusion alpha échouera.) L'écriture en mémoire est rapide et ne nécessite pas de temps à l'écran pour "dessiner". ". Chaque appel de tirage attendra un signal de retour avant de tenter à nouveau de tirer. (Pas une v-sync, un tick matériel, ce qui est beaucoup plus lent que le temps d'attente de la RAM.)
J'imagine que c'est l'une des raisons pour lesquelles vous ne voyez ce problème que sur un seul ordinateur. Ou bien, il s’agit du rendu logiciel d’ALPHA-BLEND, que toutes les cartes ne prennent pas en charge. Vérifiez-vous si cette fonctionnalité prend en charge le matériel avant de tenter de l’utiliser? Avez-vous une solution de repli (mode non alpha-blend), s’ils ne l’ont pas? De toute évidence, vous n'avez pas de code qui limite (nombre d'éléments mélangés), car je suppose que cela dégraderait le contenu de votre jeu. (Contrairement à ce qui se passait s'il ne s'agissait que d'effets de particules, tous alpha-mélangés, ce qui explique pourquoi les programmeurs les limitent, car ils pèsent lourdement sur la plupart des systèmes, même avec le support matériel.)
Enfin, je suggérerais de limiter votre mélange alpha aux seules choses qui en ont besoin. Si tout en a besoin ... Vous n'avez pas d'autre choix que de demander à vos utilisateurs de disposer de meilleures conditions matérielles, ou de dégrader le jeu pour obtenir les performances souhaitées.
la source
Créez une feuille de sprite pour les objets et un ensemble de tuiles pour le terrain, comme vous le feriez dans un autre jeu 2D, il n'est pas nécessaire de changer de texture.
Rendu des tuiles peut être une douleur parce que chaque paire de triangle a besoin de leurs propres coordonnées de texture. Il y a une solution à ce problème cependant, ça s'appelle rendu instancié .
Tant que vous pouvez trier vos données de manière à avoir, par exemple, une liste des tuiles d'herbe et leurs positions, vous pouvez restituer chaque tuile d'herbe avec un seul appel à dessiner. Il vous suffit de fournir un tableau. du modèle au monde matrices pour chaque tuile. Le tri de vos données de cette manière ne devrait pas poser de problème, même avec le graphe de scènes le plus simple.
la source