Les cartes graphiques/Le mode texte et le rendu en tiles
Les toutes premières cartes d'affichage portaient le nom de cartes d'affichage en mode texte. Comme leur nom l'indique, elles sont spécifiquement conçues pour afficher du texte, pas des images. Elles étaient utilisées sur les ordinateurs personnels ou professionnels, qui n'avaient pas besoin d'afficher des graphismes, seulement des lignes de commandes, tableurs, traitements de textes rudimentaires, etc. Elles ont rapidement été remplacées par des cartes graphiques avec un framebuffer. Il s'agit d'une avancée énorme, qui permet beaucoup plus de flexibilité dans l'affichage.
L'existence de telles cartes en mode texte tient au fait que la mémoire vidéo était chère et qu'on ne pouvait pas en mettre beaucoup dans une carte d'affichage ou dans une console de jeu. Aussi, les fabricants de cartes graphiques devaient ruser. La petitesse de la mémoire faisait qu'il n'y avait pas de framebuffer proprement dit. On a vu au chapitre précédent qu'il existe des techniques de rendu 2D qui se passent de framebuffer, hé bien les premières cartes d'affichage les utilisaient sous une forme détournée.
Le rendu en mode texte
[modifier | modifier le wikicode]En mode texte, 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ères 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.

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 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 du code ASCII du caractère.
Le 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 inimitables.
L'architecture d'une carte d'affichage en mode texte
[modifier | modifier le wikicode]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 termes 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 et avec framebuffer ont une architecture assez similaire. Pour rappel, une carte graphique avec un framebuffer est composée de plusieurs composants : un circuit d’interfaçage avec le bus, un circuit de contrôle appelé le CRTC, une mémoire vidéo, un DAC qui convertit les pixels en signal analogique, et quelques circuits annexes.

Une carte en mode texte a les mêmes composants, avec quelques modifications. Le tampon de texte (text buffer) est la mémoire vidéo. Dans celle-ci, les caractères à afficher sont placés les uns à la suite des autres. Chaque caractère est stocké en mémoire avec deux octets : un octet pour le code ASCII, suivi d'un octet pour les attributs. 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.
Le CRTC est modifié de manière tenir compte de l'organisation du tampon de texte. De plus, quelques circuits sont ensuite utilisés pour faire la conversion texte-image, comme on le verra plus bas. Le circuit de conversion texte-pixel est une petite mémoire ROM appelée la mémoire de caractère, qui mémorise, pour chaque caractère, sa représentation sous forme de pixels. La carte graphique contient aussi un circuit chargé de gérer les 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.

La table des caractères
[modifier | modifier le wikicode]Les images de chaque caractère sont mémorisées dans une mémoire : la table des caractères, aussi appelée mémoire des caractères dans les schémas au-dessus. Dans cette mémoire, chaque caractère est représenté par une matrice de pixels, avec un bit par pixel. Certaines cartes graphiques permettent à l'utilisateur de créer ses propres caractères en modifiant cette table, ce qui en fait une mémoire ROM/EEPROM ou RAM.
On pourrait croire que la table des caractères 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 pixels en sortie, soit facilement plusieurs centaines de bits, 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 pixel, le pixel 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. Les deux numéros sont fournis par le CRTC, comme on le verra plus bas.

Le CRTC sur une carte en mode texte
[modifier | modifier le wikicode]Le mode texte impose de modifier le CRTC de manière à ce qu'il adresse le tampon de texte correctement. Il contient toujours deux compteurs, pour localiser la ligne et la colonne du pixel à afficher, mais doit transformer cela en adresse de caractère. Le CRTC 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, et le CRTC fournit de quoi 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 calculs à 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ères 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.


Un exemple de CRTC est le Motorola 6845. Ce VDP génère l'adresse à lire dans la mémoire vidéo, ainsi que les signaux de synchronisation horizontale et verticale, mais ne fait pas grand-chose d'autre. Lire la mémoire vidéo, extraire les pixels et envoyer le tout à l'écran n'est pas de son ressort.
Il gère le mode texte uniquement, mais on peut supporter le mode graphique en trichant. Il supporte le mode entrelacé et le mode non-entrelacé, et est compatible aussi bien avec les moniteurs en PAL qu'en NTSC. Il contient 18 registres dont le contenu permet de configurer le VDP, pour configurer la résolution, la fréquence d'affichage, et d'autres choses encore.

D'autres CRTC plus évolués gèrent à la fois le mode texte et le mode graphique, on peut les configurer de manière à choisir lequel utiliser. Il est ainsi possible de prendre un VDP CRTC pour le mettre avec une mémoire assez petite pour gérer uniquement des graphiques en mode texte. Ou au contraire, de prendre un VDP CRTC et de le combiner avec une mémoire importante pour l'utiliser comme framebuffer.
Le défilement du texte
[modifier | modifier le wikicode]Faire défiler du texte est une opération très courante. Concrètement, cela fait descendre d'une ou plusieurs lignes dans le texte, ce qui demande de bouger tout le texte à l'écran. Le défilement ne se fait pas ligne de pixel par ligne de pixel, mais ligne par ligne, voire par paquets de plusieurs lignes. Par exemple, si un caractère de texte fait 8 pixels de haut, alors on saute les lignes par paquets de 8.
Il s'agit presque toujours de défilement vertical. Le texte est généralement défilé de haut en bas, verticalement, le défilement horizontal étant plus rare. Même sur les écrans de l'époque, qui avaient des colonnes limitées à 80/100 caractères, le texte était conçu de manière à ce que les lignes de texte ne débordent pas de l'écran. Aussi, les optimisations qui nous intéressent sont surtout les optimisations du défilement vertical.
Défiler du texte est une opération très courante qui gagne à être optimisé au niveau de la carte graphique. Les toutes premières cartes graphiques MBA (monochromes) et CGA n'incorporaient pas de défilement vertical optimisé, mais les cartes suivantes, EGA et VGA, le faisaient. Les optimisations en question sont nombreuses, mais nous allons en voir deux.
La première optimisation que nous allons voir réutilise les techniques vues dans le chapitre sur le rendu 2D. Pour cela, il utilise un tampon de texte organisé en lignes de texte consécutives. Le tampon de texte mémorise plus de lignes que ce qui est affiché à l'écran. Ce qui est visible à l'écran est une portion du tampon de texte, appelée le viewport. Ainsi, les lignes à afficher au-dessus ou en dessous du viewport sont déjà en mémoire vidéo. Il y a juste à fournir un registre qui pointe vers la position du viewport dans le tampon de texte, ce qui permet de faire bouger le viewport à volonté.
Une autre solution, plus économe en RAM, fait usage d'un VDC à co-processeur. Pour rappel, ce sont des VDC qui incorporent un processeur qui exécute un programme d'affichage. Le programme, appelé la display list, afficher une image à l'écran, ou du texte, ou tout ce qu'il faut afficher. Chaque instruction de la display list dit quoi afficher sur une ligne à l'écran. Ici, elle dit quoi afficher sur une ligne de texte, une ligne de caractère, et non une ligne de pixels comme dans les chapitres précédents.
Un exemple de VDC à co-processeur en mode texte est celui des ordinateurs Amstrad PCW. Il s'agissait d'ordinateurs qui ne géraient que le mode texte, ils ne pouvaient pas afficher d'image pixel par pixel, ils n'avaient pas de framebuffer. Le texte à afficher à l'écran n'était pas stocké dans un tableau unique, ligne par ligne, dans l'ordre de rendu, comme c'est le cas sur les autres cartes en mode texte. A la place, chaque ligne était stocké en mémoire séparément les unes des autres, ou presque.
L'affichage était gouverné par une display list qui disait quelle ligne afficher, et dans quel ordre. La display list était une liste d'adresses, chacun pointant vers le début d'une ligne en mémoire vidéo. Toutes les lignes faisaient la même taille, ce qui fait que la display list avait juste à encoder l'adresse de départ de la ligne pour l'afficher correctement. Le processeur du VDC lisait la display list adresse par adresse et rendait les lignes dans l'ordre précisé par la display list.
Le défilement était alors simple à implémenter : il suffisait de modifier le contenu de la display list. Par exemple, pour défiler d'une ligne vers le cas, on décalait son contenu de la display list d'un cran et on modifiait la ligne la plus en haut. L'avantage est que modifier une display list est plus rapide que de faire défiler l'ensemble du text buffer.
La display list était stockée dans une mémoire RAM spécialisée de 512 octets, ce qui permettait de stocker 256 adresses de 16 bits chacune. La display list avait 256 lignes, ce qui collait exactement à la résolution de 720 par 256 pixels de l'Amstrad PCW. LA RAM qui mémorisait la display list s'appelait la roller RAM. Elle était utilisée par le processeur 280 de la machine pour gérer le rendu de l'affichage. Le VDC utilisé était en effet très simple et se résumait sans doute à un vulgaire CRTC en mode texte.
Le rendu en mode semi-graphique
[modifier | modifier le wikicode]
Le rendu en mode texte ne permet en théorie de n'afficher que du texte. Cependant, il est possible d'émuler un rendu graphique à partir du mode texte, en trichant un petit peu. On a alors un mode de rendu appelé mode semi-graphique. Il y en a deux sous-types, appelés rendu graphique en bloc et pseudo-graphique, qui seront détaillés ci-dessous. Les mode semi-graphiques sont en soi des techniques logicielles, qui ne demandent pas de support particulier de la part du matériel. Ils servent cependant d'introduction propédeutique au rendu en motifs qui sera vu à la fin du chapitre.
La triche n'est possible que sur les cartes en mode texte sur lesquelles les caractères sont configurables, c’est-à-dire qu'on peut préciser à quoi ressemblent les caractères. Sur de telles cartes en mode texte, on peut fournir un dessin rectangulaire de quelques pixels de côté à la carte graphique et lui dire : ceci est le caractère numéro 40. L'idée est de remplacer les caractères par des dessins basiques, des motifs, qui sont assemblés pour fabriquer des "sprites", qui sont eux-même assemblés pour former l'image finale.
Le rendu à motif (tiles)
[modifier | modifier le wikicode]Le rendu en motifs est un proche cousin du mode texte. Il n'est cependant pas spécialisé pour du texte, mais permet de compresser des images complètes. Il a été utilisé sur des consoles de jeu, afin d'outrepasser les contraintes en mémoire RAM. les consoles 8 bits avaient peu de mémoire et ne pouvaient pas utiliser de framebuffer, même en utilisant la technique de la palette indicée. Elles ne pouvaient pas non plus utiliser de tampon de ligne, car le processeur n'était pas assez puissant pour. Alors, elles utilisaient le rendu en motifs pour rendre des images avec peu de mémoire vidéo.
Le stockage des sprites et de l’arrière-plan : les tiles
[modifier | modifier le wikicode]
Le rendu en motifs force une certaine redondance à l'intérieur de l'arrière-plan et des sprites. L'idée est que les sprites et l'arrière-plan sont fabriqués à partir de motifs, aussi appelés tiles. Concrètement, ce sont des dessins carrés de 8, 16, 32 pixels de côtés, qui sont assemblés pour fabriquer un sprite ou l'arrière-plan.
L'ensemble des motifs est mémorisée dans un fichier unique, appelé le tile set, le tilemap, ou encore la table des motifs. La table des motifs est placée dans la cartouche de jeu, souvent dans une mémoire ROM dédiée. Ci-contre, vous voyez la table des motifs du jeu Ultima VI.
Les motifs sont numérotés, un numéro identifiant un motif parmi toutes les autres. L'image ne mémorise pas des pixels, mais des numéros de motifs. Le gain est assez appréciable : avec des motifs de 8 pixels de côté, au lieu de stocker X * Y pixels, on stocke X/8 * Y/8 numéros de motifs par image. La mémoire vidéo n'est donc pas un framebuffer, mais un tampon de motifs. L'image à afficher à l'écran est reconstituée par la carte graphique, lors de l'affichage, en plusieurs étapes.
Un avantage est que les motifs peuvent être utilisés en plusieurs endroits, ce qui garantit une certaine redondance. L'arrière-plan, qui est généralement l'image la plus redondante. Par exemple, un ciel est composé de motifs bleus identiques. Idem quand il faut rendre plusieurs petits ennemis identiques à l'écran : on n'utilise qu'un seul motif pour tous les ennemis. Pareil si un motifs est utilisé dans plusieurs sprites, il n'est stockée qu'une seule fois. Il s'agit donc d'une forme de compression d'image qui profite d'une certaine redondance.
Ajoutons à cela que les numéros de motifs prennent moins de place que la couleur d'un pixel, et les gains sont encore meilleurs. La consommation mémoire est déportée de l'image vers la mémoire qui stocke les motifs, une mémoire ROM intégrée dans la cartouche de jeu. .
L'architecture d'une carte d'affichage en rendu à motifs
[modifier | modifier le wikicode]Pour les consoles de 2ème, 3ème et 4ème génération, l'usage de motifs était obligatoire et tous les jeux vidéo utilisaient cette technique. Les cartes graphiques des consoles de jeux de cette époque étaient conçues pour gérer les motifs.
Le matériel affichait l'image finale ligne par ligne, pixel par pixel comme le ferait un CRTC. Sauf qu'il détermine : dans quelle motif se trouve le pixel à afficher, où se trouve le pixel dans le motif (quelle ligne, quelle colonne). Une fois cela fait, le VDC accède à la mémoire vidéo pour récupérer le numéro de motif. Une fois le numéro de motif connu, il lit la table des potifs en mémoire ROM, sur la cartouche. Puis, il sélectionne le pixel à afficher dans ce motif, et l'envoie à la palette indicée, puis à l'écran.

Les motifs sont des équivalents des caractères dans le mode texte. Un motif peut être vu comme une sorte de super-caractère. Une carte d'affichage en mode texte et une carte en rendu à motifs sont d'ailleurs très similaires. Le tampon de motifs est l'équivalent pour le rendu à motifs du tampon de texte en mode texte. La table des motifs est l'équivalent de la table des caractères, les deux convertissent un caractère/tile en pixels. La méthode d'adressage est fortement similaire, l'utilisation l'est aussi. La seule différence est leur contenu, la table des caractères stockant des caractères, la table des motifs stockant des motifs graphiques.
Une optimisation permettait de lire certains motifs dans les deux sens à l'horizontale, de faire une sorte d'opération miroir. Ce faisant, on pouvait créer un objet symétrique en mémorisant seulement un motif. Par exemple, la moitié droite est générée par une opération miroir sur la partie gauche. Mais cette optimisation était assez rare, car elle demandait d'ajouter des circuits dans un environnement où le moindre transistor était cher. De plus, les objets symétriques sont généralement assez rares.
Les sprites sont presque toujours gérés avec le rendu à motifs, pour des raisons de performance. Les sprites les plus simples sont un seul motif, mais les autres sprites sont formés en assemblant plusieurs motifs. Typiquement, un sprite prend de 2 à 4 motifs. Par exemple, un sprite de 16 pixels de haut et 8 pixels de large est composé de deux motifs de 8 pixels de côté. Pour cela, les registres pour les sprites mémorisent des numéros de motif, pas des pixels.

Le défilement avec des motifs
[modifier | modifier le wikicode]Le défilement se marie assez mal avec un rendu à base de motifs. La solution la plus simple fait du défilement motif par motif, par sauts de 8 pixels. L'implémentation est la même qu'avec le défilement, à savoir qu'on utilise un viewport matériel. La différence est que la mémoire vidéo contient des motifs et non des pixels, idem pour le viewport. Le résultat à l'écran n'est pas fluide du tout, il donne un défilement saccadé et désagréable. Pour corriger cela, il a existé des techniques pour implémenter du défilement du défilement pixel par pixel avec un rendu à motifs. Un défilement de ce type est appelé un défilement fluide, ou encore du défilement pixel par pixel.
La première implémentation du défilement fluide avec des motifs était purement logicielle, le VDC faisait lui du défilement motif par motif. Sur PC, la première implémentation a été trouvée par John Carmack, le programmeur derrière les moteurs des jeux IDSoftware comme DOOM, Quake, Wolfenstein 3D, etc. Il a inventé la technique de l'Adaptive tile refresh qui permet justement d'avoir un défilement fluide sur des cartes sans gestion hardware du défilement. D'autres solutions logicielles similaires existaient sur console. Elles utilisaient 8 copies de chaque motif, chacun étant décalé d'un pixel. Le motif adéquat était choisi suivant un décalage dépendant du défilement.
Mais il est possible de faire du défilement pixel par pixel avec un rendu à motifs, en ajoutant du hardware spécifique au VDC. Elles étaient implémentées sur les consoles de 4ème génération et antérieures, mais pas sur les anciens PC. Une des toute première console à gérer le défilement pixel par pixel était l'Intellivision, une des toutes premières consoles, précisément une console de deuxième génération. Elle avait des motifs de 8 pixels de côté, et permettait de défiler au pixel près.
Pour cela, elle disposait d'un registre de décalage contenant une valeur allant de 0 à 7, qui indiquait de combien de pixels il fallait décaler l'image sur l'écran. Pour décaler d'une valeur plus grande, il fallait recourir au logiciel. Le logiciel devait alors faire défiler l'image en la déplaçant en mémoire vidéo. Il s'agissait donc d'une implémentation partielle du défilement avec des motifs. Le logiciel faisait du défilement motif par motif, et pouvait finir le travail en ajustant le tout pour obtenir un défilement pixel par pixel.
Les consoles suivantes géraient le défilement pixel par pixel d'une manière plus optimisée, en gérant à la fois le registre de décalage et le défilement motif par motif. Le défilement pixel par pixel était réalisé en faisant un défilement motif par motif, puis en finissant le travail avec un registre de décalage. Le défilement motif par motif était géré comme expliqué ci-dessus, avec un viewport.
Les glitchs graphiques sur les bords de l'écran
[modifier | modifier le wikicode]Malgré leur support matériel, il arrive que le défilement au pixel près cause des glitchs graphiques sur les bords de l'écran. La raison est qu'avec un défilement par pixel, les motifs aux bords de l'écran sont partiellement affichés. Par exemple, un motif sur le bord gauche ait ses pixels gauche en-dehors de l'écran, ses pixels droits dans l'écran. Idem mais sur le bord droit : un motif a ses pixels de gauche dans l'écran, ceux de droite en-dehors. Le problème est que de tels motifs peuvent ne pas être affichés si le VDC ne le permet pas. En général, les motifs partiellement en-dehors de l'écran ne sont pas affichés, ce qui cause des glitchs graphiques.
Les VDC de l'époque ne pouvaient pas afficher un motif si ses coordonnées sont en-dehors de l'écran. En général, la coordonnée d'un motif est définie par rapport à une origine placée sur le pixel le plus en haut et le plus à gauche du motif. Dans ce cas, les motifs à gauche de l'écran ont une origine qui est en-dehors de l'écran, même si quelques pixels de droite sont affichés. Le motif n'est alors pas affiché. Ces pixels à droite disparaissent, ce qui fait qu'entre 0 et 7 pixels disparaissent pour un motif de 8 pixel de largeur, entre 0 et 15 pixels s'il a une largeur de 16 pixels, etc. Idem avec le défilement vertical, mais pour les motifs tout en haut de l'écran.
- Si l'origine du motif est définie sur le bord droit, c'est les motifs à droite de l'écran qui disparaissent.
Il y a la même chose avec les sprites. Si leur origine est en-dehors de l'écran, mais que le reste du sprite est affiché, ils ne sont pas affichés. Une solution pour éviter les problèmes est de les faire rentrer d'un seul côté de l'écran, typiquement à droite si leur origine est définie à gauche. Si le défilement va dans le bon sens, aucun glitch graphique ne se manifeste.
Pour cacher ces glitchs graphiques, il est possible d'ajouter des bandes noires sur les deux côtés de l'écran. Les bandes noires sont verticales et/ou horizontales, elle ont une largeur égale à un motif. Le VDC peut gérer nativement ces bandes noires, en matériel. Pour cela, il réduit simplement la résolution pour couper les bords de l'écran.