Bienvenue, Invité. Merci de vous connecter ou de vous inscrire.
Avez-vous perdu votre e-mail d'activation ?

Auteur Sujet: [SFML 2.0 RC] Aide optimisation  (Lu 10046 fois)

0 Membres et 1 Invité sur ce sujet

hugoxerox

  • Newbie
  • *
  • Messages: 12
    • Voir le profil
[SFML 2.0 RC] Aide optimisation
« le: Février 28, 2013, 02:36:00 pm »
Bonjour à tous et à toutes,
Après une assez longue absence du monde de la programmation, me revoilà.

Je m'étais donc relancé dans mon projet de jeu vidéo, l'éditeur suffisamment avancé, je me suis dit : "Allez, il est temps d'optimiser tout ça et de comprendre pourquoi cela bouffe autant de proc".

Après plusieurs essais, j'en suis arrivé à la conclusion (peut-être fausse, corrigez moi si je me trompe) que cela venait de la méthode .Draw() de ma RenderWindow.

C'est pour cela que je me tourne vers vous aujourd'hui. Actuellement j'utilise une méthode un peu barbare (qui ne consommait rien sur la 1.6, étrange, je vous l'accorde). Cette méthode consiste à dessiner case par case, couche par couche (sur lesquelles il y a quelque chose à afficher, les cases vides sont pas dessinées, faut pas déconner) des cases de 32*32pix.. Ce qui correspond donc à 19*14*7=1862 petites cases à dessiner (si toutes les couches sont remplies par des tiles non nuls, bien entendu).

Voici mon code, j'aimerais que vous m'aidiez à l'optimiser pour parvenir à réduire la consommation processeur anormalement élevée pour de la 2D. Je souhaiterais également comprendre et pas me faire balancer une solution comme ça. Merci.

Voici ma boucle de refresh (Gameloop) :
' - Boucle de rafraichissement du jeu
    Public Sub Gameloop()

        While _Socket.Connected

            ' Affichage graphique
            If EnJeu Then
                FenetreRendu.Clear(New Color(160, 160, 160))

                ' Affichage des couches inférieures
                For x = 0 To MAX_MAPX
                    For y = 0 To MAX_MAPY
                        If Not x < 0 And Not x > 30 And Not y < 0 And Not y > 30 Then
                            With Map(MapActuelle).Cases(x, y)

                                If .Sol <> 0 Then
                                    Call AfficherCase(MapActuelle, x, y, 0)
                                End If

                                If .Inf1 <> 0 Then
                                    Call AfficherCase(MapActuelle, x, y, 1)
                                End If

                                If .Inf2 <> 0 Then
                                    Call AfficherCase(MapActuelle, x, y, 2)
                                End If

                                If .Inf3 <> 0 Then
                                    Call AfficherCase(MapActuelle, x, y, 3)
                                End If

                            End With
                        End If
                    Next
                Next

'Petit commentaire pour vous, j'afficherais les joueurs ici, c'est pourquoi j'ai fais deux boucles.

                ' Affichage des couches supérieures
                For x = 0 To MAX_MAPX
                    For y = 0 To MAX_MAPY
                        If Not x < 0 And Not x > 30 And Not y < 0 And Not y > 30 Then
                            With Map(MapActuelle).Cases(x, y)

                                If .Sup1 <> 0 Then
                                    Call AfficherCase(MapActuelle, x, y, 4)
                                End If

                                If .Sup2 <> 0 Then
                                    Call AfficherCase(MapActuelle, x, y, 5)
                                End If

                                If .Sup3 <> 0 Then
                                    Call AfficherCase(MapActuelle, x, y, 6)
                                End If

                            End With
                        End If
                    Next
                Next

                ' Affichage de la grille
                If Grille Then
                    Call AfficherGrille()
                End If

                ' Affichage du tiles selectionné
                Call AfficherPreviTiles()


                FenetreRendu.Display()
                FPS += 1

            End If

                Application.DoEvents()
        End While

        MsgBox("Connexion avec le serveur perdue ! Merci de le signaler à l'équipe du jeu.", MsgBoxStyle.Critical, "Erreur fatale")
        End
    End Sub
 

