Introduction des RAII en OpenGL


Ce tutoriel introduit l'utilisation des RAII (Ressource Aquisition Is Initialisation) en OpenGL. Un point clef en opengl est de conserver la "machine à états" propre. L'utilisation de RAII va permettre de faciliter cette gestion et également de clarifier le code.

Prenons un exemple:

glEnable(flag);
....
glDisable(flag);

C'est ici l'utilisation normale d'opengl. Rien de compliqué mais c'est exactement ce qu'on veut éviter pour ne pas alourdir le code...

Un premier pas vers le RAII

struct Enable{
   Enable(GLenum flag){
      glEnable(flag);
   }
 
   ~Enable() {
      glDisable(flag);
   }
};

Puis on l'utilise dans un bloc, comme suit :

{
   Enable active(flag);
   ...
}

Comme on peut le voir ici, Le principe est très simple : Lors de la création de l'instance de la structure Enable, on execute via le constructeur la commande opengl glEnable(). Lorsque l'on sort de la zone de portée de cette instance (ici, le bloc {}), elle est détruite. Il y a donc appel du destructeur dans lequel on a placé la commande glDisable().

Conservation et propreté

Cette première version fonctionne, mais imaginons qu'avant le bloc le flag soit deja activé : à la sortie il sera desactivé. Pour s'assurer de la récuperation d'un contexte identique, nous devons modifier notre classe pour mémoriser l'etat courant avant sa modification.

On procède de cette manière :

struct Enable {
 
   GLboolean state;
   Enable(GLenum flag) {
      state = glIsEnable(flag);
      // State contient l'état actuel du flag
      // Si le flag est deja activé il n'est pas necessaire de le réactiver
      if(!state)
         glEnable(flag);
   }
 
   ~Enable() {
      // Si le flag était désactivé à la création de la classe on le désactive à nouveau
      if(!state)
         glDisable(flag);
   }
};

Ici nous sommes : L'utilisation de l'objet conserve le contexte opengl.

Cas d'usage : factorisation du code à l'aide des templates

Prenons le cas suivant :

{
   Enable lighting(GL_LIGHTING);
   Enable light0(GL_LIGHT0);
   Enable depth(GL_DEPTH_TEST);
 
   // ...code
}

Supposons que ces trois appels reviennnent régulierement : l'héritage multiple devrait nous permettre de factoriser ce code en regroupant le tout en une seule et même classe. Mais le probleme ici, c'est que nous avons une unique classe pour tous les états. Comme nous ne pouvons pas heriter plusiseurs fois de la même classe, il va falloir trouver une autre solution.

Et celle qui s'y prête le mieux reste l'utilisation de templates :

template struct Enable {
   GLboolean state;
   Enable() { //plus de paramètre au constructeur
      state = glIsEnable(Flag);
      // state contiend l'etat actuel du flag
      // si le flag est deja active il 'est pas necessaire de le reactiver
 
      if(!state)
          glEnable(Flag);
   }
 
   ~Enable() {
      if(!state)
      glDisable(Flag);
   }
};

En "templetant" la classe Enable, nous obtenons une classe différente pour chaque paramètre du template, et donc pour chaque flag.

Au niveau de l'utilisation,

Enable depth(GL_DEPTH_TEST);

deviendra :

Enable depth;

Mais surtout, ceci va nous permettre de factoriser des états.
Reprenons les trois appels redondant de notre programme :

Enable lighting;
Enable light0;
Enable depth;

En templatant, on peut créer une structure héritant de chaque classe :

struct MonContext :
   Enable ,
   Enable ,
   Enable
{
};

Nous obtenons une structure ayant les propriétés de 3 classes parents. Et surtout, à la creation de l'objet les 3 constructeurs des parents seront appelés, et réciproquement à la destruction.
On peut donc se contenter de ceci :

{
   MonContext ctx_light;
}

Conclusion

Voilà pour l'utilisation des RAII : Rien de très complexe dans la théorie ou dans la pratique. C'est par contre un puissant outil qui devrait rapidement devenir indispensable à la clarté de tout programme conséquent.
Un grand merci à Twxs pour ce tuto !

Cet article provient du tutorial publié par Twxs le 03/10/2004 sur l'ancien site de Coder-Studio.

,

  1. No comments yet.
(will not be published)

  1. No trackbacks yet.