Les cartes graphiques/Les cartes accélératrices 3D

Un livre de Wikilivres.
Aller à : navigation, rechercher

Le premier jeu à utiliser de la "vraie" 3D fût le jeu Quake, premier du nom. Et depuis sa sortie, presque tous les jeux vidéos un tant soit peu crédibles utilisent de la 3D. Face à la prolifération de ces jeux vidéos en 3D, les fabricants de cartes graphiques se sont adaptés et ont inventé des cartes capables d'accélérer les calculs effectués pour rendre une scène en 3D : les cartes accélératrices 3D.

Concepts de base du rendu 3D[modifier | modifier le wikicode]

Une scène 3D est composée d'un espace en trois dimensions, dans laquelle le moteur physique du jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert de système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.

Dans toute scène 3D, on trouve une caméra, qui représente les yeux du joueur. Cette caméra est définie par :

  • une position ;
  • par la direction du regard (un vecteur) ;
  • le champ de vision (un angle) ;
  • un plan qui représente l'écran du joueur ;
  • et un plan au-delà duquel on ne voit plus les objets.
Caméra.

Ces autres objets sont composés de formes de base, combinées les unes aux autres pour former des objets complexes. Ces formes géométriques peuvent être des triangles, des carrés, des courbes de Béziers, etc. Dans la majorité des jeux vidéos actuels, nos objets sont modélisés par un assemblage de triangles collés les uns aux autres. Ces triangles sont définis par leurs sommets, qui sont appelés des vertices. Chaque vertice possède trois coordonnées, qui indiquent où se situe le sommet dans la scène 3D : abscisse, ordonnée, profondeur.

Illustration des vertices pour un modèle 3D complexe.

Pour rajouter de la couleur, ces objets sont recouverts par des textures, des images qui servent de papier peint à un objet. Un objet géométrique est recouvert par une ou plusieurs textures, qui permettent de le colorier ou de lui appliquer du relief.

Pipeline graphique[modifier | modifier le wikicode]

Pipelines graphiques Open Gl et Direct X.

Depuis un bon moment, les jeux vidéos utilisent une technique de rendu spécifique : la rasterization. Celle-ci calcule une scène 3D intégralement, avant de faire des transformations pour n'afficher que ce qu'il faut à l'écran. Le calcul de l'image finale passe par diverses étapes bien séparées, l'ensemble étant appelé le pipeline graphique. Le cas le plus simple ne demandant que quatre étapes :

  • une étape de traitement des vertices ;
  • une étape de rasterization, qui va déterminer quelle partie de l'image 3D s'affiche à l'écran, et qui attribue chaque vertice à un pixel donné de l'écran ;
  • une étape de texturing et de traitement des pixels ;
  • une étape d'enregistrement des données calculées en mémoire.

Dans certains cas, des traitements supplémentaires sont ajoutés. Par exemple, les cartes graphiques modernes supportent une étape en plus, qui permet de rajouter de la géométrie : l'étape de tesselation. Cela permet de déformer les objets ou d'augmenter leur réalisme.

Traitement des vertices[modifier | modifier le wikicode]

La première étape place les objets au bon endroit dans la scène 3D. Lors de la modélisation d'un objet, celui-ci est encastré dans un cube : un sommet du cube possède la coordonnée (0, 0, 0), et les vertices de l'objet sont définies à partir de celui-ci. Pour placer l'objet dans la scène, il faut tenir compte de sa localisation, calculée par le moteur physique : si le moteur physique a décrété que l'objet est à l'endroit de coordonnées (50, 250, 500), toutes les coordonnées des vertices de l'objet doivent être modifiées. Pendant cette étape, l'objet peut subir une translation, une rotation, ou un gonflement/dégonflement (on peut augmenter ou diminuer sa taille). C'est la première étape de calcul : l'étape de transformation.

Ensuite, les vertices sont éclairées dans une phase de lightning. Chaque vertice se voit attribuer une couleur, qui définit son niveau de luminosité : est-ce que la vertice est fortement éclairée ou est-elle dans l'ombre ?

Vient ensuite une phase de traitement de la géométrie, où les vertices sont assemblées en triangles, points, ou lignes, voire en polygones. Ces formes géométriques de base sont ensuite traitées telles quelles par la carte graphique. Sur les cartes graphiques récentes, cette étape peut être gérée par le programmeur : il peut programmer les divers traitements à effectuer lui-même.

Rasterization[modifier | modifier le wikicode]

Vient ensuite la traduction des formes (triangles) rendues dans une scène 3D en un affichage à l'écran. Cette étape de rasterization va projeter l'image visible sur notre caméra. Et cela nécessite de faire quelques calculs. Tout d'abord, la scène 3D va devoir passer par une phase de clipping : les triangles qui ne sont pas visibles depuis la caméras sont oubliés. Ensuite, ils passent par une phase de culling, qui élimine les pixels cachés par un objet géométrique. Enfin, chaque pixel de l'écran se voit attribuer un ou plusieurs triangle(s). Cela signifie que sur le pixel en question, c'est le triangle attribué au pixel qui s'affichera. C'est lors de cette phase de rasterisation que la perspective est gérée, en fonction de la position de la caméra.