Ainsi que ma méthode de dessin (Ce n'est pas tant les calculs qui sont lourds, j'ai fais des tests, mais si vous avez une meilleure optimisation, je suis preneur car je n'ai pas encore optimisé la partie calculs) :
 ' - Affichage d'une case sur la map
    Public Sub AfficherCase(ByVal mapnum As Integer, ByVal X As Byte, ByVal Y As Byte, ByVal Couche As Byte)
        Dim tX As Byte
        Dim tY As Byte

        Select Case Couche
            Case 0 ' Sol
                sprtTiles = New Sprite(imgTiles(Map(mapnum).Cases(X, Y).SolSet))
                tX = DecodeX(Map(mapnum).Cases(X, Y).Sol)
                tY = DecodeY(Map(mapnum).Cases(X, Y).Sol)
            Case 1 ' Inf1
                sprtTiles = New Sprite(imgTiles(Map(mapnum).Cases(X, Y).Inf1Set))
                tX = DecodeX(Map(mapnum).Cases(X, Y).Inf1)
                tY = DecodeY(Map(mapnum).Cases(X, Y).Inf1)
            Case 2 ' Inf2
                sprtTiles = New Sprite(imgTiles(Map(mapnum).Cases(X, Y).Inf2Set))
                tX = DecodeX(Map(mapnum).Cases(X, Y).Inf2)
                tY = DecodeY(Map(mapnum).Cases(X, Y).Inf2)
            Case 3 ' Inf3
                sprtTiles = New Sprite(imgTiles(Map(mapnum).Cases(X, Y).Inf3Set))
                tX = DecodeX(Map(mapnum).Cases(X, Y).Inf3)
                tY = DecodeY(Map(mapnum).Cases(X, Y).Inf3)
            Case 4 ' Sup1
                sprtTiles = New Sprite(imgTiles(Map(mapnum).Cases(X, Y).Sup1Set))
                tX = DecodeX(Map(mapnum).Cases(X, Y).Sup1)
                tY = DecodeY(Map(mapnum).Cases(X, Y).Sup1)
            Case 5 ' Sup2
                sprtTiles = New Sprite(imgTiles(Map(mapnum).Cases(X, Y).Sup2Set))
                tX = DecodeX(Map(mapnum).Cases(X, Y).Sup2)
                tY = DecodeY(Map(mapnum).Cases(X, Y).Sup2)
            Case 6 ' Sup3
                sprtTiles = New Sprite(imgTiles(Map(mapnum).Cases(X, Y).Sup3Set))
                tX = DecodeX(Map(mapnum).Cases(X, Y).Sup3)
                tY = DecodeY(Map(mapnum).Cases(X, Y).Sup3)
        End Select

        sprtTiles.TextureRect = New IntRect(tX * 32, tY * 32, 32, 32)
        sprtTiles.Position = New Vector2f(X * 32, Y * 32)
        FenetreRendu.Draw(sprtTiles)
        sprtTiles.Dispose()

    End Sub
 



Voilà, je remercie ceux qui ont pris le temps de me lire, de réfléchir et de m'aider. :)
Bonne journée !

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #1 le: Février 28, 2013, 02:54:16 pm »
Ca n'a pas l'air foncièrement mauvais. Si ça marchait avec SFML 1.6 ça devrait marcher pareil avec 2.0 (tu es sûr que tu n'as rien changé d'autre entre temps, genre du hardware ou des drivers ?).

Sinon, il y a évidemment moyen d'optimiser tout ça grandement ;)
Puisque tout est statique, pourquoi s'embêter à faire tout ce travail 60 fois par seconde ? Fais le une seule fois à l'init (pré-calcule tout), et tant que tu y es stocke le résultat dans un gros machin dessinable en un appel à Draw plutôt que dans plein de petits sprites. Le gros machin étant en l'occurence un vertex array. Il n'y a pas encore de tuto, désolé, mais en cherchant un peu sur le forum tu devrais trouver des exemples de tilemaps faites avec un vertex array.
Laurent Gomila - SFML developer

hugoxerox

  • Newbie
  • *
  • Messages: 12
    • Voir le profil
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #2 le: Février 28, 2013, 03:13:07 pm »
Non, aucune modification matérielle, j'ai d'ailleurs le même programme tournant sous la 1.6, et j'ai comparé sur 3 machines différentes (dont une virtuelle).

Concernant le vertex array, je regarderais + testerais ça ce soir, je vous tiens au parfum ;)

