David Madore's WebLog: La 3D sous Linux, c'est toujours incompréhensible

[Index of all entries / Index de toutes les entréesLatest entries / Dernières entréesXML (RSS 1.0) • Recent comments / Commentaires récents]

↓Entry #2300 [older| permalink|newer] / ↓Entrée #2300 [précédente| permalien|suivante] ↓

(mardi)

La 3D sous Linux, c'est toujours incompréhensible

Il y a quatre ans, j'avais écrit cette entrée pour me plaindre que je n'arrivais pas à faire fonctionner la 3D et le WebGL sous GNU/Linux parce que que Personne N'Y Comprend Rien® : ce n'est pas la peine de la (re)lire, parce que je vais surtout me plaindre ici que les choses sont encore largement incompréhensibles. Mais pour tâcher d'être un peu constructif, je vais quand même rapporter les rares choses que j'ai quand même réussi à comprendre. (Je souligne : mon grief n'est pas tant que les choses ne marchent pas — globalement, elles ne marchent pas si mal — mais que c'est si difficile de comprendre comment elles marchent.)

Pour fixer la terminologie pour ceux qui ne sont pas au courant des derniers gadgets à la mode, je rappelle que ce que j'appelle la 3D, c'est un ensemble d'interfaces graphiques permettant d'effectuer du dessin principalement (quoique non exclusivement) orienté vers le tracé d'images tridimensionnelles (c'est-à-dire, plus exactement, de projections en deux dimensions d'images tridimensionnelles) — l'opération de base étant typiquement le tracé d'un triangle en projection (on trace des formes plus complexes en les triangulant), l'intérêt étant notamment que le mécanisme 3D va s'occuper de ne tracer que les triangles visibles et pas ceux qui sont masqués par d'autres. Les calculs peuvent être faits par le processeur principal, ou par des circuits dédiés dans la carte graphique, et dans ce dernier cas on parle de 3D accélérée ou de 3D en matériel. Sinon, on parle de 3D non-accélérée ou émulée ou en logiciel. Faire de la 3D accélérée nécessite un chipset graphique correctement supporté ; faire de la 3D émulée (=logicielle), en revanche, devrait être possible sur absolument tous les systèmes — en contrepartie, c'est plus lent. Je dis devrait, parce que ce n'est pas forcément évident de trouver comment il faut s'y prendre en pratique.

L'interface dominante pour faire de la 3D (et en tout cas celle qui servira sous Unix) s'appelle OpenGL, et il y a une variante d'OpenGL connue sous le nom de WebGL, qui rend ces fonctions graphiques 3D disponibles aux pages Web, c'est-à-dire, aux programmes JavaScript. De plus en plus de pages Web dynamiques se servent de ces fonctionnalités WebGL, par exemple le nouveau Google maps (que je trouve épouvantablement mauvais par rapport à l'ancien, mais c'est un autre problème), même s'il y a un mode lite qui n'utilise pas WebGL et qui sera activé si celui-ci n'est pas disponible. J'avais moi-même écrit (cf. cette entrée passée) un petit jeu de labyrinthe en JavaScript utilisant WebGL, et qui peut servir de test extrêmement simpliste pour vérifier que WebGL fonctionne au moins minimalement (sinon, l'exemple le plus simple qui puisse servir de test standardisé est probablement cette page qui doit afficher un bête cube qui tourne).

J'ai cru comprendre les choses suivantes. Je rappelle que sous Unix (et notamment sous GNU/Linux, au moins avant l'arrivée de Wayland), l'affichage graphique passe par un programme appelé le serveur X[11], qui centralise les accès à la carte graphique, les programmes voulant faire de l'affichage étant alors des clients X, qui parlent au serveur X pour lui demander d'afficher tel ou tel truc. J'ai cru comprendre, donc, qu'il existe quatre mécanismes possibles pour faire de la 3D dans le cadre X11 :

  1. on peut faire de la 3D émulée (c'est-à-dire, non accélérée), et pour ça, il y a deux approches :
    1. soit l'émulation est faite au niveau du serveur X, qui prétend donc avoir des capacités 3D, et qui reçoit des commandes OpenGL (≈GLX) de la part du client, et effectue les calculs 3D de son côté (dans ce cas de figure, le client X n'a même pas à savoir si la 3D est accélérée ou non : il fait des appels OpenGL et le serveur X se débrouille comme il peut avec le matériel derrière),
    2. soit l'émulation est faite au niveau du client X, qui fait les calculs avant de les envoyer au serveur X (dans ce cas de figure, le serveur X n'a rien à savoir de la 3D, il ne reçoit que des ordres de tracé 2D) ;
  2. soit le serveur X parle à une carte graphique capable de faire de la 3D accélérée, reçoit des commandes OpenGL (≈GLX) de la part du client et les transmet à la carte graphique (ou plutôt, au pilote pour la cate graphique qui est dans le noyau Linux), en convertissant éventuellement au passage certaines opérations (dans ce cas de figure, le client voit exactement la même chose que dans le cas (1a) ci-dessus, à savoir, il parle OpenGL à un serveur X qui se débrouille comme il peut derrière) ;
  3. soit enfin le client parle directement à la carte graphique capable de faire de la 3D accélérée : le client négocie avec le serveur X le droit de parler à la carte graphique, et il doit aussi recevoir cette possibilité de la part du pilote noyau, et alors le serveur X se contente de laisser le client s'occuper d'un bout de l'écran (une fenêtre, un tampon graphique dans la carte graphique, je ne sais quoi) (on notera que ce mécanisme est probablement plus rapide que tous les précédents, mais il a l'inconvénient d'exiger que le client tourne sur la même machine que le serveur).

