web-dev-qa-db-fra.com

Comment fonctionne OpenGL au niveau le plus bas?

Je comprends comment écrire des programmes OpenGL/DirectX, et je connais les mathématiques et les éléments conceptuels derrière cela, mais je suis curieux de savoir comment la communication GPU-CPU fonctionne à un bas niveau.

Disons que j'ai un programme OpenGL écrit en C qui affiche un triangle et fait pivoter la caméra de 45 degrés. Lorsque je compile ce programme, est-ce qu'il sera transformé en une série d'appels ioctl, et le pilote gpu envoie ensuite les commandes appropriées au gpu, où toute la logique de rotation du triangle et de définition des pixels appropriés dans la couleur appropriée est câblée dans? Ou le programme sera-t-il compilé en un "programme gpu" qui est chargé sur le gpu et calcule la rotation, etc.? Ou quelque chose de complètement différent?

Edit : Quelques jours plus tard, j'ai trouvé cette série d'articles, qui répond essentiellement à la question: http://fgiesen.wordpress.com/ 2011/07/01/a-trip-through-the-graphics-pipeline-2011-part-1 /

81
Benno

Il est presque impossible de répondre à cette question car OpenGL en lui-même n'est qu'une API frontale, et tant qu'une implémentation adhère à la spécification et que le résultat est conforme, cela peut être fait comme vous le souhaitez.

La question était peut-être: comment fonctionne un pilote OpenGL au niveau le plus bas. Maintenant, il est de nouveau impossible de répondre en général, car un pilote est étroitement lié à un élément matériel, qui peut à nouveau faire les choses selon la conception du développeur.

La question aurait donc dû être: "A quoi cela ressemble-t-il en moyenne dans les coulisses d'OpenGL et du système graphique?". Regardons cela de bas en haut:

  1. Au niveau le plus bas, il y a un périphérique graphique. De nos jours, ce sont des GPU qui fournissent un ensemble de registres contrôlant leur fonctionnement (qui dépend exactement du périphérique) ont une mémoire de programme pour les shaders, une mémoire en vrac pour les données d'entrée (sommets, textures, etc.) et un canal d'E/S pour le reste du système sur lequel il reçoit/envoie les flux de données et de commandes.

  2. Le pilote graphique garde la trace de l'état des GPU et de tous les programmes d'application de ressources qui utilisent le GPU. Il est également responsable de la conversion ou de tout autre traitement des données envoyées par les applications (convertir les textures au format pixel pris en charge par le GPU, compiler les shaders dans le code machine du GPU). De plus, il fournit une interface abstraite et dépendante du pilote aux programmes d'application.

  3. Ensuite, il y a la bibliothèque/le pilote client OpenGL dépendant du pilote. Sur Windows, cela est chargé par proxy via opengl32.dll, sur les systèmes Unix, cela réside à deux endroits:

    • Module X11 GLX et pilote GLX dépendant du pilote
    • et /usr/lib/libGL.so peut contenir des éléments dépendants du pilote pour un rendu direct

    Sur MacOS X, il s'agit du "Framework OpenGL".

    C'est cette partie qui traduit les appels OpenGL comme vous le faites en appels aux fonctions spécifiques au pilote dans la partie du pilote décrite dans (2).

  4. Enfin, la bibliothèque API OpenGL réelle, opengl32.dll dans Windows et sur Unix /usr/lib/libGL.so; cela transmet généralement les commandes à l'implémentation OpenGL proprement dite.

Le mode de communication réel ne peut pas être généralisé:

Sous Unix, la connexion 3 <-> 4 peut se produire soit via des sockets (oui, cela peut, et passe par le réseau si vous le souhaitez), soit via la mémoire partagée. Sous Windows, la bibliothèque d'interface et le client du pilote sont tous deux chargés dans l'espace d'adressage du processus, il n'y a donc pas tant de communication que de simples appels de fonction et de passage de variable/pointeur. Dans MacOS X, cela est similaire à Windows, sauf qu'il n'y a pas de séparation entre l'interface OpenGL et le client du pilote (c'est la raison pour laquelle MacOS X est si lent à suivre les nouvelles versions d'OpenGL, il nécessite toujours une mise à niveau complète du système d'exploitation pour fournir la nouvelle cadre).

