Accueil » Dossier » GPU haut de gamme : ATI VS nVidia » Page 2

GPU haut de gamme : ATI VS nVidia

1 : Introduction 3 : Architectures (suite) 4 : Filtrage des textures 5 : AntiAliasing 6 : Le test 7 : Performances synthétiques 8 : Tests pratiques 9 : Tests pratiques (suite) 10 : Cartes Asus, bruit, overclocking 11 : Bilan, conclusion

Architectures

Après plus d’un an au cours duquel les deux constructeurs se sont contentés de décliner leurs architectures respectives en grappillant quelques Mhz à chaque mise à jour de leur gamme, il était temps pour ATI et nVidia de nous présenter leur seconde génération de cartes DirectX 9. Il est aujourd’hui incontestable que le constructeur Canadien a remporté la première manche. Si les performances des GeForce FX étaient globalement satisfaisantes, elles étaient aussi inconstantes. En particulier lors de l’exécution de Pixel Shaders flottants où elles étaient largement distancées par les puces ATI. En contrepartie elles offraient des fonctionnalités bien plus avancées en ne limitant plus les Pixel Shaders à un agencement bien défini d’instructions.

Comme on le constate les objectifs pour les deux concurrents étaient différents : pour nVidia il s’agissait d’offrir des performances de haut niveau dans toutes les circonstances afin de ne plus subir la même déconvenue qu’avec les GeForce FX. Pour ATI il fallait continuer sur la lancée de sa génération précédente tout en améliorant les fonctionnalités de son pipeline de rendu. Lequel des deux constructeurs a réussi son pari ? C’est ce que nous allons tâcher de déterminer.

NV40 VS R420 : une vue d’ensemble

Si l’on se contente d’une vue d’ensemble assez superficielle des deux puces, on peut constater une ressemblance frappante entre les deux architectures :

  • 6 unités de vertex shaders
  • 16 pixel pipelines organisés sous la forme de 4 « quad pipelines» indépendants
  • 1 unité de texture par pixel pipeline
  • un contrôleur mémoire 256 bits constitué de 4 canaux 64 bits organisés en crossbar
  • utilisation de RAM de type GDD3
C’est la première fois que les deux constructeurs présentent des architectures qui semblent aussi similaires au premier abord. Cela tranche radicalement avec ce que nous avions pu voir initialement lors de la génération précédente :

En soit cela n’est pas surprenant : après chaque génération les constructeurs affinent leur design en fonction de ce qui s’est avéré efficace et corrigent ainsi les erreurs qui pouvaient déséquilibrer leurs architectures. ATI et nVidia cherchant une solution optimale au même problème il est logique, et quelque part rassurant, qu’ils arrivent aux mêmes conclusions et que leurs designs finissent par converger vers des caractéristiques communes.

Cette fois il semble que ce soit nVidia qui rejoigne les choix d’ATI mais si l’on regarde les générations précédentes le Canadien ne s’était pas privé pour faire de même : Le R100 était ainsi constitué de 2 pixel pipelines chacun pourvu de 3 unités de texture. Cette architecture contrastait avec le choix de nVidia qui offrait alors le NV15 formé de 4 pixel pipelines dotés de 2 unités de texture. Le choix de la firme Californienne vert s’étant avéré le plus efficace le R200 s’est présenté sous la même configuration : 4×2.

Pour cette deuxième génération la différence portait sur le contrôleur mémoire : 4 contrôleurs 32 bits indépendants pour nVidia (NV20/NV25), 2 contrôleurs 64 bits pour ATI (R200). Là encore la solution de nVidia était la meilleure et pour le R300 ATI passa donc à 4 contrôleurs mémoire 64 bits.

Alors si ces deux architectures sont finalement aussi proches, cela a-t-il encore un intérêt de s’y intéresser ? Oui car si l’on se plonge dans ces architectures plus dans le détail on s’aperçoit que beaucoup de choses les séparent encore et c’est ainsi que l’on pourra expliquer leur comportement dans différentes situations.

Mais avant de se lancer je préfère faire un petit rappel technique pour être sûr que tout le monde soit d’accord sur les définitions. Vous l’aurez constaté dans cet article il est beaucoup question de « quad pipeline » et il est possible que vous ignoriez la signification de ce terme. Vous le savez sans doute les puces 3D lorsqu’elles rastérisent (c’est-à-dire lorsqu’elles transforment les polygones en pixels) ne travaillent pas pixel par pixel mais en traitent plusieurs à la fois. On parle ainsi d’architecture à deux pixel pipelines, à quatre pixel pipelines ou a 8 pixel pipelines etc… En pratique depuis la GeForce pour nVidia et depuis la Radeon 8500 pour ATI il est inexact de parler de pixel pipelines. Il faudrait parler de quad pipeline.