J'ai repris la typologie de l'entrée précédente que j'avais écrit sur ce sujet (en gros du plus lent au plus rapide, sachant qu'entre (1a) et (1b) ce n'est pas certain), mais elle n'est sans doute pas idéale, et il sera peut-être plus clair de la disposer sous forme du tableau suivant :

Indirect renderingDirect rendering
3D émulée (=logicielle)(1a)(1b)
3D accélérée (=matérielle)(2)(3)

J'ai déjà expliqué ce que signifie 3D émulée et accélérée. Le direct rendering fait référence au mécanisme (3), et peut-être aussi à (1b), c'est-à-dire au fait que c'est le client qui gère la 3D directement (soit en parlant au matériel qui va faire les calculs, soit en faisant lui-même les calculs). Lorsque c'est le serveur X qui gère la 3D en recevant des commandes OpenGL du client, on parle d'indirect rendering.

Le mode que tout le monde essaye d'utiliser, puisque c'est le plus rapide, c'est celui que j'ai appelé (3) : l'accélération matérielle avec rendering direct. À cause de ça, il est assez difficile de trouver des informations sur les autres mécanismes (comment les détecter, ou comment les utiliser).

Je répète que je ne suis pas sûr d'avoir bien compris (ni si ceci est toujours d'actualité). Même si j'ai bien compris, je n'ai pas de source fiable pour confirmer mon analyse : personne n'a l'air foutu de faire un petit tableau clair montrant les quatre cas de figure que je viens de décrire ; le plus proche que je trouve d'une confirmation est cette page qui affirme bien que all four combinations of direct/indirect software/hardware rendering are possible, ce qui semble coller avec les quatre cases de mon tableau ; elle explique par ailleurs comment distinguer les cas (1a|2), (1b) et (3) (mais pas comment départager (1a) et (2)) : pour savoir si on est en rendering direct ou indirect, il faut chercher la ligne direct rendering dans la sortie de glxinfo, et pour distinguer (1b) du reste, on devrait voir apparaître Software Rasterizer dans la ligne OpenGL renderer string (mais je soupçonne que cette explication est partielle et cache des choses, cf. plus bas).

