Les cartes graphiques/Les cartes d'affichage

Les cartes graphiques sont des cartes qui communiquent avec l'écran, pour y afficher des images. Au tout début de l'informatique, ces opérations étaient prises en charge par le processeur : celui-ci calculait l'image à afficher à l'écran, et l'envoyait pixel par pixel à l'écran, ceux-ci étant affichés immédiatement après. Pour simplifier la vie des programmeurs, les fabricants de matériel ont inventé des cartes d'affichage, ou cartes vidéo. Avec celles-ci, le processeur calcule l'image à envoyer à l'écran, la transmet à la carte d'affichage, qui la transmet à l'écran. L'avantage d'une carte d'affichage et qu'elle décharge le processeur de l'envoi de l'image à l'écran. Le processeur n'a pas à se synchroniser avec l'écran, juste à envoyer l'image à une carte d'affichage.

Les cartes graphiques actuelles sont des cartes d'affichage améliorées auxquelles on a ajouté des circuits annexes, afin de leur donner des capacités de calcul pour le rendu 2D et/ou 3D, mais elles n'en restent pas moins des cartes d'affichages. La seule différence est que le processeur n’envoie pas une image à la mémoire vidéo, mais que l'image à afficher est calculée par la carte graphique 2D/3D. Si vous analysez une carte graphique récente, vous verrez que les circuits des premières cartes d'affichage sont toujours là, bien que noyés dans des circuits de calcul ou de rendu 2D/3D.

L'architecture globale d'une carte d'affichage modifier

Une carte d'affichage contient au minium trois circuits : une mémoire vidéo, un circuit de contrôle, un circuit d'interfaçage avec l'écran et un circuit d'interfaçage avec le bus. La mémoire vidéo mémorise l'image à afficher, les deux circuits d'interfaçage permettent à la carte d'affichage de communiquer respectivement avec l'écran et le reste de l'ordinateur, le circuit de contrôle commande les autres circuits et sert de chef d'orchestre pour un orchestre dont les autres circuits seraient les musiciens.

 
Carte d'affichage - architecture.

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. Les circuits de communication avec le bus permettent à l'ordinateur de communiquer avec la carte graphique, via le bus PCI-Express, AGP, PCI ou autre. 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. Il y a peu à dire sur ce circuit, aussi nous allons nous concentrer sur les autres circuits.

Le circuit d'interfaçage électrique se contente de convertir les signaux de la carte graphique en signaux que l'on peut envoyer à l'écran. Il s'occupe notamment de convertir les tensions et courants : si l'écran demande des signaux de 5 Volts mais que la carte graphique fonctionne avec du 3,3 Volt, il y a une conversion à faire. De même, le circuit d'interfaçage électrique peut s'occuper de la conversion des signaux numériques vers de l'analogique. L'écran peut avoir une entrée analogique, surtout s'il est assez ancien. Les anciens écrans CRT ne comprenaient que des données analogiques et pas le binaire, alors que c'est l'inverse pour la carte graphique, ce qui fait que le circuit d'interfaçage devait faire la conversion. La conversion était réalisée par un circuit qui traduit des données numériques (ici, du binaire) en données analogiques : le convertisseur numérique-analogique ou DAC (Digital-to-Analogue Converter). Au tout début, le circuit d’interfaçage était un DAC combiné avec des circuits annexes, ce qu'on appelle un RAMDAC (Random Access Memory Digital-to-Analog Converter). De nos jours, les écrans comprennent le binaire sous réserve qu'il soit codé suivant le standard adapté et les cartes graphiques n'ont plus besoin de RAMDAC.

Il y a peu à dire sur les circuits d'interfaçage. Leur conception et leur fonctionnement dépendent beaucoup du standard utilisé. Sans compter qu'expliquer leur fonctionnement demande de faire de l'électronique pure et dure, ce qui est rarement agréable pour le commun des mortels. Par contre, étudier le circuit de contrôle et la mémoire vidéo est beaucoup plus intéressant. On peut donner quelques généralités particulièrement utiles sur ces deux circuits, qui forment le cœur de la carte d'affichage. Aussi, les deux sections qui suivent seront consacrées à la mémoire vidéo et au circuit de contrôle.

Le circuit de contrôle modifier

Le circuit de contrôle, aussi appelé séquenceur, s'occupe de générer les signaux à destination de l'écran. Pour cela, il doit faire deux choses : lire les pixels à envoyer à l'écran depuis la mémoire vidéo, et générer des signaux de contrôle annexes. Les signaux de contrôle sont variés, sans compter qu'ils sont émis avec des timings bien précis.

 
Architecture globale d'une carte d'affichage, avec CRTC.

L'envoi des pixels à l'écran modifier

Le câble qui relie la carte graphique à l'écran transmet au mieux un seul pixel à la fois, voire un seul bit à la fois. On ne peut pas envoyer l'image d'un seul coup à l'écran, et on doit l'envoyer pixel par pixel. L'écran traite alors ce flux de pixels de deux manières différentes. Dans le cas le plus intuitif, l'écran accumule les pixels reçus dans une mémoire tampon et affiche l'image une fois qu'elle est totalement reçue. Sinon, l'écran affiche les pixels reçus immédiatement dès leur réception sur l'entrée. Mais il faut envoyer les pixels dans un certain ordre bien précis.

 
Coordonnées d'un pixel à l'écran.

Rappelons qu'un écran est considéré par la carte graphique comme un tableau de pixels, organisé en lignes et en colonnes. Les écrans LCD sont bel et bien conçus comme cela, c'est plus compliqué sur les écrans CRT, mais cela ne change rien du point de vue de la carte graphique. Chaque pixel est localisé sur l'écran par deux coordonnées : sa position en largeur et en hauteur. Par convention, on suppose que le pixel de coordonnées (0,0) est celui situé tout haut et tout à gauche de l'écran. Le pixel de coordonnées (X,Y) est situé sur la X-ème colonne et la Y-ème ligne. Le tout est illustré ci-contre.

