Les cartes graphiques

Chapitre 4.1 : MultiGPU

Guy Grave

Combiner plusieurs cartes graphiques dans un PC pour gagner en performances. L'idée n'est pas mauvaise : si une seule carte graphique ne suffit pas, profiter des performances de plusieurs cartes graphiques peut aider à obtenir les performances voulues. Ces dernières années, les deux principaux fabricants de cartes graphiques non intégrées ont sorti des techniques multiGPU : le SLI et le Crossfire. Ces technologies sont destinées aux jeux vidéo et permettent aux joueurs d'obtenir des performances excellentes avec des graphismes sublimes.

Commentez

- Titre : Les cartes graphiques

- Auteur : Guy Grave

- Parution : 4 juin 2014

- Mise à jour : 25 novembre 2019

- Licence : Le contenu de cet article est rédigé par Guy Grave et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- Navigation

- I. Introduction

- II. Split Frame Rendering

- II-A. Scan Line Interleave

- II-B. Checker Board

- II-C. Scan-lines contigus

- III. Alternate Frame Rendering

- III-A. Microstuttering

- III-B. Dépendances interframes

- Navigation

Navigation

Tutoriel précédent : microarchitecture

Sommaire

I - Introduction

Combiner plusieurs cartes graphiques dans un PC pour gagner en performances. L'idée n'est pas mauvaise : si une seule carte graphique ne suffit pas, profiter des performances de plusieurs cartes graphiques peut aider à obtenir les performances voulues. Ces dernières années, les deux principaux fabricants de cartes graphiques non intégrées ont sorti des techniques multiGPU : le SLI et le Crossfire. Ces technologies sont destinées aux jeux vidéo, et permettent aux joueurs d'obtenir des performances excellentes avec des graphismes sublimes.

Toutefois, le multiGPU ne sert pas qu'aux joueurs. Combiner la puissance de plusieurs cartes graphiques peut servir à bien d'autres applications. Par exemple, on pourrait citer les applications de réalité virtuelle, l'imagerie médicale haute précision, les applications de conception par ordinateur, etc. Utiliser plusieurs cartes graphiques est alors une nécessité. Mine de rien, c'est ce genre de choses qui se cachent derrière les films d'animation : Pixar ou Disney ont vraiment besoin de rendre des images très complexes, avec beaucoup d'effets. Et ne parlons pas des effets spéciaux créés par ordinateur. Une seule carte graphique a tendance à rapidement rendre l'âme dans ces situations.

Le multiGPU peut se présenter sous plusieurs formes. La plus simple consiste à utiliser plusieurs cartes graphiques séparées, reliées à la même carte mère. Nos deux cartes graphiques sont alors utilisées de concert, à condition que les pilotes de l'ordinateur le supportent.

Dans certains cas, les cartes graphiques sont simplement connectées à la carte mère via PCI-Express. Si les deux cartes graphiques ont besoin d'échanger des informations, les transferts passent par le bus PCI-Express. Sur certaines cartes, on trouve un connecteur qui relie les deux cartes, sans passer par le bus PCI-Express : les échanges d'informations entre les cartes graphiques passent alors par ce connecteur spécialisé. Généralement, la solution la plus rapide est celle utilisant le connecteur. Si vous ne l'avez pas branché, faites-le pour obtenir de meilleures performances.

Dans d'autres cas, les deux, trois, quatre GPU sont câblés sur une seule carte. Les communications entre GPU s'effectuent alors via un bus ou un système d'interconnexion spécialisé, placé sur la carte. Il n'y a pas de différences de performances avec la solution utilisant des cartes séparées reliées avec un connecteur.

Contrairement à ce que l'on pourrait penser, le multiGPU n'est pas une technique inventée par Nvidia ou AMD. Il s'agit d'une technique très ancienne. Pensez donc qu'en 1998, il était possible de combiner dans un même PC deux cartes graphiques Voodoo 2, de marque 3dfx (un ancien fabricant de cartes graphiques, aujourd'hui disparu). Autre exemple : en 2006, le fabricant de cartes graphiques S3 avait introduit cette technologie pour ses cartes graphiques Chrome.

