Compilation/linkage des shaders sous Android

 

Principe

Pour ceux qui développent en C, C++ voire assembleur, rien de nouveau en fait.

Il s'agit à partir de sources de produire un programme qui s'exécutera sur le GPU.

Il faut donc compiler tous les sources, pour produire des binaires, puis linker ces binaires pour produire l'exécutable.

 

Pourquoi livrer des sources

L'idée est de permettre d'avoir un exécutable juste au moment de l'exécution, rendant le programme indépendant des plateformes, et évite ainsi de devoir fournir autant de programmes que de cartes graphiques par exemple.

En effet, le compilateur/linker fait parti intégrante d'OpenGL, mais au niveau driver. Les constructeurs peuvent ainsi optimiser le code suivant leur carte graphique, voire ajouter certaines fonctions spécifiques.

Le problème étant que certains développeurs n'aiment pas trop la distribution de codes ainsi aussi facilement recopiable. L'ARB est donc en train d'étudier la possibilité de fournir du code précompilé plutôt que des sources.

 

Les différentes phases de la compilation/linkage

 

Classe OpenGL à utiliser

L'utilisation des shaders est disponible depuis OpenGL 2.0 ES. La classe à utiliser sera donc: GLES20

 

Gestion d'un objet shader

Création d'un shader

Il s'agit de créer un objet shader, sous OpenGL, vous utiliserez la fonction de la classe GLES20: public static int glCreateShader (int type).

En entrée:

type est le type de shader que vous voulez utiliser:GL_VERTEX_SHADER, GL_FRAGMENT_SHADER

En sortie:

Un identifiant sur un nouveau shader qu'il vous faudra bien conserver ! Si à 0, il n'a pas été possible de le créer (Par exemple, OpenGL non initialisé !).

Image non trouvée !Vous avez une "instance" de shader spécialisée sur un type précis de shader, mais il est vide pour le moment !

 

Suppression d'un shader

Puisque vous avez créé un shader, il faudra le supprimer à un moment ou un autre: public static void glDeleteShader (int shader)

En entrée:

shader est l'identifiant du shader (pensez à repositionner cet identifiant à 0 pour éviter de le réutiliser alors qu'il n'existe plus).

En sortie:

GL_INVALID_VALUE si l'identifiant n'est pas un identifiant généré par OpenGL.

 

Image non trouvée !Un shader attaché à un programme sera marqué comme suppimé, mais ne sera supprimé effectivement que lorsque ce programme sera lui aussi supprimé.

 

Tester la validité d'un shader

Il sera possible de vérifier la validité d'un identifiant en utilisant la fonction GLIsShader (int shader). La fonction retournant true si l'identifiant est valide . false sinon.

Image non trouvée !Utilisez glGetShader avec GL_DELETE_STATUS pour vérifier qu'un shader est évetuellement supprimé.

 

Associer un shader à un fichier source glsl

Les fichiers sources de shaders sont maintenant à associer aux différents shaders correspondant. Il faudra utiliser la fonction glShaderSource()

public static void glShaderSource (int shader, String string)

Image non trouvée !Contrairement à OpenGL 2.0 (par exemple sous Linux), cette méthode n'acceptera qu'un seul source en entrée. Ce sera donc à vous de composer les différents sources en un seul avant de faire appel à cette méthode.

En entrée:

shader est l'identifiant du shader précédemment créé.

string est le source glsl du code du shader.

 

En sortie:

Le fichier source est recopié dans le shader,

ou erreur:

GL_INVALID_VALUE l'identifiant shader n'a pas été généré via OpenGL

GL_INVALID_OPERATION pour un objet existe pour cet identifiant, mais ce n'est pas un shader !

GL_INVALID_VALUE si count est < 0.

 

Compiler le source d'un shader

Une fois le source renseigné dans le shader, vous pouvez le compiler: glCompileShader()

public static void glCompileShader (int shader)

En entrée:

L'identifiant du shader.

 

En sortie:

Il faudra utiliser la fonction glGetShaderiv avec GL_COMPILE_STATUS pour vérifier qu'il n'y a pas eu d'erreur lors de la compilation.

Si erreur, afficher le résultat de la compilation pour comprendre ce qui se passe !

 

Gestion d'un objet programme sous OpenGL

La dernière phase consiste à produire un programme exécutable. Mais il faut d'abord indiquer à OpenGL que vous désrirez créer un programme, puis attacher à ce programme les différents shaders qui seront utilisés pour produire ce programme.

Création d'un objet programme

La fonction glCreateProgram permet de récupérer un identifiant sur un objet programme. Ce programme sera "vide", vous obtenez juste une instance d'un programme qu'il faudra ensuite initialiser.