Le balayage progressif de l'écran modifier

La carte graphique doit envoyer les pixels dans un certain ordre, qui est généralement ligne par ligne, colonne par colonne : de haut en bas et de gauche à droite. Cet ordre d'envoi est appelé le balayage progressif. Le balayage progressif est utilisé sur tous les écrans LCD moderne, mais il était plus adapté aux écrans CRT. Sur les écrans plats, l'image transmise à l'écran est affichée une fois qu'elle est intégralement reçue, d'un seul coup. Mais sur les anciens écrans de télévision, les choses étaient différentes. Les vieux écrans CRT fonctionnaient sur ce principe : un canon à électrons balayait l'écran en commençant en haut à gauche, et balayait l'écran ligne par ligne. Ce scan progressif de l'image faisait apparaître l'image progressivement et profitait de la persistance rétinienne pour former une image fixe. L'image était donc affichée en même temps qu'elle était envoyée et le scan progressif correspondait à l'ordre d'allumage des pixels à l'écran.

 
Intérieur d'un écran CRT. En 1, on voit le canon à électron. En 2, on voit le faisceau d'électron associé à chaque couleur. En 3, les faisceaux d'électrons sont déviés par des électroaimants, pour atterrir sur le pixel à éclairer. En 4, le faisceau d'électrons frappe la surface de l'écran, composée de phosphore, qui s'illumine alors. En 5, on voit que les trois faisceaux ne frappent exactement au même endroit : l'un frappe sur une zone colorée en bleu, l'autre sur du vert, l'autre sur du rouge. Les trois zones combinées affichent une couleur par mélange du rouge, du vert et du bleu. Ne vous trompez pas : le faisceau d'électron n'a pas de couleur, comme indiqué sur le schéma, la couleur a été ajoutée pour faire comprendre qu'un faisceau est dirigé sur les pixels rouges, un autre sur les pixel bleus, et l'autre sur les pixels verts.

Avec le balayage progressif, la carte graphique envoie le pixel (0,0) en premier, puis celui situé à gauche et ainsi de suite. Quand il a fini d'envoyer la ligne de pixel, il descend et reprend à la ligne suivante, tout à gauche. L'ordre de transfert est donc assez simple : ligne par ligne, de gauche à droite. Pour gérer cet ordre de transmission, la carte graphique contient deux compteurs (des circuits qui comptent de 0 à N). Le premier compteur compte pour la coordonnée X et l'autre la coordonnée Y, ce qui leur vaut les noms de compteur de colonne et de compteur de ligne. Les deux compteurs sont initialisés à 0 avant la transmission. De plus, ils sont configurés de manière à prendre en compte la résolution de l'écran. Par exemple, pour une résolution de 640 par 480, le compteur de colonne est configuré pour compter de 0 à 639, alors que l'autre compte de 0 à 479. En clair, leur valeur maximale, celle à laquelle ils s’arrêtent de compter, est égale à la résolution horizontale/verticale. Quand un pixel est envoyé, le compteur de colonne X est incrémenté, afin de pointer sur le pixel suivant. Quand ce compteur dépasse sa valeur maximale, cela signifie qu'il faut changer de ligne. Le compteur de ligne Y est alors incrémenté, afin de passer à la ligne suivante. Quant au compteur de colonne, il est réinitialisé, remis à zéro, afin de balayer la prochaine ligne à partir de la bonne colonne. Ainsi, pour une résolution de 640 par 480, le compteur de colonne est remis à 0 quand on l'incrémente au-delà de 639 : il ne passe alors pas à 640, mais repasse à 0.

La carte graphique doit lire l'image dans la mémoire vidéo pixel par pixel dans l'ordre adéquat, pour obtenir ce balayage ligne par ligne, de gauche à droite. Cela est réalisé par des compteurs, des registres à décalage et quelques circuits. Le contenu des compteurs de ligne et de colonne est combiné avec d'autres informations, de manière à pointer sur le pixel en mémoire vidéo. En clair, l'adresse mémoire du pixel à afficher est calculée à partir de la valeur des deux compteurs, et de l'adresse du premier pixel. Mais le calcul à réaliser dépend de la manière dont l'image est codée en mémoire vidéo. En général, ce codage est des plus simple : l'image est stockée dans ce que les programmeurs appellent un tableau bi-dimensionnel. On peut récupérer un pixel en spécifiant les deux coordonnées X et Y, ce qui est l'idéal. Pour détailler un peu ce tableau bi-dimensionnel de pixels, c'est juste que les pixels consécutifs sur une ligne sont consécutifs en mémoire et les lignes consécutives sur l'écran le sont aussi dans la mémoire vidéo. En clair, il suffit de balayer la mémoire pixel par pixel en partant de l'adresse mémoire du premier pixel, jusqu’à atteindre la fin de l'image.

 
CRTC et calcul d'adresse.

L'entrelacement modifier

 
Illustration de l'entrelacement.

La technique du balayage progressif n'avait pas de défauts particuliers, ce qui fait que tous les écrans d’ordinateurs CRT l'utilisait. Mais les télévisions de l'époque utilisaient une méthode différente, appelée l'entrelacement. Avec elle, l'écran faisait un scan pour les lignes paires, suivi par un scan pour les lignes impaires. Le tout est illustré dans l'animation ci-contre.

 
Illustration de l'entrelacement et de ses effets sur la perception.

L'entrelacement donne l'illusion de doubler la fréquence d'affichage, ce qui est très utile sur les écrans à faible fréquence de rafraîchissement. Pour comprendre pourquoi, il faut comparer ce qui se passe entre un écran à scan progressif non-entrelacé et un écran entrelacé. Avec l'écran non-entrelacé, l'image met un certain temps à s'afficher, qui correspond au temps que met le canon à électron à balayer la totalité de l'écran, ligne par ligne. Avec l'entrelacement, le temps mis pour balayer l'écran est le même, car le nombre de lignes à balayer reste le même, seul l'ordre change. Par contre, sur l'écran entrelacé, l'image s'affiche à moitié une première fois (sur les lignes paires) avant que l'image complète s'affiche. La moitié d'image affichée par l'écran entrelacé a une résolution suffisante pour que le cerveau humain soit trompé et perçoive une image presque complète. En clair, le cerveau verra deux images par balayage complet : une image partielle lors du balayage des lignes paires et une image complète lors du balayage des lignes impaires. Sans entrelacement, le cerveau ne verra qu'une seule image lors de chaque balayage complet.