On en déduit même quelques indications sur comment forcer certains de ces modes, et l'information semble être sur cette page : mettre LIBGL_ALWAYS_INDIRECT à 1 dans l'environnement force le passage à la colonne de gauche, et mettre LIBGL_ALWAYS_SOFTWARE à 1 force le passage à (1b) (je ne sais pas comment les deux interagissent : je crois que c'est la première qui a priorité ; on ne peut logiquement pas choisir entre (1a) et (2) côté client, ça doit être dans la configuration du serveur — mais je ne sais pas où). Ceci étant, chez moi, LIBGL_ALWAYS_INDIRECT ne marche pas :

vega david ~ $ LIBGL_ALWAYS_INDIRECT=1 glxgears
X Error of failed request:  BadValue (integer parameter out of range for operation)
  Major opcode of failed request:  156 (GLX)
  Minor opcode of failed request:  3 (X_GLXCreateContext)
  Value in failed request:  0x0
  Serial number of failed request:  21
  Current serial number in output stream:  23

…je me demande si le mécanisme (2) est encore supporté ou s'il est tombé en désuétude. Je n'ai pas vraiment envie de pousser mon enquête trop loin, de peur de faire planter mon serveur X, sur lequel j'ai des fenêtres ouvertes que je ne veux pas perdre pour le moment.

Ajout () : Si j'en crois cette page, pour que LIBGL_ALWAYS_INDIRECT=1 fonctionne, il faut mettre l'option IndirectGLX et/ou AllowIndirectGLX dans la configuration du serveur. Je n'ai pas testé.

Une chose pas très claire dans l'histoire, c'est le rôle exact joué par la bibliothèque Mesa, qui est responsable de l'interface OpenGL sous les Unix libres. Celle-ci semble servir à la fois à parler au matériel (via, sans doute, une autre bibliothèque) et à faire de l'émulation logicielle. J'ai tendance à imaginer que dans les cas de figure (1a) et (2), le serveur X doit utiliser la bibliothèque Mesa (dans le cas (1a), pour faire l'émulation, et dans le cas (2), pour parler à la carte graphique) alors que le client utilisera la bibliothèque Mesa dans les cas (1b) et (3) et peut-être en fait dans tous les cas, mais ça non plus, ce n'est pas clair du tout.

Par ailleurs, je crois que les choses sont en fait un peu plus compliquées que ce que suggère la typologie ci-dessus, et qu'il existe plusieurs variantes de (1b). Notamment, si j'en crois cette discussion, où un autre utilisateur tout aussi perdu que moi dans le dédale des subtilités de Mesa se fait engueuler par des développeurs qui daignent à peine expliquer les choses, il y a deux mécanismes pour faire (1b), qui consistent (sous Debian, pour fixer les idées) à utiliser (1b₁) le paquet libgl1-mesa-swx11, ou (1b₂) le paquet libgl1-mesa-glx avec le module/pilote(?) swrast_dri.so — tout ceci n'étant bien sûr documenté absolument nulle part. Je pense que LIBGL_ALWAYS_SOFTWARE force le mécanisme (1b₂), mais je ne suis vraiment sûr de rien. Ce qui est sûr, c'est que chez moi, glxinfo renvoie OpenGL renderer string: Gallium 0.4 on llvmpipe (LLVM 3.5, 128 bits) si j'ai mis LIBGL_ALWAYS_SOFTWARE et Gallium 0.4 on AMD CAICOS si je ne l'ai pas mis, mais apparemment jamais Software Rasterizer comme suggéré par la page mentionnée plus haut. C'est peut-être un signe de la différence entre (1b₁) et (1b₂). Je n'en sais pas plus, et je ne comprends pas grand-chose à ces subtilités.

Ajout () : On me signale en commentaire l'existence de la variable d'environnement (évidemment documentée nulle part…) GALLIUM_DRIVER qui, combinée à LIBGL_ALWAYS_SOFTWARE, permet de choisir entre softpipe et llvmpipe, deux rasterizers logiciels différents de Mesa (que je devrais sans doute appeler (1b₂(i)) et (1b₂(ii))) ; je n'ai aucune idée de la différence entre eux, ni de si swrast est au même niveau que ces deux-là ou encore autre chose. D'autre part, j'aurais dû souligner que si on utilise une carte graphique nVidia avec le pilote propriétaire nVidia, celui-ci vient avec sa propre version de Mesa, qui ne fonctionne pas comme le reste, du coup ces diverses variables d'environnement n'auront pas d'effet (c'est une raison parmi d'autres de ne pas aimer ce pilote propriétaire).

À part les variables d'environnement servant à configurer la bibliothèque Mesa, il existe aussi un fichier .drirc (ou globalement /etc/drirc), lu par Apollon sait qui, dont le seul semblant de documentation semble être cette page (qui ne dit essentiellement rien), celle-ci (idem) ainsi que ce que peut afficher la commande xdriinfo (notamment xdriinfo options 0 et xdriinfo options $(xdriinfo driver 0) si votre écran s'appelle 0, ce qui est probable), mais tout ceci est de toute façon assez incompréhensible.

