Les cartes graphiques/Les cartes graphiques : architecture de base
Dans ce chapitre, nous allons voir l'architecture de base d'une carte accélératrice 3D, et voir quelle est la distinction entre une carte accélératrice et un GPU. Dans ce chapitre, nous allons faire le lien avec le rendu tel que décrit dans le chapitre précédent. Les cartes graphiques modernes implémentent des circuits programmables, qui seront partiellement laissé de côté dans ce chapitre. Nous allons aussi nous concentrer sur les cartes graphiques à placage de texture inverse, le placage de texture direct ayant déjà été abordé dans le chapitre précédent.
L'architecture d'une carte graphique 3D
[modifier | modifier le wikicode]Une carte graphique 3D est composée de plusieurs circuits spécialisés, regroupé dans une même puce électronique pour les GPU modernes. Toute carte graphique contient obligatoirement des circuits essentiels qui ne sont pas liés au rendu 3D proprement dit, mais sont nécessaires pour qu'elle fonctionne : de la mémoire vidéo, des circuits de communication avec le bus, des circuits d’interfaçage avec l'écran, et d'autres circuits.
L'un d'entre eux est un VDC qui est connecté à l'interface avec l'écran. Vous avez bien lu : le VDC est là, mais ne s'occupe pas du traitement de la 3D. Du moins, c'est le cas sur les cartes à placage de texture inverse. Le placage de texture direct utilise au contraire un VDC avec accélération 2D très performant, comme nous l'avons vu au chapitre précédent. Mais nous mettons ce cas particulier de côté.
Les cartes 3D à placage de texture inverse sont des cartes d'affichages auxquelles ont a ajouté des circuits pour le rendu de la 3D. Les circuits non-liés au rendu 3D sont globalement les mêmes que pour une carte d'affichage 2D, aussi nous n'en reparlerons pas ici et nous allons nous concentrer sur les circuits du pipeline graphique.
En plus des circuits de rendu 3D, une carte graphique contient d'autres circuits qui s'occupent de la logistique, du transfert de données ou de la répartition du travail entre les unités de traitement proprement dites. L'un de ces circuits de logistique est le processeur de commandes, dont nous parlerons dans quelques chapitres. Pour simplifier, il commande les différentes étapes du pipeline graphique et s'assure que les étapes s’exécutent dans le bon ordre.
Les circuits de rendu 3D : une séparation entre géométrie et rastérisation
[modifier | modifier le wikicode]Les circuits pour le rendu 3D proprement dit comprennent un ensemble hétérogène de circuits aux fonctions fort différentes. Il est d'usage de découper la carte graphique en circuits qui prennent en charge une étape du pipeline graphique, appelés des unités de traitement graphiques. On trouve ainsi une unité pour le placage de textures, une unité de traitement de la géométrie, une unité de rasterization, une unité d'enregistrement des pixels en mémoire appelée ROP, etc. Les anciennes cartes graphiques fonctionnaient ainsi, mais on verra que les cartes graphiques modernes font un petit peu différemment.
Pour simplifier les explications, nous allons séparer la carte graphique en deux circuits : un qui gère la géométrie, et un qui gère la rastérisation au sens large. Nous allons les appeler l'unité géométrique et le rastériseur, respectivement. Les cartes graphiques modernes fusionnent les deux, dans une certaine mesure, mais oublions cela pour le moment. Cette séparation sera pertinente dans ce qui suit, car les deux circuits ont évolués séparément.
L'unité géométrique lit en mémoire vidéo la scène 3D, qui est mémorisée dans un tampon de sommets. De même, les textures sont lues dans la mémoire vidéo et les pixels calculés sont enregistrés dans le framebuffer. Les cartes graphiques ajoutent souvent des mémoires caches pour la géométrie et les textures, afin de rendre leur accès plus rapide. De même, elles utilisent la technique du z-buffer, vue dans le chapitre précédent, pour éliminer les surfaces cachées.
Le circuit de rastérisation et de plaquage de textures
[modifier | modifier le wikicode]Le circuit de rastérisation est assez complexe car il regroupe des fonctionnalités très différentes. Globalement, on peut la scinder en trois étapes, sur les cartes graphiques les plus simples. Les trois étapes sont réalisées par trois circuits distincts : l'unité de rastérisation, l'unité de placage de texture, et les Raster Operation Pipeline.
Le premier circuit s'occupe de la rastérisation proprement dite, qui projette une scène 3D sur l'écran. L'opération fait passer d'une scène 3D à un écran en 2D avec des pixels. Lors de la rastérisation, chaque sommet est associé à un ou plusieurs pixels, à savoir les pixels qu'il occupe à l'écran. Elle fournit aussi diverses informations qui sont utiles pour la suite du pipeline graphique, notamment la profondeur du sommet associé au pixel. Elle fournit notamment les coordonnées de textures associée au sommet, qui permettent de colorier le pixel.
L'étape de placage de texture lit la texture associée au modèle 3D et identifie le texel adéquat avec les coordonnées textures, pour colorier le pixel. On travaille pixel par pixel, on récupère le texel associé à chaque pixel. Soit l'inverse du placage de texture direct, qui traversait une texture texel par texel, pour recopier le texel dans le pixel adéquat.
Après l'étape de placage de textures, la carte graphique enregistre le résultat en mémoire. Lors de cette étape, divers traitements de post-traitement sont effectués et divers effets peuvent être ajoutés à l'image. Un effet de brouillard peut être ajouté, des tests de profondeur sont effectués pour éliminer certains pixels cachés, l'antialiasing est ajouté, on gère les effets de transparence, etc. Un chapitre entier sera dédié à ces opérations.
Les circuits d'élimination des pixels cachés
[modifier | modifier le wikicode]L'élimination des surfaces cachées élimine les triangles invisibles à l'écran car cachés par un objet. Elle est prise en charge par un circuit dont la position dans le pipeline graphique varie, mais qui est toujours situé après l'étape de rastérisation. En effet, elle demande de connaitre la profondeur d'un pixel, qui est calculée par l'étape de rastérisation. Elle est réalisée soit immédiatement après la rastérisation, soit lors des ROPs.
Elle est généralement réalisée dans les ROPs car cela permet de gérer la transparence. Dès que de la transparence est présente, on ne peut pas éliminer les triangles invisibles avant le placage de textures. En effet, on ne sait pas si une texture transparente sera plaquée sur le triangle ou non. La seule option est alors de gérer la transparence et l'élimination des surfaces cachées dans les ROP.
Mais la plupart des cartes graphiques incorporent des circuits d'élimination des surfaces cachées juste après la rastérisation, pour gérer le cas où on sait d'avance que les textures ne sont pas transparentes. Cela permet d'éliminer à l'avance les triangles dont on sait qu'ils ne seront pas rendus.
Les deux possibilités coexistent sur les cartes graphiques modernes. Une carte graphique moderne peut éliminer les surfaces cachées soit après la rastérisation, soit avant, suivant que les textures sont marquées comme transparentes. Mais cela demande d'implémenter quelques techniques d'realy-z dont nous parlerons plus tard, dans un chapitre dédié sur la rastérisation.
Les circuits d'éclairage
[modifier | modifier le wikicode]Les circuits de rastérisation précédents correspondent au cas d'une carte graphique très simple, qui ne gère pas les techniques d'éclairage. Mais les cartes graphiques de ce genre n'ont tout simplement pas existé. Elles étaient toutes capables de gérer une forme d'éclairage limitée. Cependant, il faut faire la différence entre l'éclairage par pixel que l'éclairage par sommet. Pour rappel, l'éclairage par sommet attribue une couleur et une luminosité à chaque sommet. Et vu qu'un sommet prend plusieurs pixels à l'écran, l'éclairage est donc assez grossier. L'éclairage par pixel est lui plus fin car il attribue une luminosité pour chaque pixel de l'écran.
Les deux formes d'éclairage sont implémentées différemment. L'éclairage par sommet est réalisé principalement dans l'unité de traitement de la géométrie, alors que l'éclairage par pixel demande une coopération entre l'unité de rastérisation et l'unité de traitement de la géométrie.
L'éclairage par sommet est réalisé par un circuit incorporé dans l'unité de traitement géométrique. Il est réalisé typiquement après ou pendant le calcul de la géométrie proprement dit. En général, un triangle est éclairé juste après avoir être calculé, mais le traitement se fait triangle par triangle.
Mais une implémentation complète demande de modifier l'unité de rastérisation. En effet, l'éclairage par sommet calcule une luminosité pour chaque triangle/sommet, comprise entre 0 (très sombre) et 1 (très brillant). Et elle doit être combinée avec le pixel texturé pour faire son œuvre. Typiquement, le pixel texturé est multiplié par la luminosité calculée par l'unité de traitement géométrique. On lit la texture, et le texel lu est alors modifié suivant la luminosité du triangle/sommet associé. Il y a donc un circuit de combinaison situé après l'unité de texture qui effectue la combinaison/multiplication.
L'éclairage par pixel est implémenté de la même manière que l'éclairage par sommet. L'unité de texture est suivie par une unité d'éclairage par pixel dédiée. Dans le cas le plus simple, elle travaille à partir des données fournies par l'unité géométrique. Mais elle n'utilise pas la luminosité calculée par sommet. A la place, elle récupère les normales de chaque sommet, qui ont été interpolées par l'unité de rastérisation, et effectue un calcul d'éclairage pour calculer la luminosité. Et c'est cette luminosité qui est multipliée par le combiner.
Parfois, l'opération de combinaison n'est pas une multiplication, mais une addition. Dans ce cas, on additionne le pixel texturé avec la couleur calculée par l'unité de traitement. Il est alors possible de choisir quelle opération effectuer pour combiner texture et luminosité/couleur d'un triangle/sommet. Le circuit de combinaison situé après les textures est alors configurable. Un tel circuit de combinaison s'appelle alors un combiner, dans la vielle nomenclature graphique de l'époque des années 90-2000.
les cartes graphiques ont depuis fortement modifié leurs circuits d'éclairage par pixel. De nos jours, les circuits d'éclairage, combiner inclus, n'existent plus vraiment. Ils ont été remplacés par un processeur dit de pixel shader, qui implémente des algorithmes d'éclairage avancés, voire des techniques de rendu non-liées à l'éclairage. L'unité de texture est inclue dans ce processeur de shader. L'avantage est que l'éclairage est maintenant programmable par le programmeur, et n'est plus limité à quelques algorithmes d'éclairage par pixel choisit par le constructeur de la carte graphique. Le programme d'éclairage est appelé un shader, il s'exécute sur un processeur et combine l'éclairage avec les textures.
L'évolution de circuits d'éclairage fixes vers des processeurs de shaders s'est faite assez lentement, à force d'amélioration des circuits précédents, et nous détaillerons cela dans le prochains chapitre. Pour en faire un bref résumé, il a d'abord fallu que les techniques de multitexturing, qui appliquent plusieurs textures sur une surface, fassent surface. Puis, ces techniques de multitexturing ont évolué vers toujours plus de programmation, avant de laisser la main aux shaders. Le processus est alors compliqué à expliquer et n'a de sens que si on retrace l'histoire des cartes graphiques, ce qui est le sujet du chapitre suivant. Passons maintenant à autre chose.
Les cartes graphiques en mode immédiat et à tuile
[modifier | modifier le wikicode]Il existe deux types de cartes graphiques : celles en mode immédiat, et celles avec un rendu en tiles. Les deux ont sensiblement les mêmes circuits, à quelques différences près, mais elles les utilisent d'une manière fort différente. Dans la suite de ce cours, nous parlerons surtout des cartes graphiques en mode immédiat, les architectures en tile étant reléguées à la fin du cours.
Les cartes graphiques des ordinateurs de bureau ou portables sont toutes en mode immédiat, alors que les cartes graphiques des appareils mobiles, smartphones et autres équipements embarqués ont un rendu en tiles. Les raisons à cela sont multiples. La première est que les architectures en tiles sont considérées comme moins performantes que celles en mode immédiat et sont donc utilisées pour les équipements où la performance n'est pas une priorité, comme les appareils mobiles. Par contre, le rendu en tiles est plus facile à implémenter en matériel. Une autre caractéristique est un avantage du rendu en tile pour le rendu en 2D, comparé aux architectures en mode immédiat, qui se marie bien aux besoins des smartphones et autres objets connectés.
Les cartes graphiques en mode immédiat effectuent la rasterisation triangle par tringle, pixel par pixel. L'unité géométrique envoie ses résultats à l'unité de rastérisation, sans passer par la mémoire. Entre l'unité géométrique et le rasteriseur, se trouve une mémoire tampon, appelé le tampon de primitives. Le tampon de primitives est nécessaire car il y a un déséquilibre entre géométrie et rastérisation. Un triangle dans une scène 3D correspond généralement à plusieurs pixels. La rastérisation prend donc plus de temps de calcul que la géométrie. Une conséquence est qu'il arrive fréquemment que le circuit de rastérisation soit occupé, alors que l'unité de géométrie veut lui envoyer des données. Le tampon de sommet permet d'accumuler les sommets calculés quand l'unité de rastérisation est occupé.
Avec le rendu en tiles, la géométrie est intégralement rendue avant de faire la rastérisation et le résultat est mémorisé en mémoire vidéo. La rastérisation découpe l'écran en l'écran en carrés/rectangles, appelés des tiles, qui sont rendus séparément, les unes après les autres. L'unité géométrique mémorise la scène 3D en mémoire vidéo en tenant compte de la rasterisation, en regroupant les triangles par tile. Le rastériseur lit alors la géométrie d'une tile depuis la mémoire vidéo et la rastérise.
L'avantage est que la tile est tellement petite qu'elle est intégralement rastérisée sans avoir à passer par la mémoire vidéo. En effet, le rastériseur incorpore une petite mémoire SRAM qui mémorise tout ce qui est nécessaire pour rendre une tile. Pas besoin d’accéder à un gigantesque z-buffer pour toute l'image, juste besoin d'un z-buffer minuscule pour la tile en cours de traitement, qui tient totalement dans la SRAM pour la tile.
La performance d'une carte graphique est limitée par la quantité d'accès mémoire qu'on peut effectuer par seconde. Autant dire que les économiser est primordial. Avec une architecture en mode immédiat, le tampon de primitives évite d'avoir à passer par la mémoire vidéo. Par contre, la rastérisation utilise énormément d'accès mémoire pour rendre les pixels, notamment à cause du z-buffer. Peu d'accès mémoire liés à la géométrie, mais la rastérisation est gourmande en accès mémoire. Avec les architectures à tile, c'est l'inverse. La géométrie est enregistrée en mémoire vidéo, mais la rastérisation ne fait que très peu d'accès mémoire grâce à la SRAM pour les tiles.
Au final, les deux architectures sont optimisées pour deux types de rendus différents. Les cartes à rendu en tuile brillent quand la géométrie n'est pas trop compliquée, mais que la rastérisation et les traitements des pixels sont lourds. Les cartes en mode immédiat sont elles douées pour les scènes géométriquement lourdes, mais avec peu d'accès aux pixels. Le tout est limité par divers caches qui tentent de rendre les accès mémoires moins fréquents, sur les deux types de cartes, mais sans que ce soit une solution miracle.
Un avantage des GPU en rendu à tile est que l’antialiasing est plus rapide. Pour ceux qui ne le savent pas, l'antialiasing est une technique qui améliore la qualité d’image, en simulant une résolution supérieure. Une image rendue avec antialiasing aura la même résolution que l'écran, mais n'aura pas certains artefacts liés à une résolution insuffisante. Et l'antialiasing a lieu intégralement dans les circuits situés dans l'étape de rastérisation ou après. Tout se passe donc dans le rastériseur. Les GPU en mode immédiat disposent de techniques d'optimisations pour l’antialiasing, mais il n’empêche que le GPU doit mémoriser des informations en mémoire vidéo pour faire un antialiasing correct. Alors qu'avec le rendu en tiles, la SRAM qui mémorise la tile en cours suffit pour l'antialiasing. L'antialiasing est donc plus rapide.