public static int gCreateProgram ()

En sortie:

0 si problème, sinon, identifiant d'un programme qu'il faudra mémoriser pour un usage ultérieur !

 

Supprimer un objet programme

Il faudra à un moment supprimer ce programme.

public static void glDeleteProgram (int program)

En entrée:

program, l'identifiant de votre objet program à supprimer

En sortie:

GL_INVALID_VALUE si l'identifiant n'est pas un identifiant généré par OpenGL.

Image non trouvée !La suppression d'un programme détache automatiquement les shaders. Ceux-ci ne seront pas effacés sauf s'ils étaient eux même déjà marqués comme supprimés.

 

Tester la validité d'un identifiant d'un objet programme

public static boolean glIsProgram (int program)

retoune true si l'identifiant est valide.

Image non trouvée !Utilisez glGetProgram avec GL_DELETE_STATUS pour vérifier qu'un programme est évetuellement supprimé.

 

Attacher/détacher des shaders

Attacher des shaders à un programme permet d'indiquer les shaders qui seront utiles pour produire le programme final:

public static void glAttachShader (int program, int shader)

 

Il sera aussi possible de les détacher:

public static void glDetachShader (int program, int shader) pour détacher un shader d'un programme.

 

En sortie de ces fonctions

GL_INVALID_VALUE si l'identifiant program ou shader n'a pas été généré par OpenGL.

GL_INVALID_OPERATION si:

  • program n'est pas un object programme ou shader n'est pas un identifiant de shader,
  • ou si glDetachShader avec un shader qui n'est pas attaché au programme,
  • ou si glAttachShader et que le shader est déjà associé au programme.

Image non trouvée !Lorsqu'un shader est marqué comme supprimé, celui-ci sera effectivement effacé lors du glDetachShader s'il n'est plus attaché à un quelconque programme.

Image non trouvée !Vous ne pouvez pas vous contentez de fournir une code de shader de type vertex à un programme, il faut aussi livrer un code pour le shader de type fragment, même s'il ne fait rien de spécial. Sinon, le linkage se passera sans problème, mais le résultat de l'exécution du programme sera indéterminé !

 

Linker les binaires de ce programme

Avant de produire un programme, il faut attacher des shaders à celui-ci. C'est en effet les shaders qui ont les binaires qui permettront de produire le code final.

Produire un programme se fait dans une phase de linkage (lié les binaires entre eux).

L'opération de linkage est simple, puisqu'il suffit d'appeler la fonction

public static void glLinkProgram (int program);

qui attend en entrée l'identifiant de l'objet programme à linker.

 

En sortie, le programme est peut-être linké ou

GL_INVALID_VALUE si l'identifiant n'est pas un identifiant généré par OpenGL.

GL_INVALID_OPERATION si l'identifiant program n'est pas un objet programme ou si program est déjà actif et que le mode transform feedback est actif.

 

Pour vérifier qu'il est effectivement linké, il sera nécessaire d'utiliser la fonction glGetProgramiv avec GL_LINK_STATUS pour vérifier qu'il n'y a pas eu d'erreur lors du linkage. Si erreur, affichez le log pour comprendre ce qui se passe !

 

 

Valider le programme produit

Pour pouvoir utiliser un programme, il faut tout d'abord contrôler que celui-ci peut s'exécuter sous OpenGL.

public static void glValidateProgram (int program)

qui attend en entrée l'identifiant de l'objet programme.

ou

GL_INVALID_VALUE si l'identifiant n'est pas un identifiant généré par OpenGL.

GL_INVALID_OPERATION si l'identifiant program n'est pas un objet programme

Là encore, il sera possible d'utiliser la fonction glGetProgramiv avec GL_VALIDATE_STATUS cette fois-ci pour vérifier qu'il n'y a pas eu d'erreur. Si erreur, affichez le log pour comprendre ce qui se passe !

Exemples de codes

Exemple pour gérer les shaders

Pour être plus propre, il faudrait mettre les messages au format international, ...

 

Et sa factory

 

 

 

Exemple pour gérer les programmes

Et sa factory

 

Exemple d'appel

Dans cet exemple, un appel qui se fait directement à la classe shader pour créer un vertex shader "à la main", puis un appel à la FactoryShader pour faire de même, mais plus facilement, avec un fragment shader.

Puis les appels pour produire le programme en utilisant une FactoryProgramGLSL et le valider.

vertexShaderCode et fragmentShaderCode contenant les codes sources des shaders.

listShader contenant les identifiants des shaders générés, cette liste sera passée à la factory FactoryProgramGLSL pour construire le programme

 

Annexe

Les explications équivalentes pour OpenGL sous Linux.

Les explications équivalentes pour OpenGL sous Windows.