L'effet est d'autant plus important que la résolution verticale (le nombre de lignes) est important. De plus, l'effet est encore plus important si l'ordinateur calcule un grand nombre d'images par secondes. Par exemple, pour un écran avec une fréquence de rafraîchissement de 60 Hz et un jeu vidéo qui tourne deux fois plus vite (à 120 images par secondes, donc), l'image sur les lignes impaires sera plus récente que celle sur les lignes paires. Le cerveau humain sera sensible à cela et verra une image plus fluide (bien qu'imparfaitement fluide).

L'entrelacement est géré par le circuit de communication avec l'écran, qui s'occupe aussi de la gestion de la lecture de l'image en mémoire vidéo. L'entrelacement demande de lire l'image à afficher une ligne sur deux, donc d’accéder à la mémoire vidéo d'une certaine manière. Gérer l'entrelacement est donc un sujet qui implique l'écran mais aussi la carte d'affichage. Notamment, la lecture des pixels dans la mémoire vidéo se fait différemment. Le compteur de ligne est modifié de manière à avoir une séquence de comptage différente. Déjà, il compte deux par deux, pour sauter une ligne sur deux. De plus, quand il est réinitialisé, il est réinitialisé à une valeur qui est soit paire, soit impaire, en alternant.

Le nombre de lignes est toujours impair (normes analogiques : 625 en Europe, 525 en Amérique), ce qui fait un nombre non entier de lignes pour chacune des 2 trames (impaires et paires). Par exemple, pour 625 lignes cela fait 312,5 lignes par trame. Le balayage vertical étant progressif durant le balayage horizontal, les lignes sont imperceptiblement penchées. À la fin du balayage d'une trame, le rayon se retrouve au milieu de la ligne horizontale, soit un décalage vertical d'une demie-ligne (voir image ci-dessous).

 
Entrelacement sur tube cathodique.

La gestion des timings pour la communication avec l'écran modifier

Un autre point important est que la carte graphique ne peut pas envoyer un flux de pixels n'importe quand et doit respecter des timings bien précis. Le flux de pixel envoyé à l'écran est souvent structuré d'une certaine manière, avec des temps de pause, un temps de maintien minimum pour chaque pixel, etc.

Premièrement, l'écran doit être synchronisé avec la carte graphique. Même si cela commence à changer de nos jours, l'écran affiche un certain nombre d'images par secondes, le nombre en question étant désigné sous le terme de fréquence de rafraîchissement. Pour un écran avec une fréquence de rafraîchissement de 60 Hz (60 images par secondes), la carte graphique doit envoyer une nouvelle image tous les (1 seconde / 60) = 16,666... millisecondes. Sur les écrans LCD, la fréquence de rafraîchissement ne dépend pas de la résolution utilisée, en raison de différences de technologie. Sur les anciens écrans CRT, la fréquence de rafraîchissement dépendait de la résolution utilisée, et la carte d'affichage devait alors gérer le couple résolution-fréquence elle-même et la gestion de la fréquence de rafraîchissement était donc plus compliquée.

De plus, il faut tenir compte des timings liés à la transmission de l'image elle-même. La carte graphique doit envoyer les pixels avec des timings tout aussi stricts, qui dépendent du standard vidéo utilisé. Chaque pixel doit être maintenu durant un certain temps bien précis, il y a un certain temps entre la transmission de deux pixels, etc. Et le circuit d’interfaçage doit gérer tout cela.

Enfin, il faut aussi tenir compte d'autres timings, notamment sur les écrans CRT. Sur les écrans CRT, les pixels sont envoyés ligne par ligne et une ligne de pixel n'a pas la même taille suivant la résolution : 640 pixels pour du 640*480, 1280 pour du 1280*1024, etc. La carte graphique doit donc indiquer quand commencent et se terminent chaque ligne dans le flux de pixels. Sans cela, on ne pourrait pas gérer des résolutions différentes.

L'exemple du standard VGA modifier

Pour comprendre quels sont ces timings, prenons l'exemple de l'antique standard VGA. Avec ce standard, il existait un fil H-SYNC pour indiquer qu'on transmettait une nouvelle ligne et un fil V-SYNC pour indiquer qu'on envoie une nouvelle image. Une nouvelle ligne ou image est indiquée en mettant un 0 sur le fil adéquat. De plus, on devait attendre un certain temps entre la transmission de deux lignes, ce qui introduisait des vides dans le flux de pixels. Même chose entre deux images, sauf que le temps d'attente était plus long que le temps d'attente entre deux lignes. Le tout est détaillé dans le schéma ci-dessous, qui détaille le cas pour une résolution de 640 par 480.

 
Standard VGA : spécification des temps d'attentes entre deux lignes et deux images.

Le circuit de gestion des timings est souvent fusionné avec le circuit qui lit la mémoire vidéo, pour des raisons de simplicité de conception. Et c'est le cas avec le standard VGA. Les deux signaux H-sync et V-sync sont fournit à partir du contenu des deux compteurs de ligne et de colonne vus plus haut. Ils sont synchronisés à une fréquence bien précise, qui détermine le temps mis pour passer d'un pixel à l'autre et d'une ligne à l'autre. Le temps de transmission d'un pixel est de 25,6 µs / 640 = 0,04 µs, ce qui correspond à une fréquence de 25 MégaHertz. Le compteur de colonne est donc cadencé à 25 MHz. Les temps d'attente de 1,54 et 0,64 µs correspondent respectivement à 38 et 16 cycles du compteur. Quant à la durée de 3,8 µs du signal H-sync, elle correspond à 95 cycles. En tout, cela fait 640 + 95 + 16 + 38 = 789. Il faut donc un compteur qui compte de 0 à 788. La transmission des pixels commence quand le compteur commence à compter. Puis, le compteur continue de compter pendant 0,64 µs alors qu'aucun pixel n'est envoyé, afin de gérer le temps d'attente avant le signal H-sync. Puis, au 640 + 16e cycle, le signal H-sync est généré pendant 95 cycles. Enfin, le compteur continue de compter pendant 38 cycles pour le second temps d'attente. Le signal H-sync est donc généré quand le compteur a une valeur comprise entre 656 et 751 : il suffit d'ajouter un comparateur qui vérifie si le compteur est dans cet intervalle, et donc la sortie est à zéro si c'est le cas. La même logique s'applique avec le signal V-sync, mais avec des timings différents, illustrés plus haut.

 
Circuit de gestion des timings H-sync et V-sync d'un écran VGA.

Le framebuffer modifier

La mémoire vidéo est nécessaire pour stocker l'image à afficher à l'écran, mais aussi pour mémoriser temporairement des informations importantes. Sur les toutes premières cartes graphiques, elle servait uniquement à stocker l'image à afficher à l'écran. Le terme pour ce genre de mémoire vidéo est : Framebuffer. Au fil du temps, elle s'est vu ajouter d'autres fonctions, comme stocker les textures et les vertices de l'image à calculer, ainsi que divers résultats temporaires.

Elle est très proche des mémoires RAM qu'on trouve sous forme de barrettes dans nos PC, à quelques différences près. En premier lieu, la mémoire vidéo peut supporter un grand nombre d'accès mémoire simultanés. Ensuite, elle est optimisée pour accéder à des données proches en mémoire. Dans les grandes lignes, elle est optimisée pour avoir un débit de donnée très élevé, au détriment du temps d'accès, qui est assez moyen.

Le codage des pixels modifier

Chaque pixel est codé sur un certain nombre de bits, qui dépend du standard d'affichage utilisé. À l'époque des toutes premières cartes graphiques, les écrans étaient monochromes et ne pouvait afficher que deux couleurs : blanc ou noir. De fait, il suffisait d'un seul bit pour coder la couleur d'un pixel : 0 codait blanc, 1 codait noir (ou l'inverse, peu importe). Par la suite, les niveaux de gris furent ajoutés, ce qui demanda d'ajouter des bits en plus.

1 bit 2 bit 4 bit 8 bit
       

La technique de la palette indicée modifier

 
Palette indicée. En haut, on a le framebuffer, qui contient les couleurs codées par des nombres. La table de correspondance est donnée au milieu, et l'image finale en bas.

Avec l'apparition de la couleur, il fallut ruser pour coder les couleurs. Cela demandait d'utiliser plus de 1 bit par pixel : 2 bits permettaient de coder 4 couleurs, 3 bits codaient 8 couleurs, 4 bits codaient 16 couleurs, 8 bits codaient 256 couleurs, etc. Chaque combinaison de bit correspondait à une couleur et la carte d'affichage contenait une table de correspondance qui fait la correspondance entre un nombre et la couleur associée. Cette technique s'appelle la palette indicée, la table de correspondance s'appelant la palette.

 
Palette de l'IBM16.

Au tout début, la table de correspondance était généralement fixée une bonne fois pour toute dans la carte d'affichage, dans un circuit dédié. Mais par la suite, les cartes d'affichage permirent de modifier la table de correspondance dynamiquement. Les programmeurs pouvaient modifier son contenu, et ainsi changer la correspondance nombre-couleur à la volée. Des applications différentes pouvaient ainsi utiliser des couleurs différentes, on pouvait adapter la palette en fonction de l'image à afficher, c'était aussi utilisé pour faire des animations sans avoir à modifier la mémoire vidéo. Les applications étaient multiples. En changeant le contenu de la palette, on pouvait réaliser des gradients mobiles, ou des animations assez simples : c'est la technique du color cycling.

Exemples d'animations obtenues avec du color Cycling
       

Le standard RGB et ses dérivés modifier

 
Image codée en RGB : l'image est un mélange de trois images : une ne contenant que des nuances de rouge, une des nuances de vert, et la dernière uniquement des nuances de bleu.

La table de correspondance grandit exponentiellement avec le nombre de bits, ce qui fait qu'elle devient rapidement très grande. Au-delà de 8/12 bits, la technique de la palette n'est pas très intéressante. Ce qui fait que le codage des couleurs a dû prendre une autre direction quand la limite des 8 bits fût dépassée. L'idée pour contourner le problème est d'utiliser la synthèse additive des couleurs, que vous avez certainement vu au collège. Pour rappel, cela revient à synthétiser une couleur en mélangeant deux à trois couleurs primaires. La manière la plus simple de faire cela est de mélanger du Rouge, du Bleu, et du Vert. En appliquant cette méthode au codage des couleurs, on obtient le standard RGB (Red, Green, Blue). L'intensité du vert est codée par un nombre, idem pour le rouge et le bleu.

Autrefois, il était courant de coder un pixel sur 8 bits, soit un octet : 2 bits étaient utilisés pour coder le bleu, 3 pour le rouge et 3 pour le vert. Le fait qu'on ait choisi seulement 2 bits pour le bleu s'explique par le fait que l’œil humain est peu sensible au bleu, mais est très sensible au rouge et au vert. Nous avons du mal à voir les nuances fines de bleu, contrairement aux nuances de vert et de rouge. Donc, sacrifier un bit pour le bleu n'est pas un problème. De nos jours, l'intensité d'une couleur primaire est codée sur 8 bits, soit un octet. Il suffit donc de 3 octets, soit 24 bits, pour coder une couleur.

Une autre astuce pour économiser des bits est de se passer d'une des trois couleurs primaires, typiquement le bleu. En faisant cela, on code toutes les couleurs par un mélange de deux couleurs, le plus souvent du rouge et du vert. Vu que l’œil humain a du mal avec le bleu, c'est souvent la couleur bleu qui disparait, ce qui donne le standard RG. En faisant cela, on économise les bits qui codent le bleu : si chaque couleur primaire est codée sur un octet, deux octets suffisent au lieu de trois avec le RGB usuel.

RGB 16 bits RG 16 bits
   

L'organisation du framebuffer modifier

La taille du framebuffer limite la résolution maximale atteignable. Autant ce n'est pas du tout un problème sur les cartes graphiques actuelles, autant c'était un facteur limitant pour les toutes premières cartes d'affichage. En effet, prenons une image dont la résolution est de 640 par 480 : l'image est composée de 480 lignes, chacune contenant 640 pixels. En tout, cela fait 640 * 480 = 307200 pixels. Si chaque pixel est codé sur 32 bits, l'image prend donc 307200 * 32 = 9830400 bits, soit 1228800 octets, ce qui fait 1200 kilo-octets, plus d'un méga-octet. Si la carte d'affichage a moins d'un méga-octet de mémoire vidéo, elle ne pourra pas afficher cette résolution, sauf en trichant avec les techniques d'entrelacement. De manière générale, la mémoire prise par une image se calcule comme : nombre de pixels * taille d'un pixel, où le nombre de pixels de l’image se calcule à partir de la résolution (on multiplie la hauteur par la largeur de l'écran, les deux exprimées en pixels).

Le framebuffer peut être organisé plusieurs manières différentes, mais deux grandes méthodes se dégagent. La toute première est celui du packed framebuffer, ou encore framebuffer compact. Elle est très intuitive : les pixels sont placés les uns à côté des autres en mémoire. L'image est découpée en plusieurs lignes de pixels, deux pixels consécutifs sur une ligne sont placés à des adresses consécutives, deux lignes consécutives se suivent dans la mémoire. L'autre organisation est le planar framebuffer, aussi appelé la méthode des bitplanes. Pour la comprendre, prenons le cas où chaque pixel est codé par deux bits : l'organisation planaire va découper l'image en deux : une image qui contient seulement le premier bit pour chaque pixel, et une autre image qui contient seulement le second bit. Chaque image est codée avec un framebuffer compact. Le principe se généralise pour des pixels codés sur N bits, sauf qu'il faudra alors N images.

Disons-le clairement, la première méthode est la plus simple et la plus intuitive, alors que la seconde n'a pas d'intérêt évident. Son avantage principal est qu'elle gaspille moins de mémoire quand les pixels sont codés sur 3, 5, 6, 7, 9, 11 bits ou tout autre nombre de bits qui n'est pas une puissance de deux. Un autre avantage est que l'on peut modifier un bitplanes indépendamment des autres, ce qui permet de faire certains effets graphiques simplement. C'est leur avantage principal, mais ils ont l’inconvénient que lire un pixel est plus lent. Aussi, ils sont rarement utilisés dans les cartes d'affichage, sauf pour les très anciens modèles qui codaient leurs couleurs sur 3, 5 ou 7 bits.

Pour donner un exemple d'utilisation de planar framebuffer est l'ancien ordinateur/console de jeu Amiga Commodore. Une autre utilisation est celle faite dans le standard VGA. Dans sa résolution native de 640 par 480 en 16 couleurs, le framebuffer est de type planaire. Il y a quatre plans de 1 bit chacun, ce qui colle bien avec le fait que chaque couleur est codée sur 4 bits dans cette résolution. De plus, le framebuffer est une mémoire de 256 kibioctets, divisé en 4 banques de 64 kibioctets chacun. Les quatre banques sont accessibles en parallèles, ce qui permet de lire 4 bits en même temps. La raison derrière ce système est avant tout la compatibilité avec le standard d'avant le VGA, l'EGA, qui avait une mémoire limitée à 64 kibioctets.

Le multibuffering et la synchronisation verticale modifier

Sur les toutes premières cartes graphiques, le framebuffer ne pouvait contenir qu'une seule image. L'ordinateur écrivait donc une image dans le framebuffer et celle-ci était envoyée à l'écran dès que possible. Cependant, écran et ordinateur n'étaient pas forcément synchronisés. Rien n’empêchait à l’ordinateur d'écrire dans le framebuffer pendant que l'image était envoyée à l'écran. Et cela peut causer des artefacts qui se voient à l'écran.

Un exemple typique est celui des traitements de texte. Lorsque le texte affiché est modifié, le traitement de texte efface l'image dans le framebuffer et recalcule la nouvelle image à afficher. Ce faisant, une image blanche peut apparaitre durant quelques millisecondes à l'écran, entre le moment où l'image précédente est effacée et le moment où la nouvelle image est disponible. Ce phénomène de flickering; d'artefacts liés à une modification de l'image pendant qu'elle est affichée, est des plus désagréables.

Le double buffering modifier

Pour éviter cela, on peut utiliser la technique du double buffering. L'idée derrière cette technique est de calculer une image en avance et de les mémoriser celle-ci dans le framebuffer. Mais cela demande que le framebuffer ait une taille suffisante, qu'il puisse mémoriser plusieurs images sans problèmes. Le framebuffer est alors divisé en deux portions, une par image, auxquelles nous donnerons le nom de tampons d'affichage. L'idée est de mémoriser l'image qui s'affiche à l'écran dans le premier tampon d'affichage et une image en cours de calcul dans le second. Le tampon pour l'image affichée s'appelle le tampon avant, ou encore le front buffer, alors que celui avec l'image en cours de calcul s'appelle le back buffer.

 
Double buffering

Quand l'image dans le back-buffer est complète, elle est copiée dans le front buffer pour être affichée. L'ancienne image dans le front buffer est donc éliminée au profit de la nouvelle image. Le remplacement peut se faire par une copie réelle, l'image étant copiée le premier tampon vers le second, ce qui est une opération très lente. C'est ce qui est fait quand le remplacement est réalisé par le logiciel, et non par la carte graphique elle-même. Par exemple, c'est ce qui se passait sur les très anciennes versions de Windows, pour afficher le bureau et l'interface graphique du système d'exploitation. Mais une solution plus simple consiste à intervertir les deux tampons, le back buffer devenant le front buffer et réciproquement. Une telle interversion fait qu'on a pas besoin de copier les données de l'image. L'interversion des deux tampons peut se faire au niveau matériel. Rappelez-vous que plus haut, nous avions vu qu'il y a un circuit qui détermine l'adresse du pixel à lire à partir de deux compteurs X et Y, ainsi que de l'adresse du premier pixel. L'adresse du premier pixel n'est autre que l'adresse à laquelle commence le front buffer. En changeant cette adresse pour la faire pointer vers l'ancien back buffer, l’interversion se fait automatiquement.

 
Circuit de contrôle et double buffering

La synchronisation verticale modifier

Lors de l'interversion des deux tampons, le remplacement de la première image par la seconde est très rapide. Et il peut avoir lieu pendant que l'écran affiche la première image. L'image affichée à l'écran est alors composée d'un morceau de la première image en haut, et de la seconde image en dessous. Cela produit un défaut d'affichage appelé le tearing. Plus votre ordinateur calcule d'images par secondes, plus le phénomène est exacerbé.

 
Tearing (simulé)

Pour éviter ça, on peut utiliser la synchronisation verticale, aussi appelée vsync, dont vous en avez peut-être déjà entendu parler. C'est une option présente dans les options de nombreux jeux vidéo, ainsi que dans les réglages du pilote de la carte graphique. Elle consiste à attendre que l'image dans le front buffer soit entièrement affichée avant de faire le remplacement. La synchronisation verticale fait disparaitre le tearing, mais elle a de nombreux défauts, qui s'expliquent pour deux raisons que nous allons aboder.

Rappelons que l'écran affiche une nouvelle image à intervalles réguliers. L'écran affiche un certain nombre d'images par secondes, le nombre en question étant désigné sous le terme de "fréquence de rafraîchissement". La fréquence de rafraichissement est fixe, elle est gérée par un signal périodique dans l'écran. Par contre, sans Vsync, le nombre de FPS n'est pas limité, sauf si on a activé un limiteur de FPS dans les options d'un jeu vidéo ou dans les options du driver. Avec Vsync, le nombre de FPS est limité par la fréquence de l'écran. Par exemple, si vous avez un écran 60 Hz (sa fréquence de rafraichissement est de 60 Hertz), vous ne pourrez pas dépasser les 60 FPS. Vous pourrez avoir moins, cependant, si l'ordinateur ne peut pas sortir 60 images par secondes sans problème. Un autre défaut de la Vsync est donc qu'il faut un PC assez puissant pour calculer assez de FPS.

Par contre, même avec la vsync activée, l'écran n'est pas parfaitement synchronisé avec la carte graphique. Pour comprendre pourquoi, nous allons faire une analogie avec une situation de la vie courante. Imaginez deux horloges, qui sonnent toutes les deux à midi. Les deux ont la même fréquence, à savoir qu'elles sonnent une fois toutes les 24 heures. Maintenant, cela ne signifie pas qu'elles sont synchronisées. Imaginez qu'une horloge indique 1 heure du matin pendant que l'autre indique minuit : les deux horloges sont désynchronisées, alors qu'elles ont la même fréquence. Il y a un décalage entre les deux horloges, un déphasage.

Eh bien la même chose a lieu, avec la vsync. La vsync égalise deux fréquences : la fréquence de l'écran et les FPS (la fréquence de génération d'image par la carte graphique). Par contre, les deux fréquences sont généralement déphasées, il y a un délai entre le moment où la carte graphique a rendu une image, et le moment où l'écran affiche une image. Cela n'a l'air de rien, mais cela peut se ressentir. D'où l'impression qu'ont certains joueurs de jeux vidéo que leur souris est plus lente quand ils jouent avec la synchronisation verticale activée. Le temps d'attente lié à la synchronisation verticale dépend du nombre d'images par secondes. Pour un écran qui affiche maximum 60 images par seconde, le délai ajouté par la synchronisation verticale est au maximum de 1 seconde/60 = 16.666... millisecondes.

Un autre défaut est que la synchronisation verticale entraîne des différences de timings perceptibles. Le phénomène se manifeste avec les vidéos/films encodés à 24 images par secondes qui s'affichent sur un écran à 60 Hz : l'écran affiche une image tous les 16.6666... millisecondes, alors que la vidéo veut afficher une image toutes les 41,666... millisecondes. Or, 16.666... et 41.666... n'ont pas de diviseur entier commun : une image de film d'affiche tous les 2,5 images d'écran. Concrètement, écran et film sont désynchronisés. Si cela ne pose pas trop de problèmes sans la synchronisation verticale, cela en pose avec. Une image sur deux est décalée en termes de timings avec la synchronisation verticale, ce qui donne un effet bizarre, bien que léger, lors du visionnage sur un écran d'ordinateur. Le même problème survient dans les jeux vidéos, qui ont un nombre d'images par seconde très variable. Ces différences de timings entraînent des sauts d'images quand un jeu vidéo calcule moins d'images par seconde que ce que peut accepter l'écran, ce qui donne une impression désagréable appelée le stuttering.

Pour résumer, les problèmes de la vsync sont liés à deux choses : le nombre de FPS n'est pas nécessairement synchronisé avec le rafraichissement de l'écran, et le déphasage entre ordinateur et écran se ressent.

Le triple buffering et ses dérivés modifier

Diverses solutions existent pour éliminer ces problèmes, et elles sont assez nombreuses. La première solution ajoute un troisième tampon d'affichage, ce qui donne la technique du triple buffering. L'utilité est de réduire le délai ajouté par la synchronisation verticale : utiliser le triple buffering sans synchronisation verticale n'a aucun sens. L'idée est que l'ordinateur peut calculer une seconde image d'avance. Ainsi, si l'écran affiche l'image n°1, une image n°2 est terminée mais en attente, et une image n°3 est en cours de calcul.

 
Triple buffering

Le délai lié à la synchronisation verticale est réduit dans le cas où les FPS sont vraiment bas comparé à la fréquence d'affichage de l'écran, par exemple si on tourne à 40 images par secondes sur un écran à 60 Hz, du fait de l'image calculée en avance. Dans le cas où les FPS sont (temporairement) plus élevés que la fréquence d'affichage de l'écran, la troisième image finit son calcul avant que la seconde soit affichée. Dans ce cas, la seconde image est affichée avant la troisième. Il n'y a pas d'image supprimée ou abandonnée, peu importe la situation.

Les améliorations de la synchronisation verticale modifier

La technologie Fast Sync sur les cartes graphiques NVIDIA est une amélioration du triple buffering, qui se préoccupe du cas où les FPS sont (temporairement) plus élevés que la fréquence d'affichage de l'écran. Dans ce cas, avec le triple buffering simple, aucune image n'est abandonnée : on a deux images en attente, dont l'une est plus récente que l'autre. La technologie fast sync élimine la première image en attente et de la remplacer par la seconde, plus récente. L'avantage est que le délai d'affichage d'une image est réduit, le temps d'attente lié à la synchronisation verticale étant réduit au strict minimum.

Une autre solution est la synchronisation verticale adaptative, qui consiste à désactiver la synchronisation verticale quand le nombre d'images par seconde descend sous la fréquence de rafraîchissement de l'écran. Le principe est simple, mais il s'agit pourtant d'une technologie assez récente, introduite en 2016 sur les cartes NVIDIA. Notons qu'on peut combiner cette technologie avec la technologie fast sync : cette dernière fonctionne quand les FPS dépassent la fréquence de rafraîchissement de l'écran, alors que la vsync adaptative fonctionne quand les FPS sont trop bas. C'est utile si les FPS sont très variables.

Une dernière possibilité est d'utiliser des technologies qui permettent à l'écran et la carte graphique d'utiliser une fréquence de rafraîchissement variable. La fréquence de rafraîchissement de l'écran s'adapte en temps réel à celle de la carte graphique. En clair, l'écran démarre l'affichage d'une nouvelle image quand la carte graphique le lui demande, pas à intervalle régulier. Évidemment, l'écran a une limite physique et ne peut pas toujours suivre la carte graphique. Dans ce cas, la carte graphique limite les FPS au maximum de ce que peut l'écran. Les premières technologies de ce type étaient le Gsync de NVIDIA et le Free Sync d'AMD, qui ont été suivies par les standards AdaptiveSync et MediaSync.

L'historique des cartes d'affichage : mode texte et mode graphique modifier

Fort des explications précédentes, il est temps de faire un rapide aperçu de l'histoire des cartes d'affichage. Les toutes premières cartes d'affichage portaient le nom de cartes d'affichage en mode texte. Elles ont été suivies par les cartes d'affichage en mode graphique. Paradoxalement, les cartes d'affichage en mode texte sont de loin les moins intuitives et elles sont plus complexes que les cartes d'affichage en mode graphique. Les limitations de la technologie de l'époque rendaient plus adaptées les cartes en mode texte, notamment les limitations en terme de mémoire vidéo. La faible taille de la mémoire rendait impossible l'usage d'un framebuffer proprement dit, ce qui fait que les ingénieurs ont utilisé un moyen de contournement, qui a donné naissance aux cartes graphiques en mode texte. Par la suite, avec l'amélioration de la technologie des mémoires, les cartes d'affichage avec un framebuffer sont apparues et ont remplacé les complexes cartes d'affichage en mode texte.

Les cartes d'affichage en mode texte modifier

 
Illustration du mode texte.

Les premières cartes graphiques fonctionnaient en mode texte, c'est à dire qu'elles traitaient des caractères et non des pixels. L'écran était découpé en carré ou en rectangles de taille fixe, contenant chacun un caractère. Les caractères affichables sont des lettres, des chiffres, ou des symboles courants, même si des caractères spéciaux sont disponibles. La carte d'affichage traitait les caractère comme un tout et il était impossible de modifier des pixels individuellement. Ceux-ci sont encodés dans un jeu de caractère spécifique (ASCII, ISO-8859, etc.), qui est généralement l'ASCII.

 
Ce mode texte est toujours présent dans nos cartes graphiques actuelles et est encore utilisé par le BIOS, ce qui lui donne cet aspect désuet et moche des plus inimitable.

Tous les caractères sont des images de taille fixe, que ce soit en largeur ou en hauteur. Par exemple, un caractère peut faire 8 pixels de haut et 8 pixels de large, sur les écrans cathodiques. Sur les écrans LCD, les pixels sont carrés et les caractères font 16 pixels de haut par 8 de large pour avoir un aspect rectangulaire. Les images de chaque caractère sont mémorisées dans une mémoire : la table des caractères. Certaines cartes graphiques permettent à l'utilisateur de créer ses propres caractères en modifiant cette table. La mémoire de caractères est une mémoire ROM/EEPROM. Dans cette mémoire, chaque caractère est représenté par une matrice de pixels, avec un bit par pixel. On pourrait croire que la table des caractère telle que si l'on envoie le code ASCII sur l'entrée d'adresse, on récupère en sortie l'image du caractère associé. Mais cette solution simple est irréaliste : un simple caractère monochrome de 8 pixels de large et de 8 pixels de haut demanderait près de 64 bits en sortie, ce qui est impraticable, surtout pour les mémoires de l'époque. En réalité, la mémoire de caractère a une sortie de 1 bit, le bit en question étant pris dans l'image du caractère sélectionné. L'entrée d'adresse s'obtient alors en concaténant trois informations : le code ASCII pour sélectionner le caractère, le numéro de la ligne et le numéro de la colonne pour sélectionner le bit dans l'image du caractère.

 
Représentation d'un caractère à l'écran et dans la table de caractère.

Le tampon de texte (text buffer) est une mémoire dans laquelle les caractères à afficher sont placés les uns à la suite des autres. Le processeur envoie les caractères à afficher un par un, ceux-ci étant accumulés dans le tampon de texte au fur et à mesure de leur arrivée. Chaque caractère est stocké en mémoire avec deux octets : un octet pour le code ASCII, suivi d'un octet pour les attributs. Les attributs des caractères sont des informations qui indiquent si le caractère clignote, sa couleur, sa luminosité, si le caractère doit être souligné, etc. Une gestion minimale de la couleur est parfois présente. Le tout est mémorisé dans un octet, à la suite de leur code ASCII. L'avantage du mode texte est qu'il utilise très peu de mémoire vidéo : on ne code que les caractères et non des pixels indépendants, et un caractère correspond à beaucoup de pixels. La carte graphique contient un circuit chargé de gérer ces attributs des caractères : l'ATC (Attribute Controller), aussi appelé le contrôleur d'attributs. Il est situé juste en aval du tampon de texte

Vient ensuite le CRTC (Cathod Ray Tube Controller) ou contrôleur de tube cathodique, qui gère l'affichage sur l'écran proprement dit. Sur les vieux écrans CRT, les pixels sont affichés les uns après les autres, ligne par ligne, en commençant par le pixel en haut à gauche. Pour cela, il se souvient du prochain pixel à afficher grâce à deux registres : un registre Y pour la ligne, et un registre X pour la colonne. Évidemment, les deux registres sont incrémentés régulièrement afin de passer au pixel suivant et repassent à zéro quand on les incrémente au-delà de leur valeur maximale.

Mais sur une carte en mode texte, on n'a pas accès directement aux pixels en mémoire vidéo. À la place, on doit sélectionner le caractère à afficher, puis sélectionner le pixel dans celui-ci, ce qui demande la collaboration de la mémoire de caractères et le tampon de texte. Le CRTC va déduire à quel caractère correspond le pixel choisit, et le récupérer dans le tampon de texte. Là, le code du caractère est envoyé à la mémoire de caractère, avec quelques informations pour sélectionner le numéro de ligne et le numéro de colonne. le pixel récupéré dans la mémoire de caractère est alors envoyé à l'écran. Concrètement, les calcul à faire pour déterminer le caractère et pour trouver les numéros de ligne/colonne sont très simples, sans compter que les deux sont liés. Prenons par exemple un écran dont les caractère font tous 12 pixels de large et 8 pixels de haut. Le pixel de coordonnées X (largeur) et Y (hauteur) correspond au caractère de position X/12 et Y/8. Le reste de la première division donne la position de la colonne pour la mémoire de caractère, alors que le reste de la seconde division donne le numéro de ligne.

Si les caractères ont une largeur et une hauteur qui sont des puissances de deux, les divisions se simplifient : la position du caractère dans la mémoire se calcule alors à partir des bits de poids forts des compteurs X et Y, alors que les bits de poids faible permettent de donner le numéro de ligne et de colonne pour la mémoire de caractère. Dans le diagramme ci-dessus, les 3 bits de poids faible des registres X et Y de balayage des pixels servent à adresser le pixel parmi les 8x8 du bloc correspondant au caractère à afficher. L'index de ce caractère est lu dans le tampon de texte, adressé par les bits restant des registres X et Y.

 
Balayage des pixels par le CRTC pour un mode texte avec des caractères de 8x8 pixels.

Enfin, le pixel à afficher est envoyé à l'écran. Les écrans assez anciens fonctionnent en analogiques et non en numérique, ce qui fait que les cartes d'affichage de l'époque contenaient un DAC (Digital-Analogic Converter) pour convertir les données binaires en données analogiques (courant appliqué à chaque canon à électron pour le bleu, le vert et le rouge).

 
Architecture interne d'une carte d'affichage en mode texte

Les cartes d'affichage en mode graphique modifier

Les cartes en mode texte ont rapidement été remplacées par des cartes graphiques capables de colorier chaque pixel de l'écran individuellement. Il s'agit d'une avancée énorme, qui permet beaucoup plus de flexibilité dans l'affichage. Ces cartes graphiques étaient conçues avec des composants similaires aux composants des cartes graphiques à mode texte, mais avec quelques simplifications. Premièrement, la mémoire de caractères disparait (ou n'est pas utilisée en mode graphique). Ensuite, la mémoire vidéo est remplacée par un framebuffer, qui mémorise des pixels et non des caractères. Ensuite, le CRTC est modifié pour balayer le framebuffer ligne par ligne, pixel par pixel. Il peut gérer différentes résolutions, quelques registres permettant de configurer la résolution voulue.

La couleur fait son apparition, grâce à la technique de la palette indicée. Les couleurs étaient codées sur 4 bits, 8 bits, à la rigueur 16, alors que les couleurs acceptées par l'écran étaient au format RGB. La table de correspondance utilisée pour traduire les couleurs au format RGB s'appelait la Color Look-up Table. Dans les cas plus simples, il s'agissait d'une ROM qui mémorisait la couleur RGB pour chaque numéro envoyé en adresse. Dans les cas les plus courants, cette mémoire était une RAM, ce qui permettait de changer la palette d'une application à l'autre sans aucun problème, comme on l'a vu plus haut. La Color Look-up Table était parfois fusionnée avec le DAC, et formait ce qu'on appelait le RAMDAC.

Le cas le plus simple est celui des premiers modes CGA où 4 bits étaient utilisés pour indiquer la couleur : 1 bit pour chaque composante rouge, verte et bleue et 1 bit pour indiquer l'intensité (sombre / clair).

 
Architecture interne d'une carte d'affichage en mode graphique