Pixels et textures[modifier | modifier le wikicode]

À la suite de cela, les textures sont appliquées sur la géométrie. La carte graphique sait à quel triangle correspond chaque pixel et peut donc colorier le pixel en question en fonction de la couleur de la texture appliquée sur la géométrie. C'est la phase de Texturing. Sur les cartes graphiques récentes, cette étape peut être gérée par le programmeur : il peut programmer les divers traitements à effectuer lui-même. En plus de cela, les pixels de l'écran peuvent subir des traitements divers et variés avant d'être enregistrés et affichés à l'écran. Un effet de brouillard peut être ajouté, des tests de visibilité sont effectués, l'antialiasing est ajouté, etc.

Architecture d'une carte 3D[modifier | modifier le wikicode]

Avant l'invention des cartes graphiques, toutes ces étapes étaient réalisées par le processeur : il calculait l'image à afficher, et l’envoyait à une carte d'affichage 2D. Au fil du temps, de nombreux circuits furent ajoutés, afin de déporter un maximum de calculs vers la carte vidéo. Pour déléguer ses calculs à la carte 3D, les applications pourraient communiquer directement avec la carte graphique, sans prendre en compte toute contrainte de compatibilité. Pour éviter cela, les concepteurs de systèmes d'exploitations et de cartes graphiques ont inventé des API 3D, des bibliothèques qui fournissent des fonctions que l'application pourra exécuter au besoin. De nos jours, les plus connues sont DirectX, et OpenGL. Les fonctions de ces APIs vont préparer des données à envoyer à la carte graphique, avant que le pilote s'occupe des les communiquer à la carte graphique. Un driver de carte graphique gère la mémoire de la carte graphique : où placer les textures, les vertices, et les différents buffers de rendu. Le pilote de carte graphique est aussi chargé de traduire les shaders, écrits dans un langage de programmation comme le HLSL ou le GLSL, en code machine. Ces APIs permettent de déléguer certaines tâches au GPU.

Répartition du travail entre processeur et GPU, sur les cartes graphiques récentes. On voit que le GPU s'occupe des traitements liés au moteur graphique, tandis que les autres traitements (son, physique) sont pris en charge par le processeur.

Minimum vital[modifier | modifier le wikicode]

Toute carte graphique contient obligatoirement certains circuits :

  • la mémoire vidéo ;
  • les circuits de communication avec le bus ;
  • le processeur de commandes.

On trouve naturellement d'autres circuits qui s'occupent de la lecture ou de l'écriture des texture et vertices dans la carte vidéo. Ces deux circuits portent le nom d'unité de texture et d'input assembler. Le reste de la carte graphique est un ensemble hétérogène de circuits aux fonctions forts différentes, nommé le pipeline. Ce pipeline est composé de circuits non-programmables (dits fixes) et de circuits programmables, ce qui permet de faire la distinction entre pipeline programmable qui regroupe les circuits programmables et le pipeline fixe pour le reste.

Architecture de base d'une carte 3D - 1

Mémoire vidéo[modifier | modifier le wikicode]

La carte graphique a besoin d'une mémoire RAM : la mémoire vidéo. Cette mémoire vidéo, est très proche des mémoires RAM qu'on trouve sous forme de barrettes dans nos PC, à quelques différences près : la mémoire vidéo peut supporter un grand nombre d'accès mémoire simultanés, et elle est optimisée pour accéder à des données proches en mémoire. Dans le cas le plus simple, elle sert simplement de Framebuffer : elle stocke l'image à afficher à l'écran. Au fil du temps, elle s'est vu ajouter d'autres fonctions : stocker les textures et les vertices de l'image à calculer, ainsi que divers résultats temporaires.

Bus vidéo[modifier | modifier le wikicode]

La carte graphique communique via un bus, un vulgaire tas de fils qui connectent la carte graphique à la carte mère. Les premières cartes graphiques utilisaient un bus nommé ISA, qui fût rapidement remplacé par le bus PCI, plus rapide. Viennent ensuite le bus AGP, puis le bus PCI-Express. Ce bus est géré par un contrôleur de bus, un circuit qui se charge d'envoyer ou de réceptionner les données sur le bus. Il contient quelques registres dans lesquels le processeur pourra écrire ou lire, afin de lui envoyer des ordres du style : j'envoie une donnée, transmission terminée, je ne suis pas prêt à recevoir les données que tu veux m'envoyer, etc.

Les anciennes cartes graphique pouvaient lire ou écrire directement dans la mémoire RAM, grâce à certaines fonctionnalités du bus AGP. Mais généralement, les données sont copiées depuis la mémoire RAM vers la mémoire vidéo, en passant par le bus. Cette copie est effectuée par un circuit spécialisé : le contrôleur DMA, qui permet d'échanger des données entre mémoire vidéo et mémoire RAM sans devoir utiliser le processeur. Il est souvent intégré dans le contrôleur de bus.

