66 votes

Comment le CPU et le GPU interagissent-ils dans l'affichage des graphiques informatiques ?

Vous pouvez voir ici une capture d'écran d'un petit programme C++ appelé Triangle.exe avec un triangle rotatif basé sur l'API OpenGL.

enter image description here

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

J'étais juste curieux et je voulais connaître tout le processus depuis le double clic 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 CPU (qui traite d'abord le .exe) et le GPU (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 informatique

  • HDD
  • Mémoire système (RAM)
  • CPU
  • Mémoire vidéo
  • GPU
  • Affichage LCD

Logiciel

  • Système d'exploitation
  • API DirectX/OpenGL
  • Pilote Nvidia

Quelqu'un peut-il expliquer le processus, peut-être avec une sorte d'organigramme pour l'illustrer ?

Il ne doit pas s'agir d'une explication complexe couvrant chaque étape (cela dépasserait le cadre de l'étude), mais d'une explication qu'un informaticien de niveau intermédiaire peut suivre.

Je suis sûr que beaucoup de personnes qui se qualifient de professionnels de l'informatique ne pourraient pas décrire ce processus correctement.

60voto

diegogs Points 624

J'ai décidé d'écrire un peu sur l'aspect programmation et comment les composants communiquent entre eux. Peut-être que cela vous éclairera sur certains points.

La présentation

Que faut-il pour que la seule image que vous avez affichée dans votre question soit dessinée à l'écran ?

Il existe de nombreuses façons de dessiner un triangle à l'écran. Pour simplifier, supposons qu'aucun tampon de sommets n'a été utilisé. (A Tampon de vertex est une zone de la mémoire où vous stockez les coordonnées). Supposons que le programme indique simplement au pipeline de traitement graphique tous les sommets (un sommet est simplement une coordonnée dans l'espace) d'une rangée.

Mais Avant de pouvoir dessiner quoi que ce soit, nous devons d'abord mettre en place un échafaudage. Nous allons voir 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 nous en tiendrons à OpenGL. Maintenant, votre interface avec le pilote est ce qui vous donne tous les outils dont vous avez besoin pour faire de votre programme parler à la carte graphique (ou au pilote, qui alors discussions à la carte).

Cette interface va certainement vous donner certaines outils . Ces outils prennent la forme d'un API que vous pouvez appeler depuis votre programme.

C'est cette API que nous voyons utilisée dans l'exemple ci-dessus. Regardons de plus près.

L'échafaudage

Avant de pouvoir réellement dessiner, vous devez effectuer une vérification de l'état des lieux. configuration . Vous devez définir votre fenêtre d'affichage (la zone qui sera effectivement rendue), votre perspective (le point de vue de l'utilisateur) et le point de vue de l'utilisateur. caméra dans votre monde), quel anticrénelage vous utiliserez (pour lisser les bords de votre triangle)...

Mais nous ne regarderons pas tout cela. Nous allons juste jeter un coup d'oeil sur les choses que vous aurez à faire chaque cadre . Comme :

Effacer l'écran

Le pipeline graphique ne va pas nettoyer l'écran pour vous à chaque image. Vous devrez lui dire. Pourquoi ? Voici pourquoi :

enter image description here

Si vous ne dégagez pas l'écran, vous allez simplement tirer sur à chaque image. C'est pourquoi nous appelons glClear avec le GL_COLOR_BUFFER_BIT réglé. L'autre bit ( GL_DEPTH_BUFFER_BIT ) indique à OpenGL d'effacer l'indicateur profondeur Tampon. Cette mémoire tampon est utilisée pour déterminer quels pixels se trouvent devant (ou derrière) d'autres pixels.

Transformation

enter image description here
Source de l'image

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

Ensuite, nous appliquons notre matrice de projection. Cela déplace toutes les coordonnées pour qu'elles fassent correctement face à notre caméra.

Maintenant, nous transformons une fois de plus, avec notre matrice Viewport. Nous faisons cela pour mettre à l'échelle notre modèle à la taille de notre écran. Nous avons maintenant un ensemble de sommets qui sont 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 un nouveau fichier liste de triangles en appelant glBegin avec le GL_TRIANGLES constant.
Il existe également d'autres formes que vous pouvez dessiner. Comme un bande triangulaire ou un ventilateur triangulaire . Il s'agit principalement d'optimisations, car elles nécessitent moins de communication entre le CPU et le GPU pour dessiner la même quantité de triangles.

Ensuite, nous pouvons fournir une liste d'ensembles de 3 sommets qui devraient constituer chaque triangle. Chaque triangle utilise 3 coordonnées (puisque nous sommes dans un espace 3D). De plus, je fournis également un couleur pour chaque sommet, en appelant glColor3f antes de en appelant glVertex3f .

L'ombre entre les 3 sommets (les 3 coins du triangle) est calculée par OpenGL automatiquement . Il interpolera la couleur sur toute la face du polygone.

Interaction

Maintenant, quand vous cliquez sur la fenêtre. L'application n'a plus qu'à capturer le message de la fenêtre qui signale le clic. Ensuite, vous pouvez exécuter n'importe quelle action dans votre programme que vous voulez.

Cela donne un lot plus difficile une fois que vous voulez commencer à interagir avec votre scène 3D.

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

Alors, comment le faire tourner ?

Transformation

Je connais deux types de transformations qui sont généralement appliquées :

  • Transformation matricielle
  • Transformation osseuse

La différence est que ossements affecter simple sommets . Les matrices affectent toujours tous les sommets dessinés de la même manière. Prenons un exemple.

Exemple

Plus tôt, nous avons chargé notre matrice identité avant de dessiner notre triangle. La matrice d'identité est une matrice qui fournit simplement pas de transformation du tout. Donc, tout ce que je dessine, n'est affecté que par ma perspective. Donc, le triangle ne sera pas tourné du tout.

Si je veux le faire tourner maintenant, je peux soit faire les calculs moi-même (sur le CPU) et simplement appeler glVertex3f con autre (qui subissent une rotation). Ou je pourrais laisser le GPU faire tout le travail, en appelant glRotatef avant de dessiner :

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

amount est, bien sûr, juste une valeur fixe. Si vous voulez animer vous devrez garder la trace de amount et l'augmenter à chaque image.

Alors, attendez, qu'est-il arrivé à toute la discussion sur la matrice plus tôt ?

Dans cet exemple simple, nous n'avons pas à nous soucier des matrices. Nous appelons simplement glRotatef et il s'occupe de tout ça pour nous.

glRotate produit une rotation de angle degrés autour du vecteur x y z . La matrice de courant (voir glMatrixMode ) est multipliée par une matrice de rotation, le produit remplaçant la matrice courante, comme si glMultMatrix ont été appelés avec la matrice suivante comme argument :

x 2 1 - c + c x y 1 - c - z s x z 1 - c + y s 0 y x 1 - c + z y 2 1 - c + c y z 1 - c - x s 0 x z 1 - c - y s y z 1 - c + x s z 2 1 - c + c 0 0 1

Eh bien, merci pour ça !

Conclusion

Ce qui devient évident, c'est qu'il y a beaucoup de discussions à OpenGL. Mais il ne dit pas nous n'importe quoi. Où est la communication ?

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

Envoi d'un sommet vers le GPU sera si rapide que je ne saurais même pas comment l'exprimer. L'envoi de milliers de sommets du CPU vers le GPU, à chaque image, ne posera probablement aucun problème.

Effacer l'écran peut prendre une milliseconde ou pire (n'oubliez pas 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. Pour l'effacer, OpenGL doit dessiner chaque pixel dans la couleur que vous voulez effacer, ce qui peut représenter des millions de pixels.

En dehors de cela, nous ne pouvons demander à OpenGL que les capacités de notre carte graphique (résolution maximale, anticrénelage maximal, profondeur de couleur maximale, ...).

Mais on peut aussi remplir une texture avec des pixels qui ont 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 ce fichier dans la carte graphique (en créant un buffer de texture), puis charger un fichier nuanceur Pour cela, il faut dire à ce shader d'utiliser notre texture comme entrée et d'exécuter des calculs très lourds sur notre "fichier".

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

C'est ainsi que vous pouvez faire travailler le GPU pour vous d'une autre manière. Je suppose que CUDA a des performances similaires à cet aspect, mais je n'ai jamais eu l'occasion de travailler avec.

Nous n'avons vraiment qu'effleuré le sujet. La programmation graphique 3D est une sacrée bête.

enter image description here
Source d'image

44voto

David Schwartz Points 60868

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

Le GPU possède une série de registres que le BIOS mappe. Ces registres permettent au CPU d'accéder à la mémoire du GPU et d'ordonner au GPU d'effectuer des opérations. Le CPU introduit des valeurs dans ces registres pour mapper une partie de la mémoire du GPU afin que le CPU puisse y accéder. Il charge ensuite 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 a chargées dans sa mémoire.

L'information consiste en un logiciel dont le GPU a besoin pour fonctionner. Ce logiciel est fourni avec le pilote et ce dernier gère la répartition des responsabilités entre le CPU et le GPU (en exécutant des parties de son code sur les deux appareils).

Le pilote gère ensuite une série de "fenêtres" dans la mémoire du GPU, dans lesquelles le CPU peut lire et écrire. En général, le modèle d'accès implique que le CPU écrive des instructions ou des informations dans la mémoire mappée du GPU, puis demande au GPU, par le biais d'un registre, d'exécuter ces instructions ou de traiter ces informations. Les informations comprennent la logique des shaders, les textures, etc.

16voto

Tamara Wijsman Points 56163

J'étais juste curieux et je voulais connaître le processus complet depuis le double clic 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 CPU (qui traite d'abord le .exe) et le GPU (qui affiche finalement le triangle à l'écran) interagissent-ils ?

Partons du principe que vous savez comment un exécutable s'exécute sur un système d'exploitation et comment cet exécutable est envoyé de votre GPU à l'écran, mais que vous ne savez pas ce qui se passe entre les deux. Examinons donc l'aspect matériel et poussons plus loin l'analyse de l'environnement. aspect programmeur répondre...

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

Utilisation d'un conducteur le CPU peut parler à travers carte mère comme PCI à la carte graphique et lui a envoyé des commandes pour qu'elle exécute certaines instructions du GPU, accès / mise à jour de la mémoire du GPU , charger un code à exécuter sur le GPU et plus encore...

Mais il n'est pas possible de s'adresser directement au matériel ou au pilote à partir du code. Il faut donc passer par des API comme OpenGL, Direct3D, CUDA, HLSL, Cg. Alors que les premières exécutent les instructions du GPU et / ou mettent à jour la mémoire du GPU, les dernières exécutent réellement le code sur le GPU car il s'agit de langages de physique / shaders.

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

Alors que le processeur est bon pour faire tourner nos programmes quotidiens de stations de travail et de serveurs, il n'y avait pas grand chose à penser de tous ces graphiques brillants que l'on voit dans les jeux de ces jours-ci. À l'époque, il existait des logiciels de rendu qui faisaient l'affaire pour certaines choses en 2D et en 3D, mais ils étaient très limités. C'est donc ici que le GPU est entré en jeu.

Le GPU est optimisé pour l'un des calculs les plus importants en matière de graphisme, Manipulation de la matrice . Alors que le processeur doit calculer chaque multiplication dans une manipulation matricielle une par une (plus tard, des choses telles que 3DNow ! et SSE rattrapé), le GPU peut effectuer toutes ces multiplications en même temps ! Le 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 le rend beaucoup plus rapide que de devoir faire des allers-retours avec le CPU, etc...

Comment les instructions / la mémoire / le code du GPU permettent-ils d'afficher des graphiques ?

Il manque une pièce pour que tout cela fonctionne : nous avons besoin d'un support sur lequel nous pouvons écrire et que nous pouvons ensuite lire et envoyer à l'écran. Nous pouvons le faire en créant un framebuffer . Quelle que soit l'opération que vous effectuez, vous finirez par mettre à jour les pixels du framebuffer, qui, outre l'emplacement, contient également des informations sur la couleur et la profondeur.

Prenons un exemple où vous souhaitez dessiner un sprite de sang (une image) quelque part ; tout d'abord, la texture de l'arbre lui-même est chargée dans la mémoire du GPU, ce qui permet de le redessiner facilement à volonté. Ensuite, pour dessiner réellement le sprite quelque part, nous pouvons traduire le sprite en utilisant des vertex (le placer à la bonne position), le rastériser (le transformer d'un objet 3D en pixels) et mettre à jour le framebuffer. Pour avoir une meilleure idée, voici un organigramme du pipeline OpenGL tiré de Wikipedia :

Il s'agit là de l'essentiel de l'idée du graphique, mais le lecteur devra faire des recherches supplémentaires.

9voto

dthrasher Points 10641

Pour rester simple, nous pouvons le décrire comme suit. Certaines adresses 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. Toute donnée écrite à ces valeurs (pointeurs) va à la carte. Ainsi, en théorie, tout programme peut écrire directement sur la carte vidéo en connaissant simplement la plage d'adresses et c'est exactement comme cela que cela se passait autrefois. En pratique, avec les systèmes d'exploitation modernes, cette opération est gérée par le pilote vidéo et/ou la bibliothèque graphique (DirectX, OpenGL, etc.).

7voto

Vikarti Points 101

Les GPU sont généralement pilotés par des tampons DMA. C'est-à-dire que le pilote compile les commandes qu'il reçoit du programme de l'espace utilisateur en un flux d'instructions (changer d'état, dessiner ceci de telle manière, changer de contexte, etc.), qui sont ensuite copiées dans la mémoire du périphérique. ), qui sont ensuite copiées dans la mémoire du périphérique. Il donne ensuite l'instruction au GPU d'exécuter ce tampon de commandes via un registre PCI ou des méthodes similaires.

Ainsi, à chaque appel de dessin, etc., le pilote de l'espace utilisateur compile la commande, qui appelle ensuite le pilote de l'espace noyau via une interruption, et ce dernier soumet finalement le tampon de commande à la mémoire du périphérique et demande au GPU de commencer le rendu.

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

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X