Mais qu’est ce donc qu’un quad ? Tout simplement un ensemble de 4 pixels organisés en carré de 2 pixels de côté. Vous devez vous dire que je joue sur les mots, que je pinaille sur des détails très techniques qui n’ont aucune importance mais pourtant cela n’est pas le cas. Quelle est la différence ? Tout simplement : lorsque l’on parle de n pixel pipelines il y a implicitement une notion d’indépendance entre ces pipelines. En pratique ce n’est pas le cas : les 4 pipelines sont liés, le quad étant en fait une unité SIMD opérant sur les 4 pixels en parallèle en leur appliquant la même opération. L’intérêt ? Je ne reviens pas sur celui évident de paralléliser les calculs pour traiter plus de pixels à chaque cycle, en revanche celui d’organiser les quatre pipelines sous la forme d’une unité SIMD est moins évident. Il permet essentiellement de diminuer les coûts : une unité SIMD étant nettement moins coûteuse que quatre unités indépendantes.

Ainsi la plupart des calculs ne se font pas par pixel, mais par quad, c’est le cas notamment de la détermination du niveau de MIP à employer par exemple. Maintenant que vous avez compris l’intérêt de l’utilisation d’unités SIMD vous devez vous demander pourquoi utiliser cette configuration particulière en carré de 2 pixels de côté ? Pourquoi ne pas imaginer une ligne de 4 pixels adjacents ? Voire 8 ou plus pour maximiser encore le parallélisme ! Il y a plusieurs raisons à tout cela. Si les ingénieurs ont fini par tous s’accorder sur ce schéma c’est que, vous vous en doutez, c’est celui qui offre la meilleure efficacité. Imaginez en particulier ce qui se passe aux arêtes des polygones : Il y aura des situations où seul un pixel sera utilisé ! Avec une organisation en quad cela peut aussi survenir mais on limite ce problème. De la même façon un quad « plus gros » de 3×3 pixels voire 4×4 s’avèrerait moins efficace dans ces situations. Il faut également ajouter que, couplé à un ordre de balayage particulier, la rastérisation par quad de 2 pixels de côté fournit une plus grande cohérence lors des accès mémoire, augmentant du même coup l’utilisation du cache et réduisant donc la bande passante mémoire utilisée.

  • Dans cet exemple le triangle sera rastérisé en 3 cycles en utilisant quatre pipelines organisés en un quad de 2 pixels de côté. Au pire un seul un pipe reste inactif dans ce cas précis

  • Avec une organisation en ligne de 4 pixels de côté il faut 4 cycles, et dans la pire des situations jusqu’à 3 pipes auraient été inactifs.
Avec le R300 ATI a introduit l’utilisation de plusieurs quad pipelines et, comme vous allez le voir dans les nouvelles architectures la tendance est encore plus prononcée. Cela permet d’augmenter le fill rate tout en conservant une certaine efficacité qui serait perdue par le passage à une organisation différente des pixels pipeline. De plus cela permet de décliner plus facilement un processeur graphique sur toute une gamme de produits : il suffit pour cela de désactiver un ou plusieurs quads pour obtenir des chips moins performants sans nécessiter une refonte complète de l’architecture. Cela permet aussi de limiter les pertes liées aux défauts de fabrication : ainsi si un pixel pipeline est défectueux il suffit de désactiver le quad le contenant et le chip pourra quand même être vendu. Vu la complexité des chips actuels vous comprendrez qu’il n’est pas étonnant que les constructeurs apprécient cette possibilité.

Le R420 dans le détail