Processeur de commandes[modifier | modifier le wikicode]

Tout les traitements que la carte graphique doit effectuer, qu'il s'agisse de rendu 2D, de calculs 2D, du décodage matérielle d'un flux vidéo, ou de calculs généralistes, sont envoyés par le pilote de la carte graphique, sous la forme de commandes. L'envoi des données à la carte graphique ne se fait pas immédiatement : il arrive que la carte graphique n'ait pas fini de traiter les données de l'envoi précédent. Il faut alors faire patienter les données tant que la carte graphique est occupée. Les pilotes de la carte graphique vont les mettre en attente dans une file (une zone de mémoire dans laquelle on stocke des données dans l'ordre d'ajout) : le tampon de commandes. Ensuite, ces commandes sont interprétées par un circuit spécialisé : le processeur de commandes. Celui-ci est chargé de piloter les circuits de la carte graphique.

Circuits fixes[modifier | modifier le wikicode]

Toute carte graphique contient des circuits, aussi appelés unités, qui prennent en charge une étape du pipeline graphique. Entre les différentes unités, on trouve souvent des mémoires pour mettre en attente les vertices ou les pixels, au cas où une unité est trop occupée. Pour plus d'efficacité, ces cartes graphiques possédaient parfois plusieurs unités de traitement des vertices et des pixels, ou plusieurs ROP. Dans ce cas, ces unités multiples sont précédées par un circuit qui se charge de répartir les vertex ou pixels sur chaque unités. Généralement, ces unités sont alimentées en vertex/pixels les unes après les autres (principe du round-robin).

Les toutes premières cartes graphiques contenaient simplement des circuits pour gérer les textures, en plus de la mémoire RAM vidéo. Seules l'étape de texturing, quelques effets graphiques (brouillard) et l'étape d'enregistrement des pixels en mémoire étaient prises en charge par la carte graphique. Par la suite, ces cartes s’améliorèrent en ajoutant plusieurs circuits de gestion des textures, pour colorier plusieurs pixels à la fois. Cela permettait aussi d'utiliser plusieurs textures pour colorier un seul pixel : c'est ce qu'on appelle du multitexturing. Les cartes graphiques construites sur cette architecture sont très anciennes. On parle des cartes graphiques ATI rage, 3DFX Voodoo, Nvidia TNT, etc.

Les cartes suivantes ajoutèrent une gestion des étapes de rasterization directement en matériel. Les cartes ATI rage 2, les Invention de chez Rendition, et d'autres cartes graphiques supportaient ces étapes en hardware. De nos jours, ce genre d'architecture est commune chez certaines cartes graphiques intégrées dans les processeurs ou les cartes mères.

La première carte graphique capable de gérer la géométrie fût la Geforce 256, la toute première Geforce. Son unité de gestion de la géométrie n'est autre que la bien connue T&L (Transform And Lighting). Elle implémentait des algorithmes simples, comme un éclairage de Phong, qui étaient directement câblés dans ses circuits. Nous étudierons d'ailleurs cette unité et les algorithmes qu'elle utilise dans quelques chapitres.

Circuits programmables : vertex et pixels shaders[modifier | modifier le wikicode]

A partir de la Geforce 3 de Nvidia, les unités de traitement de la géométrie sont devenues programmables. Cela permet une grande flexibilité, à savoir que changer le comportement ne nécessite pas de re-câbler tout le circuit. Les unités de traitement de la géométrie deviennent donc des processeurs indépendants, capables d’exécuter des programmes appelés Vertex Shaders. Par la suite, l'étape de traitement des pixels est elle aussi devenue programmable. Des programmes capables de traiter des pixels, les pixels shaders ont fait leur apparition. Une seconde série d'unités a alors été ajoutée dans nos cartes graphiques : les processeurs de pixels shaders. Par la suite, d'autres types de shaders ont été inventés : shaders de géométrie, shaders génériques, etc.

Ces shaders sont écrits dans un langage de haut-niveau, le HLSL ou le GLSL, et sont traduits (compilés) par les pilotes de la carte graphique avant leur exécution. Au fil du temps, les spécifications de ces langages sont devenues de plus en plus riches et le matériel en a fait autant. Les premières cartes graphiques avaient des jeux d'instructions séparés pour les unités de vertex shader et les unités de pixel shader, et les processeurs étaient séparés. Pour donner un exemple, c'était le cas de la Geforce 6800. Cette séparation entre unités de texture et de vertices était motivée par le fait que les unités de vertice n’accédaient jamais à la mémoire, contrairement aux unités de traitement de pixels qui doivent accéder aux textures. Depuis DirectX 10, ce n'est plus le cas : le jeu d'instructions a été unifié entre les vertex shaders et les pixels shaders, ce qui fait qu'il n'y a plus de distinction entre processeurs de vertex shaders et de pixels shaders, chaque processeur pouvant traiter indifféremment l'un ou l'autre.