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

Langage C++ Discussion :

Design multi-api et traitement différé


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut Design multi-api et traitement différé
    Bonjour à tous,

    Ce post fais suite à celui-ci : http://www.developpez.net/forums/d12...programmation/.

    Je viens demander votre aide pour résoudre un problème de design. En effet, pour la suite j'ai besoin de partir sur de bases saines, afin par la suite de ne pas à avoir à recommencer une n-ième fois..

    Voici l'ensemble du problème résumé ci-dessous :
    - La bibliothèque va être un moteur de rendus multi-api (typiquement opengl et directx, mais pourquoi pas opengl es, software, etc).
    - Si besoin le choix de l'api peut se faire à la compilation, grâce aux templates, préprocesseur ou tout autre manière. Mais cela n'est pas une obligation et dépendra du design général.
    - boost et c++11 sont fortement conseillés
    - Le moteur doit être facilement extensible.
    - Éviter les pointeurs nus au maximum!

    Voici le schéma général : une ressource CPU devient une ressource GPU, et un renderer applique tout un tas de traitements dessus. En effet, le traitement différé (dans un renderer) est obligatoire pour des raisons liés aux APIs.

    Par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class shader_base
    {
        shader_base(const std::string &src);
    };
     
    class ogl_shader {};
    class dx_shader {};
     
    // Le shader (ressource GPU), est crée à partir d'un code source (ressource GPU).
    // Il sera ensuite binder par le bon renderer.
    Maintenant je cherche à trouver un bon design pour tout cela. J'ai donc trouver plusieurs pistes, chacune possédant plusieurs avantages et inconvénients.
    Je vais les exposer ci-dessous. J'espère que vous pourrais me donner une direction, ou même une voie perpendiculaire à tout ça

    • Visiteur
      Je suis tout d'abord partis sur le design pattern du visiteur, surtout inspiré par l'article d'Emmanuel Deloget. Malheureusement, il en ressort que c'est peut-être pas le plus adapté dans le choix où l'api serait choisit à la compilation : en effet dans un pareil cas, il perd son intérêt non?
      Mais surtout qu'il crée des dépendances entre les classes, et complique l'ajout de fonctionnalités.

    • Template
      Pour cela j'ai pensé créer une classe template renderer, spécialisé suivant les APIs par des classes "tags".

      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      template<typename T>
      class renderer;
       
      struct ogl {}; // tag opengl
       
      template<>
      class renderer<ogl>
      {
          typename ogl_shader shader_type;
          typename ogl_texture texture_type;
       
          // ainsi de suite
      };
      Cette fois-ci le problème c'est que certaines classes dépendaient de l'api alors que ça ne devait pas être le cas : imaginons une classe material constitué de shaders. Comment je déclare les shaders dans ma classe material, sans faire renderer<ogl>::shader_type?
      Et cela pose problème car cette classe doit être indépendante de l'api.

    • Préprocesseur
      Dans les choix de l'API à la compilation j'ai opté pour la solution suivante à base de préprocesseur:
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #if defined USE_OPENGL
          typename ogl_shader shader_type;
          typename ogl_texture texture_type;
       
          // ainsi de suite
       
      #elif defined USE_DIRECTX
          // rebelote
       
      #endif
      Et ce fichier serait inclus partout où l'on se sert des objets liés aux APIs. Néanmoins cela oblige donc tout le monde à avoir accès à plus que l'interface de base par exemple..

      Et puis j'aime bien les templates

    • Interface abstraite
      Cette fois-ci c'est un renderer abstrait comme suivant (basé sur un article de Laurent Gomilla http://loulou.developpez.com/tutorie.../partie8/#L4.3):
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      class renderer_base
      {
          virtual std::shared_ptr<shader_base> createShader(/*...*/) = 0;
       
          virtual void bind(const std::shared_ptr<shader_base> &s) = 0;
       
          // ...
      };
      Seulement cela oblige dans les classes filles à downgrader les classes reçus en paramètres de la façon suivante:

      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
       
      class renderer_ogl
      {
          virtual void bind(const std::shared_ptr<shader_base> &s)
          {
              auto shader = std::static_pointer_cast<shader_ogl>(s);
       
              // suite du code ...
          }
      };
      Cela m'embête un peu de downgrader comme ça, au tant du point de vue design que performance.


    Donc j'espère bien que quelqu'un pourra m'éclairer et que mon post n'est pas trop long Quitte à proposer une autre solution à base de traits ou autre!

    Merci d'avance!

  2. #2
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut

    On avait fait une review du code de LittleWhite sur son projet OpenAWars, qui lui avait permit de concevoir une API indépendante du système graphique (il utilisé la SDL par exemple et j'ai pu faire une version Qt en 5 min) : http://www.developpez.net/forums/d96...nce-wars-like/
    C'est un peu long à lire, mais on aborde pas mal de questions sur le design

  3. #3
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Bonjour,

    Je ne comprends pas trop ton problème avec les templates, si ta classe material est constitué de shader et que les shader dépendent de l'api, alors la classe material en dépend aussi. Comment pourrait-il en être autrement ?

    Ce besoin d'indépendance de certaines classes vient de quoi ? Tu as certaines fonctions qui viennent d'une api extérieur que tu vas devoir utiliser ? Dans ce cas tu n'as pas la possibilité de transmettre les fonctions directement aux classes qui les utiliseront comme elles veulent ?

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    @gbdivers : Beaucoup de point son intéréssant comme les posts de Emmanuel Deloget en page 8. Merci je vais approfondir tout cela!

    @Flob90 : C'est la peur du code redondant je pense surtout.

    Alors dans ce cas là je vais me retrouver avec une classe material template de la sorte?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template<typename api>
    class material
    {
        std::shared_ptr<renderer<api>::shader_type> m_shader;
     
        // exemple : setter
        void shader(const std::shared_ptr<renderer<api>::shader_type> &s)
        {
             m_shader = s;
        }
    };
    De même pour les mesh (contenant les buffers hardwares du mesh en lui-même)? Et chacune des classes qui vont manipuler material ou mesh vont donc avoir un paramètre template représentant l'api?

    Cela ne deviendrait-il pas trop intrusif?

  5. #5
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Je ne trouve pas ça vraiment intrusif, c'est générique : un seul code (de material) pour toutes les api, il n'y a pas de redondance.

    Par contre, si il n'y a qu'un renderer dans ta classe material (et que rien d'autre ne dépend de l'api), alors il ne serais pas envisageable de le mettre comme paramètre template des fonctions en ayant besoin ? (Sauf si toute les fonctions en ont besoin).

    Je n'y connais rien en API graphique, mais ça aurait un sens d'écrire quelque chose comme ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    renderer << material1 << material2 << other;
    //Ou un autre opérateur binaire
    Dans ce cas ta classe material n'a pas besoin d'être template, et tu pourrais définir des fonctions template libres amies de material qui fait ce qu'il faut.

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    Pour faire rapide, OpenGL travaille avec un ensemble d'objet en interne tels que des program (ensemble de shaders), framebuffers, etc.

    Chacun de ces objets réside en mémoire graphique, sur le GPU. L'API met a la création de chacun un identifiant unique à transmettre lors de l'appel des fonctions :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    GLuint handle = glCreateProgram();
     
    // ...
     
    glUseProgram(handle);
    Mais sourtout, OpenGL ne peut avoir d'activer qu'une seule et unique instance pour chacun de ces types! Donc un seul program, une seule texture, etc sont active à un instant t.
    De plus si je centralise tout le traitement dans une classe renderer, c'est dû aux extensions OpenGL et certaines spécificités utilisé par la suite!



    Pour revenir encore sur la généricité, étant donnée que je dois avoir accès aux spécialisations de la classe renderer pour faire par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    renderer<ogl>::shader_type
    Cela veut donc dire que dans mes classes (material par exemple) je dois inclure chacune des spécialisations des renderers?

    Merci encore!

  7. #7
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Les spécialisations templates doivent être définient après la déclaration (voir la définition selon les cas) du template, et avant la première utilisation de la spécialisation qui "force" une instanciation template.

    Dans ton cas, si renderer est un template, elle ne forcera rien à être instancier tant que tu ne l'instancie pas elle même. Or au moment où ceci arrivera tu auras nécessairement les spécialisations pour tes "tag" :
    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
     
    //h1.hpp
    template<class T>
    struct renderer;
     
    //h2.hpp
    #include "h1.hpp"
     
    template<class T>
    struct material
    {
      typedef typename renderer<T>::shader_type shader_type;
    };
     
    //h3.hpp
    #include "h1.hpp"
     
    struct ogl;
     
    template<>
    struct renderer<ogl>
    {
      typedef /*...*/ shader_type;
    };
     
     
    //tu.cpp
     
    #include "h3.hpp"
    #include "h2.hpp"
     
    //...
    renderer<ogl> r;
    material<ogl> m(r);
    Quelque chose dans ce goût la ne devrait pas poser de problème, au moment de l'instanciation des template (dans tu.cpp), l'ensemble des templates et des spécialisations utilisées est connue.

    Si tu centralises, pour reprendre ce que tu disais, et que cette élément centrale dépend de l'api, alors tu as deux approches :
    • C'est la classe qui centralise qui s'occupe de tout faire, et dans ce cas tu peux réaliser des éléments de design indépendant de l'api que l'élément centrale utilisera comme il veut.
    • C'est la classe qui centralise qui permet à divers éléments de tout faire, mais dans ce cas les autres éléments du design vont forcément dépendre indirectement de l'api.

    Personnelement je serais très prudent avec une approche centralisée, on tombe facilement dans des solutions de facilités qui viennent pourrir toute évolution future du design.

    La première solution a un défaut assez important c'est que c'est un design qui se dirige clairement vers un god object : ca va marcher mais c'est pas l'idéal pour des évolutions futures.

    La seconde, bien que tu sembles penser que c'est génant, ne pose pas de réels problèmes, ca va te faire des élèments écrient en fonction d'un paramètre template, c'est si génant que ca ?
    • Il n'y aura aucune redondance du code, le paramètre template sera là pour permettre l'utilisation du bon type de renderer, mais tes interfaces renderer devraient offrir une partie de l'interface commune (dont l'implémentation pourra varier) que les classes pourront utiliser.
    • La conservation du tag "quelque part" pourra permettre l'optimisation de certaines fonctions n'importe où dans le design en utilisant des méthodes de tag dispatching (directement sur le tag de l'api ou sur des tag "caractéristiques"). (*)
    • La partie génante est de devoir repréciser l'api à chaque élément. Ce défaut se contourne de manière partiel en utilisant des fonctions de création (make_pair par exemple) et totalement si tu les couples à auto (C++11).


    NB: Les deux solutions ont un risque d'introduire une dépendance forte de tout le design à l'interface de renderer.

    (*) A la manière des algorithmes sur les itérateurs pour les différentes catégories d'itérateurs. Tu peux faire la même chose si tu arrives à créer des catégories pour les différentes caractéristiques de tes api (je ne connais rien à opengl et dx, ca ne s'applique donc peut-être pas).

  8. #8
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Citation Envoyé par victor_gasgas
    La bibliothèque va être un moteur de rendus multi-api (typiquement opengl et directx, mais pourquoi pas opengl es, software, etc).
    Moteur de rendus (ie qui fournit des services telle "dessiner un cube", "dessiner un mesh", "appliquer une texture") ou une abstraction commune pour les api 3d (id fournit les services "créer un programme", "créer un shader", "transférer un buffer sur le gpu") ?
    D'après ce que je vois ensuite, ça m'a l'air d'être la seconde possibilité (je reviendrais ensuite sur le pourquoi la distinction me semble importante)

    Citation Envoyé par victor_gasgas
    En effet, le traitement différé (dans un renderer) est obligatoire pour des raisons liés aux APIs.
    Détails d'implémentation internes, qui ne doivent pas entrer dans l'api de ton moteur.
    En règle générale, j'ai l'impression que tu ne ne fais pas la différence dans ton design ce qui fait partie de l'api (ie les services rendus) et les détails internes

    Citation Envoyé par victor_gasgas
    Cette fois-ci le problème c'est que certaines classes dépendaient de l'api alors que ça ne devait pas être le cas : imaginons une classe material constitué de shaders. Comment je déclare les shaders dans ma classe material, sans faire renderer<ogl>::shader_type?
    Et cela pose problème car cette classe doit être indépendante de l'api.
    Tu peux tout à fait avoir un concept material indépendant de l'api 3D et avoir un concept apply_material qui en dépend

    Citation Envoyé par Flob90
    Je ne comprends pas trop ton problème avec les templates, si ta classe material est constitué de shader et que les shader dépendent de l'api, alors la classe material en dépend aussi. Comment pourrait-il en être autrement ?
    En général, un material, c'est juste un ensemble d'informations (quel texture de couleur de couleur, de texture d'AO, la normal map, etc), donc indépendant du code des shaders (ou presque)

    Citation Envoyé par Flob90
    Je n'y connais rien en API graphique, mais ça aurait un sens d'écrire quelque chose comme ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    renderer << material1 << material2 << other;
    //Ou un autre opérateur binaire
    Dans ce cas ta classe material n'a pas besoin d'être template, et tu pourrais définir des fonctions template libres amies de material qui fait ce qu'il faut.
    Si ça se fait. Je crois que l'on a ça dans Qt3D

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Mais sourtout, OpenGL ne peut avoir d'activer qu'une seule et unique instance pour chacun de ces types! Donc un seul program, une seule texture, etc sont active à un instant t.
    C'est un détail d'implémentation interne. D'ailleurs, ce n'est pas obligatoire, tu peux travailler sans (direct mode), ou utiliser l'une des nombreuses possibilités (list, VAO, VBO, etc). Pour aller plus loin, je dirais que si tu veux un système évolutif, tu ne peux pas te baser sur un modèle en particulier, sinon tu risques de devoir changer lorsqu'il y a aura des évolutions (un nouveau type de BO par exemple)

    Citation Envoyé par victor_gasgas
    Cela veut donc dire que dans mes classes (material par exemple) je dois inclure chacune des spécialisations des renderers?
    vaut mieux pas, sinon, tes material sont dépendant de l'api



    Pour revenir sur le premier point.
    Tu sembles vouloir faire une api qui permet de faire une abstraction des api 3D. Par exemple, si tu veut créer une shader en gl ou en d3d, tu devrais appeler les fonctions suivantes : CreateVertexShader ou glCreateShader. Pour ça, rien de compliqué, tu fais une simple fonction createShader qui appelle la fonction correcte selon l'api utilisée.
    Le but est donc d'avoir du code indépendant de l'api 3d, mais d'appeler quand même des fonctions bas niveau (createShader, runKernel, etc)
    Le problème est que tu devras écrire des shaders qui dépendront de l'api. C'est à dire que même si tu as une fonction createShader indépendante de l'api 3D, tu devras quand même faire du code (avec un #ifdef par exemple) qui dépendant de l'api 3D.
    Donc en gros, tu n'as fait aucune abstraction et l'intérêt d'une telle api est très limitée

    Ce qui est intéressant, c'est de faire une abstraction qui te permet de manipuler des concepts 3D ("dessiner un cube", "dessiner un mesh", "appliquer une texture") indépendant de l'api 3D. Donc sans donner un code de shader spécifique à l'api 3D

Discussions similaires

  1. [Image] API De traitement de fichier JPEG
    Par Floréal dans le forum 2D
    Réponses: 10
    Dernier message: 20/01/2010, 09h42
  2. API et traitement d'image pour VB
    Par ben_ghost dans le forum VB 6 et antérieur
    Réponses: 0
    Dernier message: 06/11/2008, 16h25
  3. Réponses: 4
    Dernier message: 05/03/2008, 09h43
  4. Moteur multi-API, pourquoi?
    Par zabibof dans le forum Moteurs 3D
    Réponses: 7
    Dernier message: 17/09/2007, 10h47
  5. Réponses: 3
    Dernier message: 26/01/2007, 18h42

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