Sinon, je cherchais un moyen de tout placer dans 7 textures (ou sprites), un pour chaque couche, et ainsi appeler la méthode Draw seulement 7 fois au lieu de 18XX fois. C'est le vertex array non ?

Merci.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #3 le: Février 28, 2013, 03:25:56 pm »
Citer
C'est le vertex array non ?
Non, c'est la RenderTexture. C'est moins bien que le vertex array, mais ça peut aussi marcher si tes niveaux ne sont pas trop gros en taille.
Laurent Gomila - SFML developer

hugoxerox

  • Newbie
  • *
  • Messages: 12
    • Voir le profil
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #4 le: Février 28, 2013, 03:37:25 pm »
Alors, j'avais essayé la rendertexture, et j'ai été confronté au même problème, une forte consommation lors du .Draw().

Dans ce cas à quoi sert le Vertex array ? Il ne va pas réduire les appels à .Draw, et surement pas la consommation ? ???

Concernant la taille des niveaux, actuellement, chaque map fait la taille de la surface de jeu visible (19*14), mais une fois le jeu plus avancé, il y aura un système de scrolling qui rendra invisible les transitions entre les maps. C'est à dire que je pourrais voir plusieurs maps en même temps, sans forcément avoir quitté MA map.
Un peu confus comme explication ^^'


EDIT :
Mais je me demande, si en considérant les couches comme des entités fixes et stables, et le reste des éléments (Joueurs, PNJs, objets dynamiques) comme des sprites ça serait pas plus light. Par exemple, je charge une seule fois (comme vous l'avez dit plus haut) le décors, et je l'affiche en 7 gros morceaux (RenderTexture) uniquement au besoin et que le gameloop ne redessinait que les joueurs/PNJs/Objets...
A TESTER, c'est vrai que dans ma tête je ne considérais absolument pas les cases comme des entités fixes, c'est peut-être là MON problème ^^'
Bref, nous verrons bien tout cela ce soir, là j'ai du boulot et au lieu de ça je suis sur ce forum, rooo ;)
« Modifié: Février 28, 2013, 03:43:49 pm par hugoxerox »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #5 le: Février 28, 2013, 03:45:54 pm »
Citer
Alors, j'avais essayé la rendertexture, et j'ai été confronté au même problème, une forte consommation lors du .Draw().
Si tu utilisais RenderTexture avec la même logique, i.e. de tout redessiner à chaque frame, ça ne change rien. Tu as juste ajouté une couche de traitement intermédiaire inutile. Le but des RenderTexture est de "compiler" ton niveau en quelque chose de moins coûteux à dessiner par la suite.

Citer
Dans ce cas à quoi sert le Vertex array ? Il ne va pas réduire les appels à .Draw, et surement pas la consommation ?
Si justement. Tu peux mettre plusieurs "sprites" dans un même VertexArray, pour peu qu'ils utilisent tous la même texture (normalement c'est le cas si ton tileset est bien fait). Du coup tu peux fourrer tout ton niveau, ou du moins tout une couche, dans un seul vertex array et l'afficher en un seul Draw. Et ça c'est le genre d'optimisation qui te fait gagner le plus en performances, car celles-ci sont directement dépendantes du nombre d'appels à Draw.

Bref, que tu choisisse la voie de la render-texture (approche "pré-rendu") ou du vertex array (approche "regroupement de géometrie"), le faire à l'init et non 60 fois par seconde résoudra grandement ton problème de performances.

Citer
Bref, nous verrons bien tout cela ce soir, là j'ai du boulot et au lieu de ça je suis sur ce forum, rooo
Bah, et moi donc ;D
« Modifié: Février 28, 2013, 03:48:45 pm par Laurent »
Laurent Gomila - SFML developer

