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 :
- on peut faire de la 3D émulée (c'est-à-dire, non accélérée), et
pour ça, il y a deux approches :
- 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),
- 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) ;
- 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) ;
- 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 rendering | Direct 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.