Mais sinon, une autre difficulté est que Firefox (qui est, après tout, le principal programme sur lequel j'ai envie de faire de la 3D, de façon à avoir du WebGL qui marche) n'utilise apparemment pas Mesa. Enfin, ce dont je suis sûr, c'est que Mesa n'apparait pas dans la liste des bibliothèques chargées en mémoire par lui dans le /proc/$PID/maps de mes processus Firefox. Du coup, les variables LIBGL_ALWAYS_INDIRECT et LIBGL_ALWAYS_SOFTWARE, qui sont lues par Mesa, n'ont pas de raison a priori de fonctionner sous Firefox : comment forcer Firefox à faire du rendering indirect ou du rendering logiciel ? J'avais noté autrefois qu'il fallait faire pointer la préférence webgl.osmesalib vers le chemin de Mesa (du genre, /usr/lib/x86_64-linux-gnu/libOSMesa.so sous Debian) et mettre webgl.prefer-native-gl à false — mais ça ne semble plus marcher. Mais en fait, LIBGL_ALWAYS_SOFTWARE a quand même l'air de marcher chez moi (si j'ouvre about:support, il affiche quelque chose de différent). Soit c'est parce que Firefox utilise quand même Mesa de façon cachée (compilé en statique ? ils n'auraient quand même pas osé ?), soit parce qu'ils ont repris la convention. Au final, je n'en sais rien, et je suis perdu.

Ajout () : On me rappelle en commentaire que Firefox a une liste blanche/noire de pilotes avec lesquels il accepte/refuse de fonctionner. Autrefois, on demandait à Firefox d'ignorer cette liste en mettant MOZ_GLX_IGNORE_BLACKLIST à 1 dans l'environnement, mais ce n'est pas clair que ce soit encore d'actualité : maintenant il semble plutôt que ce soit la variable de configuration webgl.force-enabled qui soit pertinente. L'effet des autres variables mentionnées m'est complètement obscur (qu'est-ce que c'est que le layers acceleration et en quoi est-ce différent de l'accélération 3D ?). Et de nouveau, je ne sais pas si les variables webgl.osmesalib et webgl.prefer-native-gl ont encore un sens.

Bref, voilà à peu près ce que j'ai réussi à comprendre, et où je ne comprends plus. Si par hasard un de mes lecteurs comprend mieux que moi et peut m'éclairer un point quelconque (ou simplement confirmer que j'ai bien compris tel ou tel aspect des choses), je lui en serai très reconnaissant !

On va dire que mon but principal est d'avoir au moins les fonctionnalités WebGL basiques dans Firefox et si possible aussi dans Google Chrome, sur toutes les machines que j'utilise physiquement (c'est-à-dire, le PC qui est chez moi, celui qui est dans mon bureau, et celui qui est chez mes parents). Je souligne qu'à défaut de 3D accélérée par la carte graphique, je suis prêt à me contenter au moins de 3D émulée, si elle marche correctement (c'est plus la fiabilité que l'efficacité qui m'intéresse). Mon PC chez moi utilise une carte graphique AMD (ex-ATI) avec le pilote Linux radeon, ça a l'air de marcher à peu près correctement depuis que j'ai mis à jour ma Debian vers la Jessie (8). Mon PC de bureau utilise une carte graphique nVidia avec le pilote propriétaire nvidia, et les choses marchouillent aussi à peu près (même si je voudrais bien me débarrasser de ce pilote, qui est une horreur). Mon PC chez mes parents, en revanche, utilise une carte graphique nVidia avec le pilote libre nouveau, et si le WebGL fonctionnait (fût-ce lentement) avant la mise à jour de Debian, maintenant ma page de labyrinthe qui me sert de test affiche n'importe quoi (pourtant, glxgears semble marcher correctement) ; c'est peut-être lié à un message d'avertissement me signalant que la variable force_s3tc_enable a été modifiée par rapport au défaut (apparemment c'est quelque chose qui se change dans le .drirc, mais je n'y ai pas touché, donc je n'en sais rien). Je n'ai pas eu le temps de comprendre les choses en détail (notamment parce que je ne connaissais pas certaines des choses que j'ai racontées ci-dessus), mais il est sûr que j'ai encore des choses à régler.

↑Entry #2300 [older| permalink|newer] / ↑Entrée #2300 [précédente| permalien|suivante] ↑

[Index of all entries / Index de toutes les entréesLatest entries / Dernières entréesXML (RSS 1.0) • Recent comments / Commentaires récents]