Intéressons nous maintenant plus précisément à la nouvelle architecture d’ATI. Mais tout d’abord il convient de faire un petit rappel historique des faits. Alors que le R300 vient d’être annoncé et s’avère être extrêmement prometteur, ATI euphorique nous promet par la même occasion un R400 doté d’une architecture encore plus révolutionnaire ! Puis ce R400 commence à se faire de plus en plus discret alors qu’ATI dérive son R300 en plusieurs versions que ce soit dans le haut de gamme (R350, R360) ou le milieu de gamme (RV350, RV360). Finalement on finit par apprendre la vérité : le R400 prévu initialement s’est avéré être bien trop ambitieux pour la date d’introduction à laquelle il était initialement destiné. Ses fonctionnalités auraient été largement au delà de celles offertes par les API de l’époque, donc inexploitables. Et en contrepartie cela aurait eu un impact désastreux pour les performances étant donné les compromis que cela aurait nécessités dans le design de la puce.

Entre alors en scène le R420, une nouvelle architecture dérivée du R300, ce qui étant donné les excellentes performances du R300 n’est pas une mauvaise idée en soit. Le R400 initial se transforme alors en R500. Mais, nouveau coup de théâtre : le R520 fait son apparition sur les roadmaps officieuses d’ATI ! Le R500 se retrouve finalement à la base du chip de la future console de Microsoft et du R600. Tout ce qu’il faut retenir de cet imbroglio c’est que le R420 a été conçu dans un temps record à partir du R300 et quand on sait que la conception d’une nouvelle architecture nécessite plusieurs années il est inévitable que ce choix a eu des conséquences comme nous allons le voir.

Vertex Shaders

Pas de surprise de ce côté-là, à part leur nombre qui passe de quatre à six comme nous l’avons vu précédemment, les unités de vertex shader restent très largement inchangées. Elles conservent ainsi leurs spécificités : comme notamment la possibilité de travailler à la fois sur un vecteur 128 bits (4 flottants simple précision) et sur un scalaire 32 bits simultanément. Mais elles conservent également leurs principales limitations. Ainsi elles ne supportent qu’un contrôle de flux statique, sont incapables d’échantillonner une texture et travaillent toujours sur le mode SIMD (Single Instruction Multiple Data) : la même instruction étant exécutée par toutes les unités simultanément. En contrepartie ATI a ajouté le support natif des instructions trigonométriques SIN et COS qui ne nécessitent aujourd’hui plus qu’un cycle alors qu’auparavant elles étaient gérées par la macro SINCOS qui émulait leur comportement en utilisant plusieurs instructions.

Comme on le constate donc les Vertex Shaders du R420 restent au niveau 2.0.

Pixel Shaders

En ce qui concerne les Pixels Shaders les choses sont un peu plus intéressantes. En effet ATI a finalement activé le FBuffer sensé être disponible dés le R350. Cela permet donc d’éliminer un certain nombre de limitations présentes avec les PS2.0 notamment concernant le nombre d’instructions. Avec le R300 ce dernier était fixé à 96 instructions : 64 instructions arithmétiques et 32 instructions de texture. Désormais il passe à 512 instructions et ce quel que soit leur type. Mais ça n’est pas suffisant pour supprimer toutes les contraintes des PS2.0 en particulier le nombre d’indirections de textures reste pour sa part limité à 4. Pour pouvoir exploiter ces fonctionnalités un nouveau profil de compilation a été créé : PS_2_b.

Le reste des fonctionnalités n’a que peu bougé, on retrouve donc une unité arithmétique et une unité d’adressage de texture comme sur le R300. En pratique les choses sont un peu plus complexes : tout d’abord l’unité arithmétique est, comme pour les Vertex Shaders, co-issue ce qui signifie qu’au lieu d’appliquer la même opération aux 4 éléments d’un vecteur elle peut exécuter deux opérations différentes sur les éléments de ce vecteur. En l’occurrence elle travaille sur un vecteur de 3 flottants et un scalaire simultanément. Si l’opération porte sur un quadruplet R,G, B, A alors les deux unités effectueront la même instruction et le gain sera nul par rapport à une unité SIMD à 4 dimensions classique. En revanche si l’opération n’est appliquée qu’à une partie du vecteur, le driver pourra réorganiser les instructions et utiliser l’unité scalaire afin de maximiser l’utilisation des ressources. Comme il est courant dans un Pixel Shader de travailler sur la couleur (R, G, B) et le canal alpha séparément ce design s’avère extrêmement efficace.

Il faut aussi noter que l’unité arithmétique n’est pas seule : il y a en fait deux unités, une complète, gérant l’ensemble du jeu d’instructions. L’autre plus simple ne gérant qu’un sous ensemble de celui-ci permettant ainsi d’exécuter certaines instructions « gratuitement ». Le sous-ensemble géré par cette unité n’est pas détaillé, les tests ont toutefois montré qu’elle était capable de gérer l’ensemble des modifiers des PS1.4. Les modifiers sont des instructions simples pouvant être appliquées au résultat d’une opération.

