Aller au contenu

Les cartes graphiques/La mémoire unifiée et la mémoire vidéo dédiée

Un livre de Wikilivres.

Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les cartes graphiques dédiées sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée carte graphique intégrée, ou encore IGP (Integrated Graphic Processor). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.

Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :

  • Les cartes graphiques à mémoire vidéo dédiée, à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre RAM système et RAM vidéo.
  • Les cartes graphiques à mémoire unifiée, où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
Répartition de la mémoire entre RAM système et carte graphique

Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple framebuffer. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.

Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux gamers. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.

Mémoire unifiée Mémoire dédiée
GPU dédié Quelques rares GPU d'entrée de gamme Tous les GPU, sauf de rares exceptions
GPU intégré Systématique
GPU soudé Anciennes consoles de jeu Ordinateurs portables modernes

La mémoire unifiée

[modifier | modifier le wikicode]

Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.

Répartition de la mémoire entre RAM système et carte graphique

Les GPU dédiés avec mémoire unifiée

[modifier | modifier le wikicode]

Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.

D'ailleurs, de telles cartes dédiées incorporent un framebuffer directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.

Les GPU intégrés

[modifier | modifier le wikicode]

Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le bus mémoire. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.

Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.

Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.

Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.

Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.

Caches sur un iGPU

N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la zero-overhead copy, terme barbare mais qui cache une réalité très simple.

Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.

Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (Direct Memory Access). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.

Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.

La mémoire vidéo dédiée

[modifier | modifier le wikicode]

Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.

La mémoire vidéo est mappée dans l'espace d'adressage du CPU

[modifier | modifier le wikicode]

L'espace d'adressage du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des entrées-sorties mappées en mémoire.

Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.

Espace d'adressage classique avec entrées-sorties mappées en mémoire

Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des Base Address Registers (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.

Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie resizable bar, qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.

La mémoire virtuelle des GPUs dédiés

[modifier | modifier le wikicode]

Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.

Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.

Mémoire virtuelle des cartes graphiques dédiées

Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la Graphics address remapping table, abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une Memory Management Unit (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (Input Output-MMU).

Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.

La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.

Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la table des pages, qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.

Les transferts Direct Memory Access

[modifier | modifier le wikicode]

Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie Direct Memory Access, aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.

Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le contrôleur DMA, qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.

Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.

La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le driver de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.

Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
Microarchitecture du GPU NV1 de NVIDIA

La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'AGP texturing permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.

L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.

La cohérence des caches sur un GPU

[modifier | modifier le wikicode]

Pour terminer ce chapitre, nous allons parler de la cohérence des caches. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.

La cohérence des caches entre CPU et GPU

[modifier | modifier le wikicode]

Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.

Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma suivant illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.

Cohérence des caches entre CPU et GPU avec mémoire unifiée

La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les problèmes de cohérence des caches peuvent se faire à deux moments : quand le CPU écrit en mémoire RAM/vidéo, quand le GPU lit des données en mémoire vidéo. Voyons d'abord le second cas, puis le second.

Supposons que le CPU ait transféré les données dans la mémoire vidéo, avec un transfert DMA sur un GPU dédié. Une situation bien précise pose problème : quand un transfert DMA écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.

Cohérence des caches avec DMA.

Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.

La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.

Au niveau du processeur, le processeur doit écrire les données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM. Pour éviter cela, les processeurs modernes marquent des blocs de mémoire comme "non-cacheables", à savoir que toute lecture/écriture dedans se fait sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.

Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes

La situation peut être optimisée sur les GPU intégrés. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.

Cohérence des caches entre CPU et GPU intégré

La cohérence des caches entre processeurs de shaders

[modifier | modifier le wikicode]

Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.

Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.

Cohérence des caches

La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.

Pour corriger ce problème, les ingénieurs ont inventé des protocoles de cohérence des caches pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.

En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.

C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de render-to-texture. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de render-to-texture étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.