Tout le problème des solutions multiGPU est de répartir les calculs sur plusieurs cartes graphiques. Et c'est loin d'être chose facile. Il existe diverses techniques, chacune avec ses avantages et ses inconvénients. Cet article va vous présenter les techniques de répartition des calculs sur plusieurs GPU. Vous apprendrez ainsi des choses très intéressantes, et vous pourrez mieux configurer vos pilotes de cartes graphiques ou vos jeux, pour gagner en performances.

II - Split Frame Rendering

Une des techniques les plus simples pour répartir nos calculs sur plusieurs cartes graphiques consiste à découper notre image en morceaux, qui sont répartis sur plusieurs cartes graphiques. Ainsi, celles-ci s'occupent toutes de calculer la même image. Ce principe s'appelle le Split Frame Rendering.

Ce principe a été décliné en plusieurs versions et nous allons les passer en revue. Nous pouvons commencer par faire la différence entre les méthodes de distribution statiques et dynamiques. Dans les méthodes statiques, la manière de découper l'image est toujours la même. Celle-ci sera découpée en blocs, en lignes, en colonnes, etc., de la même façon, quelle que soit l'image. Dans les techniques dynamiques, la taille des blocs, lignes, etc. s'adapte en fonction de la complexité de l'image. Nous allons commencer par aborder les méthodes statiques.

II-A - Scan Line Interleave

Historiquement, la première technique multiGPU fût utilisée par les cartes graphiques Voodoo 2. La technique utilisée pour répartir les calculs sur ces deux cartes graphiques s'appelait le Scan Line Interleave.

Comme vous le savez, l'image à rendre est composée de pixels, organisés en lignes et en colonnes. Avec cette technique, chaque carte graphique calculait une ligne sur deux. La première carte rendait les lignes paires et l'autre les lignes impaires. On peut adapter la technique à un nombre arbitraire de GPU. Il suffit de faire calculer par chaque GPU une ligne sur 3, 4, 5, etc. Pour n GPU, chaque GPU calculera une ligne sur n.

Cette technique avait un avantage certain pour l'époque. Avant, la résolution des images était limitée par les cartes graphiques. Dans toutes les cartes graphiques, l'image à afficher est calculée, puis stockée dans une portion de la mémoire vidéo, que l'on appelle le tampon d'image. Celui-ci avait une taille limitée matériellement sur chaque carte graphique. Ainsi, une Voodoo 2 ne pouvait pas dépasser une résolution de 800 * 600. Avec le Scan Line Interleave, ce tampon d'image était utilisé pour seulement une moitié de l'image. Tout se passait comme si les deux tampons d'image des deux cartes étaient combinés en un seul tampon d'image plus gros, capable de supporter des résolutions plus élevées.

Cette technique a toutefois un gros défaut : l'utilisation de la mémoire vidéo n'est pas optimale. Comme vous le savez, la mémoire vidéo sert à stocker les objets géométriques de la scène à rendre, les textures et d'autres choses encore. Avec le Scan Line Interleave, chacun de ces objets, textures, etc. ; est présent dans la mémoire vidéo de chaque carte graphique. Il faut dire que ces objets et textures sont assez grands : la carte graphique devant rendre une ligne sur deux, il est très rare qu'un objet doive être rendu totalement par une des cartes et pas l'autre. Avec d'autres techniques, cette consommation de mémoire peut être mieux gérée.

II-B - Checker Board

Autre technique : ne pas découper l'image en lignes, mais en carrés de plusieurs pixels. Chacun de ces carrés sera alors attribué à une carte graphique bien précise. Dans le cas le plus simple, les carrés ont une taille fixe. Par exemple, on peut découper une image en blocs de 16 pixels.

Si les carrés sont suffisamment gros, il arrive qu'ils puissent contenir totalement un objet géométrique. Dans ces conditions, une seule carte graphique devra calculer ce qui est en rapport à cet objet géométrique. Elle seule aura donc besoin des données géométriques, des textures de l'objet, etc. L'autre carte n'en aura pas besoin, et n'aura pas à charger ces données dans sa mémoire vidéo. Le gain, en termes de mémoire, peut être appréciable si les blocs sont suffisamment gros. Mais il arrive souvent qu'un objet soit à la frontière entre deux blocs : il doit donc être rendu par les deux cartes et sera stocké dans les deux mémoires vidéo. Pour plus d'efficacité, on peut passer d'un découpage statique, où tous les carrés ont la même taille, à un découpage dynamique, dans lequel on découpe l'image en rectangles dont la longueur et la largeur varient.

