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!