Comment le processeur et le processeur graphique interagissent-ils lors de l'affichage des graphiques informatiques?


58

Ici, vous pouvez voir une capture d'écran d'un petit programme C ++ appelé Triangle.exe avec un triangle tournant basé sur l'API OpenGL.

entrez la description de l'image ici

Certes, c'est un exemple très basique, mais je pense que cela s'applique à d'autres opérations de cartes graphiques.

J'étais simplement curieux et voulais connaître l'ensemble du processus en double-cliquant sur Triangle.exe sous Windows XP jusqu'à ce que je puisse voir le triangle tourner sur le moniteur. Que se passe-t-il, comment le processeur (qui gère d'abord le fichier .exe) et le processeur graphique (qui affiche finalement le triangle à l'écran) interagissent-ils?

Je suppose que l’affichage de ce triangle rotatif implique principalement le matériel / logiciel suivant, entre autres:

Matériel

  • Disque dur
  • Mémoire système (RAM)
  • CPU
  • Mémoire vidéo
  • GPU
  • affichage LCD

Logiciel

  • Système opérateur
  • API DirectX / OpenGL
  • Pilote Nvidia

Quelqu'un peut-il expliquer le processus, avec peut-être un organigramme à titre d'exemple?

Cela ne devrait pas être une explication complexe couvrant toutes les étapes (mais cela irait au-delà de la portée), mais une explication qu'un informaticien intermédiaire peut suivre.

Je suis presque sûr que beaucoup de personnes qui se diraient même des professionnels de l'informatique ne pourraient pas décrire ce processus correctement.


Votre dilemme serait terminé si vous pouviez simplement considérer le GPU comme une extension du processeur!
KawaiKx

Réponses:


55

J'ai décidé d'écrire un peu sur l'aspect de la programmation et sur la manière dont les composants se parlent. Peut-être que cela va nous éclairer sur certains domaines.

La présentation

Que faut-il pour avoir cette seule image, que vous avez postée dans votre question, dessinée à l'écran?

Il y a plusieurs façons de dessiner un triangle à l'écran. Pour simplifier, supposons qu'aucun tampon de vertex n'a été utilisé. (Un vertex buffer est une zone de mémoire dans laquelle vous stockez des coordonnées.) Supposons que le programme communique simplement le pipeline de traitement graphique à chaque sommet (un sommet est simplement une coordonnée dans l'espace) d'une ligne.

Mais avant de pouvoir dessiner quoi que ce soit, nous devons d'abord exécuter un échafaudage. Nous verrons pourquoi plus tard:

// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

// Reset The Current Modelview Matrix
glMatrixMode(GL_MODELVIEW); 
glLoadIdentity();

// Drawing Using Triangles
glBegin(GL_TRIANGLES);

  // Red
  glColor3f(1.0f,0.0f,0.0f);
  // Top Of Triangle (Front)
  glVertex3f( 0.0f, 1.0f, 0.0f);

  // Green
  glColor3f(0.0f,1.0f,0.0f);
  // Left Of Triangle (Front)
  glVertex3f(-1.0f,-1.0f, 1.0f);

  // Blue
  glColor3f(0.0f,0.0f,1.0f);
  // Right Of Triangle (Front)
  glVertex3f( 1.0f,-1.0f, 1.0f);

// Done Drawing
glEnd();

Alors qu'est-ce que ça a fait?

Lorsque vous écrivez un programme qui veut utiliser la carte graphique, vous choisissez généralement une sorte d'interface avec le pilote. Certaines interfaces bien connues du pilote sont:

  • OpenGL
  • Direct3D
  • CUDA

Pour cet exemple, nous allons rester avec OpenGL. Maintenant, votre interface avec le pilote est ce qui vous donne tous les outils dont vous avez besoin pour que votre programme parle à la carte graphique (ou au pilote, qui parle ensuite à la carte).

Cette interface est destinée à vous donner certains outils . Ces outils prennent la forme d'une API que vous pouvez appeler depuis votre programme.

Cette API est ce que nous voyons être utilisé dans l'exemple ci-dessus. Regardons de plus près.

L'échafaudage

Avant de pouvoir réellement réaliser un dessin, vous devez effectuer une configuration . Vous devez définir votre fenêtre d'affichage (la zone qui sera réellement rendue), votre perspective (la caméra dans votre monde), quel anti-aliasing vous allez utiliser (pour aplanir les contours de votre triangle) ...