La communication entre 3 <-> 2 peut passer par ioctl, lire/écrire ou par mapper de la mémoire dans l'espace d'adressage du processus et configurer le MMU pour déclencher un code de pilote chaque fois que des modifications sont apportées à cette mémoire. Ceci est assez similaire sur n'importe quel système d'exploitation car vous devez toujours traverser la frontière noyau/espace utilisateur: en fin de compte, vous passez par un appel système.

La communication entre le système et le GPU se fait via le bus périphérique et les méthodes d'accès qu'il définit, donc PCI, AGP, PCI-E, etc., qui fonctionnent via les E/S de port, les E/S mappées en mémoire, DMA, IRQ.

82
datenwolf

Lorsque je compilerai ce programme, sera-t-il transformé en une série d'appels ioctl, et le pilote gpu envoie ensuite les commandes appropriées au gpu, où toute la logique de rotation du triangle et de définition des pixels appropriés dans la couleur appropriée est câblée dans? Ou le programme sera-t-il compilé en un "programme gpu" qui est chargé sur le gpu et calcule la rotation, etc.?

Vous n'êtes pas loin. Votre programme appelle le pilote client installable (qui n'est pas vraiment un pilote, c'est une bibliothèque partagée en espace utilisateur). Cela utilisera ioctl ou un mécanisme similaire pour transmettre des données au pilote du noyau.

Pour la partie suivante, cela dépend du matériel. Les cartes vidéo plus anciennes avaient ce qu'on appelle un "pipeline à fonction fixe". Il y avait des espaces mémoire dédiés dans la carte vidéo pour les matrices, et du matériel dédié pour la recherche de texture, le mélange, etc. Le pilote vidéo chargerait les bonnes données et les bons drapeaux pour chacune de ces unités, puis configurer DMA pour transférer votre données de sommet (position, couleur, coordonnées de texture, etc.).

Le matériel plus récent a des cœurs de processeur ("shaders") à l'intérieur de la carte vidéo, qui diffèrent de votre CPU en ce qu'ils fonctionnent chacun beaucoup plus lentement, mais il y en a beaucoup plus qui fonctionnent en parallèle. Pour ces cartes vidéo, le pilote prépare les programmes binaires à exécuter sur les shaders GPU.

23
Ben Voigt

Votre programme n'est pas compilé pour un GPU particulier; il est simplement lié dynamiquement à une bibliothèque qui implémentera OpenGL. L'implémentation réelle peut impliquer l'envoi de commandes OpenGL au GPU, l'exécution de solutions de secours logicielles, la compilation de shaders et leur envoi au GPU, ou même l'utilisation de solutions de remplacement de shader aux commandes OpenGL. Le paysage graphique est assez compliqué. Heureusement, la liaison vous isole de la plupart de la complexité des pilotes, laissant les implémenteurs de pilotes libres d'utiliser les techniques qu'ils jugent appropriées.

19
Tobu

Les compilateurs/éditeurs de liens C/C++ font exactement une chose: ils convertissent les fichiers texte en une série d'opcodes spécifiques à la machine qui sont exécutés sur le CPU. OpenGL et Direct3D ne sont que des API C/C++; ils ne peuvent pas par magie convertir votre compilateur/éditeur de liens C/C++ en un compilateur/éditeur de liens pour le GPU.

Chaque ligne de code C/C++ que vous écrivez sera exécutée sur le CPU. Les appels à OpenGL/Direct3D feront appel aux bibliothèques C/C++, statiques ou dynamiques selon le cas.

Le seul endroit où un "programme gpu" entrerait en jeu est si votre code crée explicitement des shaders. Autrement dit, si vous effectuez des appels d'API dans OpenGL/D3D qui provoquent la compilation et la liaison des shaders. Pour ce faire, vous (au moment de l'exécution, pas lors de la compilation C/C++) générez ou chargez des chaînes qui représentent des shaders dans un langage de shader. Vous les placez ensuite dans le compilateur de shaders et récupérez un objet dans cette API qui représente ce shader. Vous appliquez ensuite un ou plusieurs shaders à une commande de rendu particulière. Chacune de ces étapes se produit explicitement dans le sens de votre code C/C++, qui, comme indiqué précédemment, s'exécute sur le processeur.

De nombreux langages de shader utilisent une syntaxe similaire à C/C++. Mais cela ne les rend pas équivalents à C/C++.

16
Nicol Bolas