Ces rectangles de pixels sont ensuite répartis sur les différentes cartes graphiques du système. En faisant varier le mieux possible la taille et la longueur de ces rectangles, on peut faire en sorte qu'un maximum de rectangles contiennent totalement un objet géométrique. Le gain en termes de mémoire et de rendu peut être appréciable. Le gain, en termes de mémoire, peut être réutilisé pour augmenter le niveau d'antialiasing sans trop de pertes de performances, pour stocker plus de textures, etc. Le gain, en termes de vitesse, peut se sentir si l'on dispose de peu de mémoire. Néanmoins, cette technique n'est vraiment pas optimale, même d'un point de vue mémoire. Découper des blocs dynamiquement est très complexe et le faire efficacement est un casse-tête pour les développeurs de drivers.

II-C - Scan-lines contigus

Une technique permet de garder le gain en mémoire sans trop compliquer la tâche des développeurs de pilotes. L'idée consiste à simplement couper l'image en deux, verticalement. La partie haute de l'image ira sur un GPU et la partie basse sur l'autre. Cette technique peut être adaptée avec plusieurs GPU : il suffit de découper l'image en autant de parties qu'il y a de GPU, et attribuer chaque portion à un GPU.

Ainsi, le gain en termes de mémoire et de rendu, est appréciable. De nombreux objets n'apparaissent que dans une portion de l'image et pas dans l'autre. Le pilote peut ainsi répartir les données géométriques et les textures pour éviter toute duplication, comme dans la technique des carrés. Cela demande du travail au pilote, mais cela en vaut la peine.

Le découpage de l'image peut reposer sur une technique statique : la moitié haute de l'image pour le premier GPU et le bas pour l'autre. Et cette technique s'adapte aussi avec plus de deux portions en découpant l'image en N portions identiques.

Ceci dit, quelques complications peuvent survenir dans certains jeux : les FPS notamment. Dans ces jeux, plus ou moins réalistes, le bas de l'image est plus chargé que le haut. Dans le bas de l'image, on trouve un sol assez complexe, des murs, les ennemis, etc. Le haut représente souvent le ciel ou un plafond, assez simple géométriquement. Toute l'agitation a lieu vers le bas ou le milieu de l'écran, et le haut est presque vide.

Ainsi, le rendu de la partie haute sera plus rapide que celui du bas, et une des cartes 3D finira par attendre l'autre. Mieux répartir les calculs devient alors nécessaire. Après tout, il vaut mieux que chaque carte doive faire 50 % du travail, au lieu d'avoir une carte qui se tape 70 % du boulot et l'autre qui ne fait que 30 % du rendu. Pour cela, on peut choisir un découpage statique adapté, dans lequel la partie haute envoyée au premier GPU est plus grande que la partie basse.

Cela peut aussi être fait dynamiquement : le découpage de l'image est alors choisi à l'exécution, et la balance entre partie haute et basse s'adapte aux circonstances. Comme cela, si vous voulez tirer une roquette sur un ennemi qui vient de prendre un jumper (vous ne jouez pas à UT ou Quake ?), vous ne subirez pas un gros coup de lag parce que le découpage statique était inadapté.

Dans ce cas, c'est le pilote qui gère ce découpage. Il dispose d'algorithmes plus ou moins complexes capables de déterminer assez précisément comment découper l'image au mieux. Mais il va de soi que ces algorithmes ne sont pas parfaits.

III - Alternate Frame Rendering

Comme je l'ai dit, la répartition des différentes données entre les GPU et la répartition des calculs posent de nombreux problèmes. S'il est mal fait, les performances s'effondrent. Et même bien fait, on n'obtient pas un gain proportionnel au nombre de GPU : des transferts entre les cartes graphiques sont toujours nécessaires et la répartition ne peut pas être parfaite.