Mais nous ne regarderons rien de tout cela. Nous allons juste jeter un coup d'oeil sur les trucs que vous devrez faire à chaque image . Comme:

Effacement de l'écran

Le pipeline graphique ne va pas effacer l'écran pour chaque image. Vous devrez le dire. Pourquoi? C'est pourquoi:

entrez la description de l'image ici

Si vous n'effacez pas l'écran, vous dessinez simplement chaque image. C'est pourquoi nous appelons glClearavec l' GL_COLOR_BUFFER_BITensemble. L'autre bit ( GL_DEPTH_BUFFER_BIT) indique à OpenGL de vider le tampon de profondeur . Ce tampon est utilisé pour déterminer quels pixels sont devant (ou derrière) d'autres pixels.

Transformation

entrez la description de l'image ici
Source de l'image

La transformation est la partie où nous prenons toutes les coordonnées en entrée (les sommets de notre triangle) et appliquons notre matrice ModelView. C'est la matrice qui explique comment notre modèle (les sommets) est pivoté, mis à l'échelle et traduit (déplacé).

Ensuite, nous appliquons notre matrice de projection. Cela déplace toutes les coordonnées afin qu'elles soient correctement orientées vers notre caméra.

Maintenant, nous transformons encore une fois, avec notre matrice Viewport. Nous faisons cela pour adapter notre modèle à la taille de notre moniteur. Nous avons maintenant un ensemble de sommets prêts à être rendus!

Nous reviendrons sur la transformation un peu plus tard.

Dessin

Pour dessiner un triangle, nous pouvons simplement dire à OpenGL de commencer une nouvelle liste de triangles en appelant glBeginavec la GL_TRIANGLESconstante.
Il y a aussi d'autres formes que vous pouvez dessiner. Comme une bande de triangle ou un fan de triangle . Ce sont principalement des optimisations, car elles nécessitent moins de communication entre le processeur et le GPU pour dessiner le même nombre de triangles.

