IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

API graphiques Discussion :

Un vertex buffer par quad 2D ?


Sujet :

API graphiques

  1. #1
    Membre averti
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2008
    Messages
    187
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Octobre 2008
    Messages : 187
    Points : 448
    Points
    448
    Par défaut Un vertex buffer par quad 2D ?
    Hello tous le monde,

    Dans le cadre de mon petit moteur 2D en C# que je développe sur mon temps libre, je suis en train d'écrire un moteur de rendu simple pour afficher des sprites liés à des matériaux. La question que je me pose, c'est quelle est la meilleure stratégie pour créer les vertex buffers ? J'ai plusieurs stratégies en tête :

    1) Je créé un vertex buffer par sprite, quitte à appeler device.setVertexBuffer() un grand nombre de fois par frame. On aurait très approximativement ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    class Bitmap
    {
     
        public Material Material{ get{ return material_; } set{ material_ = value;} }
        public Sprite Sprite{ get{ return sprite_;} set{ sprite_ = value; UpdateVertexBuffer(); } }
     
        public Bitmap(string materialPath, Sprite sprite)
        {
            material_ = Load(materialPath);
            sprite_ = sprite;
            UpdateVertexBuffer();
        }
     
        public void Destroy()
        {
            Device.ReleaseVertexBuffer(vertexBuffer_);
        }
     
        public void Draw(Renderer renderer)
        {
            renderer.SetVertexBuffer(vertexBuffer_);
            renderer.SetMaterial(material_);
            renderer.DrawQuad();
        }
     
        private void UpdateVertexBuffer()
        {
            if (vertexBuffer_ == null)
            {
                vertexBuffer_ = Device.CreateVertexBuffer(4);
            }
            vertexBuffer_.Lock();
            vertexBuffer_[0] = sprite_.topLeft;
            vertexBuffer_[1] = sprite_.topRight;
            vertexBuffer_[2] = sprite_.bottomLeft;
            vertexBuffer_[3] = sprite_.bottomRight;
            vertexBuffer_.Unlock();
        }
     
        private Material material_;
        private Sprite sprite_;
        private VertexBuffer vertexBuffer_;
    }

    Ça me paraît être un gaspillage énorme de créer un VB pour seulement 4 sommets, et le coût d'appel de SetVertexBuffer dans le Draw me paraît énorme...

    2) Je créé un très grand vertex buffer (de plusieurs Mo), et chaque Bitmap peut réserver des sommets dans celui-ci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    class Bitmap
    {
        public Material Material{ get{ return material_; } set{ material_ = value;} }
        public Sprite Sprite{ get{ return sprite_;} set{ sprite_ = value; UpdateVertexBuffer(false); } }
     
        public Bitmap(string materialPath, Sprite sprite)
        {
            material_ = Load(materialPath);
            sprite_ = sprite;
            UpdateVertexBuffer(true);
        }
     
        public void Destroy()
        {
            // que faire ici ?
        }
     
        public void Draw(Renderer renderer)
        {
            renderer.SetMaterial(material_);
            renderer.DrawQuad(vertexBufferStartIdx_);
        }
     
        private void UpdateVertexBuffer(bool createVB)
        {
            if (createVB)
            {
                vertexBufferStartIdx_ = Device.ReserveVertex(4);
            }
            VertexBufferFragment buffer = Device.LockVertexBuffer(vertexBufferStartIdx_);
            buffer[0] = sprite_.topLeft;
            buffer[1] = sprite_.topRight;
            buffer[2] = sprite_.bottomLeft;
            buffer[3] = sprite_.bottomRight;
            Device.UnlockVertexBuffer();
        }
     
        private Material material_;
        private Sprite sprite_;
        private uint vertexBufferStartIdx_;
     
    }
    Là c'est beaucoup mieux, puisqu'on a besoin que d'un unique buffer dynamique ! Oui mais... Que se passe-t-il dans la méthode Destroy ? Il faudrait implémenter un moyen de libérer les vertex réservé par le Device, mais ça me semble compliqué, car entre temps, on peut avoir réservé d'autres vertex dans d'autre Bitmap, et on risque d'avoir tous les problèmes liés à la gestion mémoire (fragmentation, réallocation...)

    3) J'ai une 3e idée, mais je ne sais pas si elle efficace : il s'agit de n'avoir qu'un unique vertex buffer de 4 sommets dans le programme, de coordonnées (0,0), (1,0), (0,1) et (1,1), et de jouer sur les rotations/translations/scale dans le vertex shader pour afficher correctement ma texture.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class Bitmap
    {
     
        public Material Material{ get{ return material_; } set{ material_ = value;} }
        public Sprite Sprite{ get{ return sprite_;} set{ sprite_ = value; } }
     
        public Bitmap(string materialPath, Sprite sprite)
        {
            material_ = Load(materialPath);
            sprite_ = sprite;
        }
     
        public void Destroy()
        {
        }
     
        public void Draw(Renderer renderer)
        {
            renderer.SetMaterial(material_);
            renderer.DrawQuad(sprite.Scale, sprite.Rotation, sprite.Translation); // les différents paramètres vont être utilisé par le vertex shader
        }
     
        private Material material_;
        private Sprite sprite_;
     
    }
    Cette solution n'est pas mal, mais à chaque appel de DrawQuads (donc des milliers de fois par frame), on va devoir envoyer 5 float à la carte graphique... Le coût n'est pas négligeable.


    Voilà, j'espère que je me suis bien exprimé, tout conseil ou critique sont les bienvenus, je suis un peu débutant dans la programmation rendu et je ne sais pas trop par où partir.

    Sinon je sais ce que vous allez me dire, la partie rendu d'un moteur 2D n'est jamais critique, et je ne devrais me poser la question du meilleur choix seulement si j'ai des problèmes de performance, mais je pose la question plus par curiosité que par nécessité. De plus, je programme un jeu qui demande d'afficher des milliers de sprites à l'écran, donc j'aurais peut-être des problèmes de perfs au final

    ps : j'utilise XNA, mais je prévoie de passer sous Mono sous peu.

  2. #2
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 860
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 860
    Points : 219 064
    Points
    219 064
    Billets dans le blog
    120
    Par défaut
    Bonjour,

    Il est conseillé d'éviter les drawCall. Maintenant, je ne sais plus si XNA propose une technique de point sprite de base ou non (simulable avec un geometry shader, mais je ne crois pas qu'on y ait accès avec XNA). Cela aurait permis de ne passer que la position du sprite (son centre), sa taille (qui peut être fixe) et une coordonnées x/y pour retrouver son emplacement sur la sprite sheet.

    Mon idée est de faire en sorte, que ce soit le fragment shader/pixel shader (c'est le même, juste qu'il a deux noms ) qui retrouver tout seul les coordonnées de texture du sprite sheet a afficher (il faut concevoir le sprite sheet comme une grille).

    Maintenant, vous pouvez faire un drawCall, de tout les sprites à afficher utilisant un même sprite sheet (cela devrait réduire considérablement le nombre de call, je pense).
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  3. #3
    Membre averti
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2008
    Messages
    187
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Octobre 2008
    Messages : 187
    Points : 448
    Points
    448
    Par défaut
    Merci Little White pour ta réponse !

    Citation Envoyé par LittleWhite Voir le message
    Il est conseillé d'éviter les drawCall.
    Oui, certes, il faut réduire le plus possible les DrawCall dans le rendu : si j'ai 1000 bullets identiques à afficher dans une frame, je vais essayer de tout regrouper dans un seul DrawCall (un seul vertex buffer/spritesheet).

    Citation Envoyé par LittleWhite Voir le message
    Cela aurait permis de ne passer que la position du sprite (son centre), sa taille (qui peut être fixe) et une coordonnées x/y pour retrouver son emplacement sur la sprite sheet.
    Je ne sais pas si les geomtry shaders sont dispos sur XNA (je n'ai pas regardé), mais l'idée ressemble justement à ma solution 3, qui consiste à envoyer dans le vertex shader la position du sprite, sa taille, et ses UVs... Une idée à creuser, je devrais peut-être poster le code du shader.

    EDIT : Je viens de regarder comment le rendu était fait dans le Rapid Prototyping Framework (le moteur de World Of Goo), et c'est bien la 3e solution qui est implémentée : on n'utilise qu'un seul vertex buffer de taille 4 sommets, et c'est le vertex shader qui s'occupe d'afficher la texture à la bonne place. Je crois que je vais utiliser cette méthode alors

  4. #4
    Membre expert

    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Février 2006
    Messages
    1 031
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2006
    Messages : 1 031
    Points : 3 092
    Points
    3 092
    Par défaut
    Jette un oeil dans le code de SpriteBatch.cs, j'ai souvenir d'une optimisation mais je ne me souviens plus de quoi exactement.
    Suivez le développement de Chibis Bomba
    twitter : https://twitter.com/MoD_DiB
    DevBlog : http://moddib.blogspot.fr/

  5. #5
    Membre averti
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2008
    Messages
    187
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Octobre 2008
    Messages : 187
    Points : 448
    Points
    448
    Par défaut
    Hello MoDDiB, j'ai regardé le code de SpriteBatch.cs, et le fonctionnement est un peu différent en effet.

    Dans Mono, à chaque fois qu'on affiche un quad à l'écran, le SpriteBatch remplit un vertex buffer avec les positions/color/UV du quad. Lorsqu'on change de texture courante, ou lors d'un End(), on envoie le batch qui contient le vertex buffer à la carte graphique. Je pense que l'optimisation dont tu parle consiste donc à regrouper à la volée l'affichage de quad d'une même texture dans un seul seul batch, plutôt que de faire un batch par quad.

    Le problème, c'est que le vertex buffer est entièrement recréé et envoyé à la carte graphique à chaque frame, pas étonnant que ça rame chez moi avec des milliers de sprites ! Même en regroupant les sprites par textures (j'en ai 4 ou 5 différentes), je ne dépasse pas les 20 FPS su mon (vieux) PC.

    J'ai vraiment l'impression que le mieux est de regrouper l'affichage des sprites par material pour limiter le nombre de batch (comme me l'a fait remarqué LittleWhite). Peut-être serait-il efficace de créer un VB par texture, et lors de l'affichage, faire un unique batch par couple texture/VB, qu'en pensez-vous ?

  6. #6
    Membre expert

    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Février 2006
    Messages
    1 031
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2006
    Messages : 1 031
    Points : 3 092
    Points
    3 092
    Par défaut
    Et bien si ce n'est pas suffisant pour toi en perf voilà de quoi faire :
    http://www.float4x4.net/index.php/20...with-textures/
    Suivez le développement de Chibis Bomba
    twitter : https://twitter.com/MoD_DiB
    DevBlog : http://moddib.blogspot.fr/

  7. #7
    Membre averti
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2008
    Messages
    187
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Octobre 2008
    Messages : 187
    Points : 448
    Points
    448
    Par défaut
    Je vais regarder ça merci beaucoup !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réception de Buffer par Record
    Par Leobaillard dans le forum Langage
    Réponses: 3
    Dernier message: 23/12/2005, 12h35
  2. [Debutant] Affichage du contenu d'un vertex buffer
    Par Edouard Kaiser dans le forum DirectX
    Réponses: 12
    Dernier message: 20/12/2005, 10h26
  3. Vertex buffer et géométrie complexe
    Par Omeyocan dans le forum DirectX
    Réponses: 13
    Dernier message: 14/12/2005, 11h28
  4. Probleme avec le vertex buffer
    Par nicoo dans le forum DirectX
    Réponses: 12
    Dernier message: 19/10/2004, 21h45
  5. question sur les vertex buffer et index buffer
    Par airseb dans le forum DirectX
    Réponses: 9
    Dernier message: 25/08/2003, 02h38

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo