Avec l'arrivée des interfaces graphiques (celles des systèmes d'exploitation, notamment) et des jeux vidéo 2D, les cartes graphiques ont pu s'adapter. Les cartes graphiques 2D ont d'abord commencé par accélérer le tracé et coloriage de figures géométriques simples, tels les lignes, segments, cercles et ellipses, afin d’accélérer les premières interfaces graphiques. Par la suite, diverses techniques d'accélération de rendu 2D se sont fait jour. Ces techniques ne sont pas utiles que pour les jeux vidéo, mais peuvent aussi servir à accélérer le rendu d'une interface graphique. Après tout, les lettres, les fenêtres d'une application ou le curseur de souris sont techniquement du rendu 2D. C'est ainsi que les cartes graphiques actuelles supportent des techniques d’accélération du rendu des polices d'écriture, une accélération du scrolling ou encore un support matériel du curseur de la souris, toutes dérivées des techniques d'accélération de rendu 2D.
Les cartes graphiques 2D peuvent se classer en deux grands types : celles où l'image est calculée puis mémorisée dans un "framebuffer" et celles qui font sans. Les premières sont relativement simples à comprendre, alors que les autres sont beaucoup plus complexes. Ces dernières fabriquent l'image en même temps qu'elles l'affichent à l'écran. Il n'y a pas d'image calculée proprement dit, mais la carte graphique calcule un pixel à la fois en même temps qu'elle l'affiche.
Les cartes 2D avec "framebuffer".
Les cartes 2D avec un "framebuffer" calculent l'image intégralement dans le "framebuffer". La base d'un rendu en 2D avec ces cartes d'affichage est de superposer des images 2D pré-calculées les unes au-dessus des autres. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, etc. On distingue généralement l'image pour l'arrièreplan, qui prend tout l'écran, des images plus petites qu'on superpose dessus et qui sont appelées des sprites. Le rendu des "sprites" doit s'effectuer de l'image la plus profonde (l'arrière-plan), vers les plus proches (les "sprites" qui se superposent sur les autres) : on parle d'algorithme du peintre.
Les copies en mémoire et le circuit de "Blitter".
Les cartes 2D avec "framebuffer" partent des "sprites" et de l'image d'arrière-plan et les combinent pour former l'image finale dans le "framebuffer". Superposer les "sprite" se traduit par une copie des pixels de l'image aux bons endroits dans la mémoire. Le "framebuffer" est d'abord remplit par l'image d'arrière-plan, puis chaque "sprite" est copié dans la portion de mémoire adéquate, qui correspond à sa place en mémoire. Ce genre de copie est nécessaire pour superposer les "sprites", mais aussi lorsqu'on doit scroller, ou qu'un objet 2D se déplace sur l'écran. Déplacer une fenêtre sur votre bureau est un bon exemple : le contenu de ce qui était présent sur l'écran doit être déplacé vers le haut ou vers le bas. Dans la mémoire vidéo, cela correspond à une copie des pixels correspondant de leur ancienne position vers la nouvelle.
Une fois totalement calculée, l'image est envoyée à l'écran avec les "timings" adéquats. De telles cartes graphiques doivent être assez performantes. Elles doivent pouvoir rendre une image complète tous les soixantième ou cinquantième de secondes, les écrans CRT de l'époque ayant une fréquence de rafraichissement de 50 ou 60 Hertz. Vu que le rendu 2D demande de faire beaucoup de copie pour superposer les "sprites", les cartes 2D ont introduit un composant pour accélérer les copies d'images en mémoire. Ce circuit chargé des copies s'appelle le blitter. Sans "blitter", les copies étaient donc à la charge du processeur, qui devait déplacer lui-même les données en mémoire. Le blitter est conçu pour ce genre de tâches, sauf qu'il n'utilise pas le processeur.
La gestion des masques.
Ceci dit, un "blitter" possède d'autres fonctionnalités. Il peut effectuer une opération bit à bit entre les données à copier et une donnée fournie par le programmeur. Pour voir à quoi cela peut servir, reprenons notre exemple du jeu 2D, basé sur une superposition d'images. Les images des différents personnages sont souvent des images rectangulaires. Par exemple, l'image correspondant à notre bon vieux pacman ressemblerait à celle-ci. Évidemment, cette image s'interface mal avec l’arrière-plan. Avec un arrière-plan blanc, les parties noires de l'image du pacman se verraient à l'écran.
L'idéal serait de ne pas toucher à l’arrière-plan sur les pixels noirs de pacman, et de ne modifier l’arrière-plan que pour les pixels jaunes. Ceci est possible en fournissant un masque, une image qui indique quels pixels modifier lors d'un transfert, et quels sont ceux qui ne doivent pas changer. Grâce à ce masque, le "blitter" sait quels pixels modifier. Le "blitter" prend l'image du pacman, le morceau de l’arrière-plan auquel on superpose pacman, et le masque. Pour chaque pixel, il effectue l'opération suivante : ((arrière-plan) AND (masque)) OR (image de pacman).
Au final, l'image finale est bel et bien celle qu'on attend.
Les cartes 2D sans "framebuffer".
Avec les cartes 2D sans "framebuffer", les "sprites" ne sont pas copiés sur un arrière-plan préexistant. À la place, l'image est rendue pixel par pixel, à la volée. La carte graphique décide, à chaque envoi de pixel, s'il faut afficher les pixels de l’arrière-plan ou du "sprite" pendant l'envoi des pixels à l'écran, lors du balayage effectué par le CRTC.
L'architecture matérielle d'une carte 2D sans "framebuffer".
Sur ces cartes 2D, les "sprites" et l'image d'arrière-plan sont stockés dans des registres ou des RAM. La RAM pour l'arrière-plan est généralement assez grosse, car l'arrière-plan a la même résolution que l'écran. Il arrive que l'arrière-plan ait une résolution supérieure à celle de l'écran, ce qui est utile pour le "scrolling". Par exemple, si je prends la console de jeux NES, elle a une résolution de base de 256 par 240, alors que l'arrière-plan a uje résolution de 512 par 480. Pour les "sprites", la mémoire est généralement très petite, ce qui fait que les "sprites" ont une taille limitée.
Pour chaque "sprite", on trouve deux registres permettant de mémoriser la position du sprite à l'écran : un pour sa coordonnée X, un autre pour sa coordonnée Y. Lorsque le CRTC demande à afficher le pixel à la position (X , Y), chaque triplet de registres de position est comparé à la position X,Y fournie par le CRTC. Si aucun "sprite" ne correspond, les mémoires des sprites sont déconnectées du bus et le pixel affiché est celui de l'arrière-plan. Dans le cas contraire, la RAM du "sprite" est connectée sur le bus et son contenu est envoyé au RAMDAC.
Les cartes 2D les plus simples ne géraient que deux niveaux : soit l'arrière-plan, soit un "sprite" devant l'arrière-plan. Il n'est donc pas possible que deux "sprites" se superposent, partiellement ou totalement. Dans ce cas, l'image n'a que deux niveaux de profondeur. C'était le cas sur les consoles de seconde et troisième génération, comme la NES ou la Sega Saturn. Par la suite, une gestion des "sprite" superposés est apparue. Pour cela, il fallait stocker la profondeur de chaque "sprite", pour savoir celui qui est superposé au-dessus de tous les autres. Cela demandait d'ajouter un registre pour chaque "sprite", qui mémorisait la profondeur. le circuit devait donc être modifié de manière à gérer la profondeur, en gérant la priorité des "sprites".
D'autres consoles ont ajouté une gestion de la transparence. Dans le cas le plus simple, la transparence permet de ne pas afficher certaines portions d'un "sprite". Certains pixels d'un "sprite" sont marqués comme transparents, et à ces endroits, c'est l'arrière-plan qui doit s'afficher. Cela permet d'afficher des personnages ou objets complexes alors que l'image du "sprite" est rectangulaire. Le choix du pixel à afficher dépend alors non seulement de la profondeur, mais aussi de la transparence des pixels des "sprite". Le pixel d'un "sprite" a la priorité sur le reste s'il est opaque et que sa profondeur est la plus faible comparé aux autres. Cette gestion basique de la transparence ne permet pas de gérer des effets trop complexe, où on mélange la couleur du "sprite" avec celle de l'arrière-plan. 
Cette technique a autrefois été utilisée sur les anciennes bornes d'arcade, ainsi que sur certaines consoles de jeu bon assez anciennes. Mais de nos jours, elle est aussi présente dans les cartes graphiques actuelles dans un cadre particulièrement spécialisé : la prise en charge du curseur de la souris, ou le rendu de certaines polices d'écritures ! Les cartes graphiques contiennent un ou plusieurs "sprites", qui représentent chacun un curseur de souris, et deux registres, qui stockent les coordonnées x et y du curseur. Ainsi, pas besoin de redessiner l'image à envoyer à l'écran à chaque fois que l'on bouge la souris : il suffit de modifier le contenu des deux registres, et la carte graphique place le curseur sur l'écran automatiquement. Pour en avoir la preuve, testez une nouvelle machine sur laquelle les drivers ne sont pas installés, et bougez le curseur : "lag" garantit !
Le stockage des "sprites" et de l’arrière-plan : les tiles.
Les "sprites" et l'arrière-plan sont donc stockés chacun dans une mémoire dédiée. En théorie, les "sprites" et l'arrière plan sont des images tout ce qu'il y a de plus normales, et elles devraient être codées telles quelles. Mais cela demanderait d'utiliser des mémoires assez grosses. Notamment, l'arrière-plan a la même taille que l'écran : un écran d'une résolution de 640 pixels par 480 demandera d'utiliser un arrière-plan de 640 pixels par 480. La mémoire pour stocker l'arrière-plan devrait donc être assez grosse pour stocker toute l'image affichée sur l'écran et pourrait servir de "framebuffer". Le problème est moins critique pour les "sprite", mais il se pose pour les plus gros "sprite", qui demandent des mémoires assez importantes pour être stockés.
Pour résoudre ces problèmes, diverses techniques sont utilisées pour réduire la taille des "sprites" et de l'arrière-plan.
La seconde idée est que les "sprites" et l'arrière-plan sont fabriqués à partir de tiles, des carrés de 8, 16, 32 pixels de côtés, qui sont assemblés pour fabriquer un "sprite". L'ensemble des "tiles" est mémorisée dans un fichier unique, appelé le tile set, aussi appelé le tilemap. Ci-contre, vous voyez le "tile set" du jeu Ultima VI. Le "tile set" est généralement placé dans la mémoire ROM de la cartouche de jeu. Les "tiles" sont numérotées et ce numéro indique sa place dans la mémoire de la cartouche. L'idée est l'image ne mémorise pas des pixels, mais des numéros de "tiles". Le gain est assez appréciable : pour une tile de 8 pixels de côté, au lieu de stocker X * Y pixels, on stocke X/8 * Y/8 tiles. Ajoutons à cela que les numéros de "tiles" 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 "tiles", une mémoire ROM intégrée dans la cartouche de jeu.
Un autre avantage est que les "tiles" peuvent être utilisés en plusieurs endroits, ce qui garantit une certaine redondance. Par exemple, un "sprite" ou l'arrière-plan peuvent utiliser une même "tile" à plusieurs endroits, ce qui impose une certaine redondance. C'était très utile pour l'arrière-plan, qui est généralement l'image la plus redondante. On y trouve beaucoup de "tiles" bleues identiques pour le ciel, par exemple. Pareil si une "tile" est utilisée dans plusieurs "sprites", elle n'est stockée qu'une seule fois. Une autre possibilité était de lire certaines "tiles" 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 la moitié gauche de celui-ci, la moitié droite étant générée en faisant 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.
Le matériel des anciennes cartes 2D était optimisé pour gérer les "tiles". Son rôle était de reconstituer l'image finale en plusieurs étapes. Le "tile set" était généralement mémorisé dans une mémoire à part, il était parfois dans la mémoire ROM de la cartouche, mais il était parfois recopié dans une mémoire RAM intégrée dans la carte graphique pour plus de rapidité. Les registres pour les "sprites" et la RAM de l'arrière-plan étaient remplis de numéros de "tiles" et non directement de pixels. Le matériel rendait les "sprites" en lisant la "tile" adéquate, automatiquement. D'abord les circuits d'adressage lisaient les mémoires pour l'arrière-plan et les "sprites", et récupéraient un numéro de "tile". La "tile" correspondant était lue depuis la "tilemap", dans une autre mémoire. Enfin, divers circuits choisissaient le pixel adéquat dans la "tile" à chaque cycle d'horloge.
Si je devais faire une comparaison, les cartes 2D avec un rendu en "tile" sont aux cartes 2D ce que les cartes en mode texte sont au cartes d'affichage. Les caractères des cartes d'affichage en mode texte sont l'équivalent des "tiles" des cartes 2D en "tiles". Là encore, le but de ces architectures est d'économiser de la mémoire, très limitée et très chère pour l'époque. Pour les consoles de 2ème, 3ème et 4ème génération, l'usage de "tiles" é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 "tiles".