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'affichageModifier

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ôleModifier

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'écranModifier

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'écranModifier

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 apparaitre 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 atterir 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 rouge, un autre sur les pixel bleus, et l'autre sur les pixels vert.

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 finit 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 compte 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. Quand 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'entrelacementModifier

 
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 rafraichissement. 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 rafraichissement 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.

La gestion des timings pour la communication avec l'écranModifier

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 rafraichissement". Pour un écran avec une fréquence de rafraichissement 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 rafraichissement 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 rafraichissement 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 rafraichissement é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 VGAModifier

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 + 16 ème 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 enctre 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 framebufferModifier

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 pixelsModifier

Chaque pixel est codé sur un certain nombre de bits, qui dépend du standard d'affichage utilisé. A 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
       
 
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.

La technique de la palette indicéeModifier

Avec l'apparition de la couleur, il fallu 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
 
       
 
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.

Le standard RGB et ses dérivésModifier

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 choisit 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 framebufferModifier

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é 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 verticaleModifier

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 bufferingModifier

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, auquelles 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. 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, sur les vielles cartes graphiques, 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, ce qui est une opération très lente. 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 verticaleModifier

Lors de l'interversion des deux tampons, on peut faire face à deux situations, suivant que le remplacement des deux images est synchronisée avec l'écran, ou non.

  • Dans le premier cas, l'image calculée remplace immédiatement l'image précédente. Cela permet d'afficher au plus vite la seconde image, mais peut entrainer des artefacts d'affichage, comme on le verra plus bas.
  • Dans le second cas, on attend que la première image soit affichée avant de faire le remplacement.

Dans le premier cas, le remplacement de la première image par la seconde est souvent très rapide, ce qui fait qu'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é)

La seconde méthode attend que la première image soit affichée avant de faire le remplacement. Elle porte le nom de synchronisation verticale, aussi appelée vsync, et vous en avez peut-être déjà entendu parler. C'est une option présente dans les options de nombreux jeux vidéos, ainsi que dans les réglages du pilote de la carte graphique. Avec cette technique, le tearing disparait tout simplement. Mais le défaut de la vsync est qu'elle impose un temps d'attente avant de remplacer l'image affichée. Le temps d'attente lié à la synchronisation verticale est d'autant plus grand que l'écran affiche peu d'images par secondes. Pour un écran qui affiche 60 images par seconde maximum, le délai ajouté par la synchronisation verticale est d'environ 1 seconde/60 = 16.666... millisecondes. Cela n'a l'air de rien, mais cela peut se ressentir. D'où l'impression qu'ont certains joueurs de jeux vidéos que leur souris est plus lente quand ils jouent avec la synchronisation verticale activée. Et encore, cela suppose que l'ordinateur peut sortir 60 images par secondes sans problèmes.

Pour comprendre les problèmes liés à la synchronisation verticale, rappelons que l'écran affiche une nouvelle image à intervalle régulier. Concrètement, l'écran affiche un certain nombre d'images par secondes, le nombre en question étant désigné sous le terme de "fréquence de rafraichissement". Or, la fréquence de rafraichissement de l'écran et le nombre d'images par secondes de l'ordinateur ne sont pas synchronisés l'un avec l'autre. La synchronisation verticale synchronise la fréquence de rafraichissement avec les FPS, ce qui réduit le tearing et élimine d'autres problèmes. Du moins,c 'est le cas à condition que l'ordinateur fournisse suffisamment l'images par secondes. Si l'écran a une fréquence de rafraichissement de 60 Hz (60 images par secondes), tout va bien tant que l'ordinateur fournit effectivement 60 images par secondes à l'écran, voire plus. Mais si le nombre d'images par secondes n'est pas raccord avec le nombre d'images par seconde affiché par l'écran, la synchronisation verticale entraine des différences de timings perceptibles.

Cela se voit avec les jeux vidéos, qui ont un nombre d'image par seconde très variable, et le temps qui s'écoule entre deux images varie grandement d'une image à l'autre. Or, le temps entre l'affichage de deux images est fixe avec la vsync activée. Ces différences de timings entrainent 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. Un phénoméne très similaire apparait 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.

Les corrections apportées à la synchronisation verticale : le triple buffering et ses dérivésModifier

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.

 
Comparison entre double et triple buffering. Video memory est ici utilisé comme synonyme de front buffer, tandis que le back buffer est désigné par le terme buffer. Draw correspond au temps de calcul de l'image, clear à l'effacement du tampon, et copy à la copie des tampons d'affichage. Le vertical retrace est le moment où l'image a été envoyée à l'écran et peut être effacée.
Les cas 1,2 et 3 se font sans Vsync, alors que les cas 4 et 5 sont avec Vsync (c'est ce que veut dire B frame delayed).

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 rafraichissement 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 rafraichissement 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 rafraichissement variable. La fréquence de rafraichissement 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.