Après cela, nous pouvons fournir une liste d’ensembles de 3 sommets qui devraient constituer chaque triangle. Chaque triangle utilise 3 coordonnées (comme nous sommes dans l'espace 3D). De plus, je fournis également une couleur pour chaque sommet, en appelant glColor3f avant d' appeler glVertex3f.

La nuance entre les 3 sommets (les 3 coins du triangle) est calculée automatiquement par OpenGL . Il interpolera la couleur sur toute la surface du polygone.

Interaction

Maintenant, lorsque vous cliquez sur la fenêtre. L'application doit uniquement capturer le message de la fenêtre signalant le clic. Ensuite, vous pouvez exécuter n’importe quelle action de votre programme.

Cela devient beaucoup plus difficile une fois que vous souhaitez commencer à interagir avec votre scène 3D.

Vous devez d’abord savoir clairement à quel pixel l’utilisateur a cliqué dans la fenêtre. Ensuite, en tenant compte de votre perspective , vous pouvez calculer la direction d’un rayon, du point du clic de la souris dans votre scène. Vous pouvez ensuite calculer si un objet de votre scène intersecte ce rayon . Vous savez maintenant si l'utilisateur a cliqué sur un objet.

Alors, comment faites-vous la rotation?

Transformation

Je connais deux types de transformations généralement appliqués:

  • Transformation basée sur la matrice
  • Transformation à base d'os

La différence est que les os affectent les sommets simples . Les matrices affectent toujours tous les sommets dessinés de la même manière. Regardons un exemple.

Exemple

Plus tôt, nous avions chargé notre matrice d’identité avant de tracer notre triangle. La matrice d'identité est une matrice qui ne fournit simplement aucune transformation . Donc, tout ce que je dessine, n’est affecté que par mon point de vue. Ainsi, le triangle ne sera pas tourné du tout.

Si je veux le faire pivoter maintenant, je pourrais soit faire le calcul moi-même (sur le processeur) et simplement appeler glVertex3favec d' autres coordonnées (qui sont pivotées). Ou je pourrais laisser le GPU faire tout le travail, en appelant glRotatefavant de dessiner:

// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);               

amountest, bien sûr, juste une valeur fixe. Si vous voulez animer , vous devrez suivre amountet augmenter chaque image.

Alors, attendez, qu'est-il arrivé à toutes les discussions sur la matrice plus tôt?

Dans cet exemple simple, nous n’avons pas à nous soucier des matrices. Nous appelons simplement glRotatefet cela prend soin de tout cela pour nous.

glRotateproduit une rotation de angledegrés autour du vecteur xyz. La matrice actuelle (voir glMatrixMode ) est multipliée par une matrice de rotation, le produit remplaçant la matrice actuelle, comme si glMultMatrix était appelée avec la matrice suivante comme argument:

x 2 ⁡ 1 - c + cx y ⁡ 1 - c - z ⁢ sx z ⁡ 1 - c + y ⁢ s 0 y ⁢ x 1 - c + z ⁢ sy 2 ⁡ 1 - c + cy ⁢ z ⁡ 1 - c - x ⁢ s 0 x z ⁡ 1 - c - y ⁢ sy ⁢ z ⁡ 1 - c + x sz 2 ⁡ 1 - c + c 0 0 0 0 1

Eh bien, merci pour ça!

Conclusion

Ce qui devient évident, c'est qu'il y a beaucoup de discussions avec OpenGL. Mais ça ne nous dit rien. Où est la communication?

La seule chose que OpenGL nous dit dans cet exemple, c'est quand c'est fait . Chaque opération prendra un certain temps. Certaines opérations prennent incroyablement longtemps, d’autres sont incroyablement rapides.

Envoyer un sommet au GPU sera si rapide que je ne saurais même pas comment l'exprimer. L'envoi de milliers de sommets de la CPU au GPU, chaque image, n'est probablement pas un problème.

L'effacement de l'écran peut prendre une milliseconde ou moins (gardez à l'esprit que vous ne disposez généralement que d'environ 16 millisecondes pour dessiner chaque image), en fonction de la taille de votre fenêtre d'affichage. Pour le supprimer, OpenGL doit dessiner chaque pixel de la couleur souhaitée, ce qui peut représenter des millions de pixels.

En dehors de cela, nous ne pouvons quasiment que demander à OpenGL les capacités de notre adaptateur graphique (résolution maximale, anti-aliasing maximum, profondeur de couleur maximale, ...).

Mais nous pouvons également remplir une texture avec des pixels ayant chacun une couleur spécifique. Chaque pixel contient donc une valeur et la texture est un "fichier" géant rempli de données. Nous pouvons charger cela dans la carte graphique (en créant un tampon de texture), puis charger un shader , demander à ce shader d'utiliser notre texture en tant qu'entrée et exécuter des calculs extrêmement lourds sur notre "fichier".

Nous pouvons alors "rendre" le résultat de notre calcul (sous la forme de nouvelles couleurs) dans une nouvelle texture.

C'est ainsi que vous pouvez faire en sorte que le processeur graphique fonctionne pour vous d'une autre manière. Je suppose que CUDA fonctionne de manière similaire à cet aspect, mais je n’ai jamais eu l’occasion de travailler avec elle.

Nous n’avons vraiment que légèrement touché le sujet. La programmation graphique 3D est un enfer d'une bête.

entrez la description de l'image ici
Source de l'image


38

Il est difficile de comprendre exactement ce que vous ne comprenez pas.

Le GPU a une série de registres que le BIOS mappe. Celles-ci permettent à la CPU d'accéder à la mémoire du GPU et lui demandent d'effectuer des opérations. La CPU connecte des valeurs à ces registres pour mapper une partie de la mémoire du GPU afin que la CPU puisse y accéder. Ensuite, il charge des instructions dans cette mémoire. Il écrit ensuite une valeur dans un registre qui indique au GPU d’exécuter les instructions que le CPU charge dans sa mémoire.

Les informations comprennent le logiciel que le GPU doit exécuter. Ce logiciel est fourni avec le pilote, qui gère ensuite la répartition des responsabilités entre le processeur et le processeur graphique (en exécutant des parties de son code sur les deux périphériques).

Le pilote gère ensuite une série de "fenêtres" dans la mémoire du processeur graphique que le processeur peut lire et écrire. En règle générale, le modèle d'accès implique que la CPU écrit des instructions ou des informations dans la mémoire du GPU mappée, puis charge le GPU, par le biais d'un registre, d'exécuter ces instructions ou de traiter ces informations. Les informations incluent la logique des shader, les textures, etc.


1
Merci pour votre explication. En gros, ce que je n'ai pas compris, c'est comment le jeu d'instructions de la CPU communique avec le jeu d'instructions du GPU, mais il est évident que c'est le pilote qui fait cette partie. C'est ce que je voulais dire par couches d'abstraction.
JohnnyFromBF

2
Il n'y a pas de jeu d'instructions de la CPU impliqué. Le pilote et le runtime compilent votre CUDA, OpenGL, Direct3D, etc. en programmes / noyaux GPU natifs, qui sont ensuite également téléchargés dans la mémoire du périphérique. Le tampon de commande se réfère alors à ceux comme n'importe quelle autre ressource.
Axel Gneiting

2
Je ne suis pas sûr de quels programmes vous faites référence (qui fonctionnent sur le gpu et sont inclus avec les pilotes). Le gpu est en grande partie un matériel à fonction fixe, et les seuls programmes qu’il exécutera sont des shaders, fournis par l’application, et non par le pilote. Le pilote ne compile que ces programmes, puis les charge dans la mémoire du gpu.
Ben Richards

1
@ sidran32: par exemple, dans l'architecture Kepler de nVidia, les noyaux, les flux et les événements sont créés par un logiciel qui s'exécute sur le GPU, et non (généralement) sur le CPU. Le logiciel côté GPU gère également RDMA. L'ensemble de ce logiciel est chargé dans la mémoire du processeur graphique par le pilote et s'exécute en tant que "mini-système d'exploitation" sur le processeur graphique qui gère le côté GPU de la paire coopérant CPU / GPU.
David Schwartz

@ DavidSchwartz J'ai oublié les tâches de calcul GPU. Cependant, ils se comportent toujours de la même manière que les shaders, de toute façon dans la mise en œuvre. Je n’appellerais pas cela un "mini-système d’exploitation", car il n’a pas les mêmes fonctionnalités que les systèmes d’exploitation. C'est toujours un logiciel très spécialisé, car le GPU n'est pas conçu comme un processeur (pour une bonne raison).
Ben Richards

13

J'étais simplement curieux et voulais connaître l'ensemble du processus en double-cliquant sur Triangle.exe sous Windows XP jusqu'à ce que je puisse voir le triangle tourner sur le moniteur. Que se passe-t-il, comment le processeur (qui gère d'abord le fichier .exe) et le processeur graphique (qui affiche finalement le triangle à l'écran) interagissent-ils?

Admettons l'hypothèse que vous savez réellement comment un exécutable s'exécute sur un système d'exploitation et comment cet exécutable est envoyé de votre GPU au moniteur, sans savoir ce qu'il se passe entre les deux. Voyons donc l'aspect matériel et approfondissons l' aspect réponse du programmeur ...

Quelle est l'interface entre le processeur et le GPU?

À l'aide d'un pilote , le processeur peut communiquer à travers les fonctions de la carte mère telles que le PCI vers la carte graphique et lui envoyer des commandes pour exécuter certaines instructions du GPU, accéder / mettre à jour la mémoire du GPU , charger un code à exécuter sur le GPU, etc.

Mais, vous ne pouvez pas vraiment parler directement du matériel ou du pilote à partir du code; alors, cela devra se faire via des API comme OpenGL, Direct3D, CUDA, HLSL, Cg. Tandis que le premier exécute des instructions GPU et / ou met à jour la mémoire du GPU, le dernier exécutera du code sur le GPU car ce sont des langages physique / shader.

Pourquoi exécuter du code sur le GPU et non sur le CPU?

Bien que le processeur soit efficace pour exécuter nos programmes quotidiens de postes de travail et de serveurs, personne n’a beaucoup réfléchi à tous ces graphiques brillants que vous voyez dans les jeux de nos jours. À l'époque, il existait des moteurs de rendu logiciels qui utilisaient certaines caractéristiques 2D et 3D, mais ils étaient très contraignants. Donc, voici où le GPU est entré en jeu.

Le processeur graphique est optimisé pour l’un des calculs graphiques les plus importants, la manipulation matricielle . Alors que le processeur doit calculer chaque multiplication dans une manipulation de matrice un par un (plus tard, des choses comme 3DNow! Et SSE rattrapées), le GPU peut effectuer toutes ces multiplications à la fois! Parallélisme.

Mais les calculs parallèles ne sont pas la seule raison, une autre raison est que le GPU est beaucoup plus proche de la mémoire vidéo, ce qui la rend beaucoup plus rapide que de devoir faire des allers-retours avec le processeur, etc.

Comment ces instructions GPU / mémoire / code affichent-ils des graphiques?

Il manque un élément pour que tout fonctionne, nous avons besoin de quelque chose que nous puissions écrire, que nous pourrons ensuite lire et envoyer à l'écran. Nous pouvons le faire en créant un framebuffer . Quelle que soit l'opération que vous fassiez, vous mettrez éventuellement à jour les pixels dans le framebuffer; qui, outre l'emplacement, contient également des informations sur la couleur et la profondeur.

Donnons un exemple où vous vouliez dessiner un sprite de sang (une image) quelque part; Tout d'abord, la texture de l'arbre elle-même est chargée dans la mémoire du processeur graphique, ce qui permet de la redessiner à volonté. Ensuite, pour dessiner réellement le sprite quelque part, nous pouvons le traduire en utilisant des sommets (en le plaçant correctement), en le pixellisant (en le transformant d’un objet 3D en pixels) et en mettant à jour le framebuffer. Pour en avoir une meilleure idée, voici un diagramme de flux de pipeline OpenGL de Wikipedia:

C’est l’essentiel de l’idée graphique, plus la recherche est un devoir pour le lecteur.


7

Pour garder les choses simples, nous pouvons le décrire comme ceci. Certaines adresses de mémoire sont réservées (par le BIOS et / ou le système d'exploitation) non pas pour la RAM, mais pour la carte vidéo. Toutes les données écrites avec ces valeurs (pointeurs) vont à la carte. Donc, en théorie, tout programme peut écrire directement sur la carte vidéo simplement en connaissant la plage d’adresses et c’est exactement ce qui se faisait jadis. En pratique, avec les systèmes d’exploitation modernes, cela est géré par le pilote vidéo et / ou la bibliothèque graphique située en haut (DirectX, OpenGL, etc.).


1
-1 demande-t-il comment un appel d'API DirectX du processeur peut communiquer avec le GPU, et votre réponse est "c'est géré par le pilote et / ou DirectX" ? Cela n'explique pas non plus comment le code personnalisé (ala CUDA) pourrait être exécuté.
BlueRaja - Danny Pflughoeft

3
s'il vous plaît apprendre à lire. J'ai dit en écrivant dans des adresses de mémoire spécifiques réservées au GPU au lieu de la RAM. Et cela explique comment vous pouvez tout exécuter. Une plage de mémoire est enregistrée pour une carte. Tout ce que vous écrivez dans cette plage va au GPU exécutant le traitement de sommet, CUDA, peu importe.
AZ.

5

Les GPU sont généralement pilotés par des tampons DMA. En d’autres termes, le pilote compile les commandes qu’il reçoit du programme d’espace utilisateur en un flux d’instructions (état du commutateur, dessin de cette manière, contextes de commutateur, etc.), qui sont ensuite copiées dans la mémoire du périphérique. Il ordonne ensuite au GPU d'exécuter ce tampon de commande via un registre PCI ou des méthodes similaires.

Ainsi, à chaque appel de tirage, etc., le pilote d’espace utilisateur compilera la commande, qui appelle ensuite le pilote d’espace du noyau via une interruption, puis soumet le tampon de commande à la mémoire de l’appareil et demande au GPU de commencer le rendu.

Sur les consoles, vous pouvez même vous amuser à faire tout cela vous-même, en particulier sur PS3.


0

Je pense que le processeur envoie les données vidéo au GPU via le bus, puis le GPU l'affiche. Donc, plus rapide GPU peut gérer plus de données de la CPU. De cette façon, une partie du traitement de cpuoffload à GPU. Par conséquent, vous obtenez une vitesse plus rapide dans les jeux.

C'est un peu comme la RAM où le processeur stocke des choses afin qu'il puisse charger et traiter rapidement. Les deux rendent les jeux plus rapides.

Ou la carte son ou la carte réseau fonctionnent sur le même principe, c'est-à-dire obtenir des données et décharger du travail de la CPU.


Cela duplique une autre réponse et n’ajoute aucun nouveau contenu. S'il vous plaît, ne postez pas de réponse sauf si vous avez réellement quelque chose de nouveau à contribuer.
DavidPostill

0

Je pense que op ne sait pas exactement ce que le processeur demande à la carte graphique de faire et pourquoi les commandes liées aux graphiques (comme les commandes opengl ou direct3d) ne sont pas envoyées directement au processeur graphique.

Le processeur indique simplement au GPU ce qu'il doit rendre. Toutes les instructions passent d'abord par le processeur où elles sont configurées / initialisées pour permettre au GPU d'effectuer le rendu.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.