Ainsi par exemple si l’on souhaite effectuer l’opération suivante :

z = (x+y) * 2

En assembleur cela donnerait approximativement :

add z, x,y

mul z, z, 2

Soit deux instructions. Grâce aux modifiers on peut l’exécuter en une seule fois en faisant

add_x2 z, x, y

A noter aussi qu’ATI a profité de l’introduction de ce nouveau profil pour exposer certaines spécificités de son architecture R300 qui n’était pas accessible via les PS2.0 comme la présence de 32 registres temporaires au lieu de 12. Le constructeur Canadien a aussi ajouté le support du face register indiquant si le fragment traité vient d’une face avant ou d’une face arrière et permettant d’adapter le traitement en conséquence.

3dc

Les apports de la compression de texture dans le domaine de la 3D sont indéniables que ce soit pour la gain de place généré ou pour les économies de bande passante que cela entraîne. Ceci explique le succès du S3TC (ou DXTC) que tous les constructeurs ont ajouté à leurs architectures. Mais le DXTC n’est pas parfait pour autant : tout d’abord c’est une compression avec pertes ce qui signifie qu’il est impossible de retrouver exactement l’image initiale à partir des données compressées. De plus la compression est basée sur des blocs de pixel ce qui peut produire des artefacts. Dans le cas de textures traditionnelles qui sont générées à partir de photographies ou par des graphistes cela ne pose pas de problème particulier, mais dans d’autres cas c’est nettement plus visible.

C’est ce qui se passe notamment lorsque l’on utilise le DXTC pour compresser des normal maps. Dans une normal map le triplet R, G, B ne représente pas une couleur mais une direction : un vecteur normal exprimé dans un espace particulier. Cette normal map est ensuite appliquée à un modèle et utilisée pour effectuer les calculs d’éclairage par pixel, ce qui permet d’obtenir de biens meilleurs résultats. C’est cette technique qui est utilisée dans Far Cry ou Doom III par exemple et qui sera également employée dans le prochain Unreal Engine. Et dans ce genre de cas la technique de compression usuelle donne de mauvais résultats. Il existe des solutions pour contourner le problème et ATI en avait décrit plusieurs dans un white paper avec leurs avantages et leurs défauts. Le 3dc dérive d’ailleurs d’une des solutions qu’ils avaient détaillées.

Le principe est simple, tout comme le DXTC le 3dc compresse par blocs de 4×4 pixels. Dans un premier temps la composante z du vecteur est éliminée puisqu’il est possible de la retrouver à partir de x et y au prix d’un léger surcoût dans le shader (un vecteur normal pouvant être écrit sous la forme x² + y² + z² = 1 on en déduit z = √(1 – x² – y²). Ensuite les valeurs minimales et maximales de x et y pour le bloc sont conservées en haute précision (8 bits par composante, c’est ici que se situe la principale différence de qualité avec la version DXT5 dont une composante avait droit à 6 bits et l’autre 8). Six valeurs intermédiaires sont générées entre ces valeurs maximales et minimales de x et de y. Enfin pour chaque élément du bloc la valeur la plus proche est déterminée parmi les 8 possibles. Il est ainsi possible de stocker chaque composante sur 3 bits. On passe donc de 512 bits par bloc sans compression (16 pixels * 3 composantes * 8 bits par composante) à 128 (16 pixels * 2 composante * 3 bits par composante + 4 composantes (xmin, ymin, xmax, ymax) * 8 bits) soit un ratio de 4 pour 1. Notons toutefois par souci d’exactitude qu’il est plus juste de parler de compression 2 pour 1. En effet il est tout à fait possible d’utiliser la même technique que le 3dc à savoir conserver uniquement les valeurs x et y dans une texture traditionnelle et retrouver la valeur z dans le shader. Dans ce cas on passe à 16 bits par pixels soit 256 bits par blocs.

Sommaire :

  1. Introduction
  2. Architectures
  3. Architectures (suite)
  4. Filtrage des textures
  5. AntiAliasing
  6. Le test
  7. Performances synthétiques
  8. Tests pratiques
  9. Tests pratiques (suite)
  10. Cartes Asus, bruit, overclocking
  11. Bilan, conclusion