L'alternate Frame Rendering, ou AFR ne pose pas ce genre de problèmes. Cette technique consiste à répartir non pas des blocs ou lignes de pixels, mais des images complètes sur les différents GPU. Au lieu de découper une image en morceaux et de répartir les morceaux sur les différents GPU, ce sont des images complètes qui sont envoyées à chaque GPU. Le problème mentionné plus haut disparaît alors. Plus besoin de réfléchir longuement pour savoir comment découper l'image et répartir les morceaux au mieux. Dans sa forme la plus simple, un GPU calcule une image et l'autre GPU calcule la suivante. Le calcul de ces deux images se fait alors en parallèle. Cette idée s'adapte aussi avec plus de deux GPU.

Cette technique est supportée par la majorité des cartes graphiques actuelles. De même, la technique consistant à découper l'image en deux et à répartir les deux portions sur deux GPU l'est aussi. Dans les pilotes AMD et Nvidia, vous avez ainsi le choix entre AFR et SFR, le SFR étant la technique de découpage d'images en deux. Il faut toutefois noter que cette technique n'est pas nouvelle : c'est ATI qui a inventé cette technologie sur ses cartes graphiques Rage Fury, afin de faire concurrence à la Geforce 256, la toute première Geforce.

Évidemment, on retrouve un vieux problème présent dans certaines des techniques vues avant. Chaque objet géométrique devra être présent dans la mémoire vidéo de chaque carte graphique, vu qu'elle devra l'afficher à l'écran. Il est donc impossible de répartir les différents objets dans les mémoires des cartes graphiques. Mais d'autres problèmes peuvent survenir.

III-A - Microstuttering

Un des défauts de cette approche, c'est le microstuttering. Lorsque nos images sont réparties sur nos cartes graphiques, elles le sont dès qu'une carte graphique est libre et qu'une image est disponible. Suivant le moment où une carte graphique devient libre et le temps mis au calcul de l'image, on peut avoir des décalages entre deux images qui varient beaucoup.

Dans des situations où le processeur est plus faible, les temps entre deux images peuvent se mettre à varier très fortement, et d'une manière beaucoup moins prévisible. Le nombre d'images par seconde se met à varier rapidement sur de petites périodes de temps. Alors certes, on ne parle que de quelques millisecondes, mais cela se voit à l'œil nu. Cela cause une impression de microsaccades, que notre cerveau peut percevoir consciemment, même si le temps entre deux images est très faible. Suivant les joueurs, des différences de 10 à 20 millisecondes peuvent rendre une partie de jeu injouable. Le phénomène est bien connu, surtout depuis la publication d'un article sur le site TheTechReport, disponible via ce lien : http://techreport.com/review/21516/inside-the-second-a-new-look-at-game-benchmarking.

Pour diminuer l'ampleur de ce phénomène, les cartes graphiques récentes incorporent des circuits pour limiter la casse. Ceux-ci se basent sur un principe simple : pour égaliser le temps entre deux images et éviter les variations, le mieux est d'empêcher des images de s'afficher trop tôt. Si une image a mis très peu de temps à se calculer, on retarde son affichage durant un moment. Le temps d'attente idéal est alors calculé en fonction de la moyenne du taux d'images mesuré précédemment.

III-B - Dépendances interframes

Il arrive que deux images soient dépendantes l'une de l'autre : les informations nées lors du calcul d'une image peuvent devoir être réutilisées dans le calcul des images suivantes. Cela arrive quand des données géométriques traitées par la carte graphique sont enregistrées dans des textures (dans les Streams Out Buffers pour être précis), dans l'utilisation de fonctionnalités de DirectX ou d'OpenGL qu'on appelle le rendu vers texture (Render To Texture), ainsi que dans quelques autres situations.

Évidemment, avec l'AFR, cela pose quelques problèmes : les deux cartes doivent synchroniser leurs calculs pour éviter que l'image suivante rate des informations utiles, et soit affichée n'importe comment. Sans compter qu'en plus, les données doivent être transférées dans la mémoire du GPU qui calcule l'image suivante.

Navigation

Tutoriel précédent : microarchitecture

Sommaire