hugoxerox

  • Newbie
  • *
  • Messages: 12
    • Voir le profil
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #6 le: Février 28, 2013, 04:36:31 pm »
Merci pour ces précisions, j'essayerais d'abord dans une RenderTexture car les planches de tiles sont en cours de création par nos pixels artists, et nous ne savons pas encore comment les classer au mieux pour faciliter leur utilisation.
Après, je pourrais toujours remplacer la RenderTexture par un vertex array si c'est moins couteux, mais pas avant d'avoir des planches terminées entre les mains !

Allez, j'y retourne !
(je suis pressé d'être ce soir !)

hugoxerox

  • Newbie
  • *
  • Messages: 12
    • Voir le profil
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #7 le: Février 28, 2013, 07:16:49 pm »
Désolé du double post.

Je viens donner des nouvelles.
Je viens donc de mettre en place l'affichage des couches FIXES et entières, je suis assez déçu :/
J'avais oublié que les RenderTexture n'avaient pas de fond transparent, c'est assez problématique quand on a plusieurs couches (correspondant à des niveau de hauteur) différents. De plus l'écran m'affiche une sorte de capture de mon écran, passée au mixeur.

C'est assez... énervant de ne pas réussir à trouver une solution à cette montée en charge  :'(

Je suppose que le vertex array n'aura pas de fond transparent non plus ?

Merci.

EDIT :

Je crois que je tiens LE bug de l'année ! Accrochez-vous bien, ça décoiffe !
Je décidais donc de remettre mon éditeur avec l'ancienne méthode d'affichage. Je lance, consommation presque normale... Je titille le programme en remplissant toutes les couches à fond la conso ne montait presque plus ! Je lance donc chrome pour vous prévenir que le problème semblait être parti, puis la consommation remonte à 54%... Je me dis : "Ah.. Oui, fallait s'y attendre", je ferme donc chrome déçu, puis la consommation retombe à 5%... Je fais le test plusieurs fois de suite, même chose. Je teste donc avec IE, aucune montée en charge.
Attention, j'ai bien regardé la consommation du processus et non de l'ordinateur.
Mais j'ai comme une petite impression que SFML2 (ou du moins mon programme) n'aime pas Google Chrome..
Étrange, je vous disais.
« Modifié: Février 28, 2013, 07:37:30 pm par hugoxerox »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #8 le: Février 28, 2013, 08:09:58 pm »
Citer
J'avais oublié que les RenderTexture n'avaient pas de fond transparent
Pourquoi pas ?
rtexture.clear(sf::Color::Transparent);

Et n'oublie pas de dessiner les tiles sans blending, pour que l'alpha soit copié et non interprété.
rtexture.draw(tile, sf::BlendNone);

Citer
Je suppose que le vertex array n'aura pas de fond transparent non plus ?
Le vertex array n'est pas un pré-dessin, c'est juste un regroupement de sprites. Donc si ces sprites ont de la transparence, ils en auront toujours dans le vertex array. Et, au risque de me répéter, je te conseille vivement cette solution, qui réglera tous tes problème de manière simple et presque élégante. Au lieu de te faire ch*** avec d'autres choses pour rien ;D

Citer
Je crois que je tiens LE bug de l'année ! Accrochez-vous bien, ça décoiffe !
En fait c'est connu. Chrome change le timing global de l'ordonnanceur Windows, donc ça affecte toutes les applications qui tournent en même temps. Ca impacte notamment sf::sleep, qui passe d'une granularité de 15 ms à une granularité de 1 ms.
Laurent Gomila - SFML developer

hugoxerox

  • Newbie
  • *
  • Messages: 12
    • Voir le profil
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #9 le: Mars 01, 2013, 10:11:56 am »
Voilà, tout fonctionne, ça consomme plus rien du tout, j'utilise des RenderTexture, et j'ai optimisé au maximum leur refresh. Dans la foulée j'ai également passé un petit coup d'aspi dans mon code (pour le moment sur la classe graphique uniquement, la classe réseau étant terminée depuis longtemps, elle est déjà toute propre).

Sinon, Laurent, concernant les VertexArray, je ne dis pas non, je garde simplement l'idée en stand-by le temps de voir nos planches de Tiles.

Pour ceux qui serait intéressés par la méthode que j'ai utilisé (qui souffriraient donc du même problème que moi) :

If me faut un tableau de RenderTexture (une case par couche)

Au chargement de l'éditeur et à chaque modification de la map (lors d'un clic sur la map quoi), j'appelle une méthode qui va vider (.Clear(new Color(0,0,0,0)) la couche, puis je redessine la couche CASE PAR CASE. Mais c'est pas si lourd car on va effectuer cette action uniquement au besoin.
Du coup, la couche est une seule grosse texture qui nécessitera un seul gros .Draw plutôt que disons... 500.

Dans le gameloop, pour que le jeu continue, il va tout de même falloir actualiser cette map, pour que les joueurs puissent joueur correctement !
Le nettoyage du RENDERWINDOW est bien entendu nécessaire (.Clear(Color.Black))
Du coup, une petite boucle for (de 0 jusqu'à votre nombre de couche -1, pour faire le tour de votre tableau ^^) qui va exécuter :
L'affichage de la couche (ex : RenderCouche(i).Display())
Puis le dessin de votre même couche (ex : RenderWindow.Draw(new Sprite(RenderCouche(i).Texture))

Voilà, j'espère que mes explications sont pas trop confuses et qu'elles seront utiles à d'autres !

Bonne journée à tous, et encore merci à Laurent !

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #10 le: Mars 01, 2013, 10:25:16 am »
Pourquoi dois-tu mettre à jour tes couches ? Typiquement tu vas pré-dessiner tout ce qui est statique (le décor), et laisser ce qui est dynamique (les joueurs, items, NPCs, ...) sous forme de sprites qui peuvent se balader librement dans le décor.

Et attention aussi à la taille maximum de texture, qui peut être assez basse sur les vieilles cartes graphiques (512x512).
Laurent Gomila - SFML developer

hugoxerox

  • Newbie
  • *
  • Messages: 12
    • Voir le profil
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #11 le: Mars 01, 2013, 11:52:48 am »
Citer
Pourquoi dois-tu mettre à jour tes couches ?
Je suis dans un éditeur de map actuellement, la couche doit donc être mise à jour à chaque fois que la map est modifiée.
Sinon dans le gameloop, je lui demande de redessiner les couches car si je redessine les joueurs, il faut bien redessiner les couches qui lui passent dessus.

Citer
Et attention aussi à la taille maximum de texture, qui peut être assez basse sur les vieilles cartes graphiques (512x512)
:-X
Tu entends quoi par "vieille" ?
C'est assez flippant car je dépasse de presque rien, je testerais cet après-midi sur un netbook tournant sous XP.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #12 le: Mars 01, 2013, 11:59:58 am »
Citer
Je suis dans un éditeur de map actuellement, la couche doit donc être mise à jour à chaque fois que la map est modifiée.
Ah oui ok.

Citer
Sinon dans le gameloop, je lui demande de redessiner les couches
Oui il faut les dessiner c'est sûr, moi je parlais juste de leur mise à jour (i.e. reconstruire la render-texture).

Citer
Tu entends quoi par "vieille" ?
Genre carte graphique Intel intégrée sur vieil ordinateur portable.
Laurent Gomila - SFML developer

hugoxerox

  • Newbie
  • *
  • Messages: 12
    • Voir le profil
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #13 le: Mars 02, 2013, 01:21:45 pm »
Par contre, que ce passe-t-il dans le cas où la taille limite de la texture est dépassée ?

J'ai testé sur le netbook en question, et il refuse simplement d'afficher quoi que ce soit, je me demandais donc si c'était cela, car même après avoir baissé la taille à 320*320, cela ne changeait rien :/

Merci.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : [SFML 2.0 RC] Aide optimisation
« Réponse #14 le: Mars 02, 2013, 02:45:42 pm »
Citer
Par contre, que ce passe-t-il dans le cas où la taille limite de la texture est dépassée ?
Il n'y a aucune alternative pour SFML, donc ça foire tout simplement.

Citer
J'ai testé sur le netbook en question, et il refuse simplement d'afficher quoi que ce soit, je me demandais donc si c'était cela, car même après avoir baissé la taille à 320*320, cela ne changeait rien :/
Pas de bol :P
Laurent Gomila - SFML developer