Comparatif GPU d'entrée de gamme : HyperMemory et TurboCache

Architectures

A chaque nouvelle génération de bus graphique c’est le même refrain chez les fabricants de puces : pour le haut de gamme on nous promet des débits permettant de transférer tellement de données qu’ils vont transfigurer nos jeux, et pour le bas de gamme on nous affirme que cela va permettre d’économiser de la mémoire vidéo en utilisant la RAM principale du PC. Ce fut le cas pour le PCI puis l’AGP vanté à grands coups d’AGP texturing et c’est maintenant le cas pour le PCI Express où chaque constructeur propose sa propre technique : HyperMemory pour ATI et TurboCache pour nVidia. Avant de décrire plus en détails ces nouvelles technologies, revenons sur les précédentes tentatives dans ce domaine.

La gestion de la mémoire vidéo : un véritable casse tête

Pour bien comprendre comment fonctionnent ces différentes techniques il faut avoir une idée de la façon dont la mémoire vidéo est gérée. Lorsqu’une puce 3D doit appliquer une texture a une primitive, elle tente d’y accéder dans sa mémoire locale et si elle n’y est pas présente, elle la transfère depuis la mémoire centrale via DMA (Direct Memory Access, cette technique permet à un périphérique de transférer directement des données depuis la mémoire centrale sans nécessiter de cycle CPU. Elle s’oppose au mode PIO dans lequel le CPU doit copier lui-même les données sur le bus).

Mais la quantité de mémoire vidéo n’étant pas infinie, lorsque celle-ci est pleine il faut remplacer une texture déjà présente par la nouvelle. Pour déterminer quelle texture doit être remplacée la plupart des drivers de cartes graphiques utilisent un algorithme LRU (Least Recently Used). Autrement dit la texture qui se voit remplacée est celle qui n’a pas été utilisée depuis le plus longtemps. Cet algorithme fonctionne convenablement la plupart du temps mais peut également s’avérer catastrophique dans certains cas ! En particulier si la quantité totale de textures nécessaire pour un frame ne tient pas dans la mémoire vidéo, la carte se retrouve à transférer l’ensemble des textures à chaque frame, ce qui a vite fait de la mettre sur les genoux.

Explications : supposons que nous disposons d’une carte dont il reste 8 Mo de mémoire vidéo pour les textures et que notre scène utilise pour chaque frame 3 textures de 3Mo chacune. Les 3 textures ne tiennent pas dans la mémoire vidéo, examinons alors comment se comporte le driver dans une telle situation :

  • Au début du frame 0 toutes les textures sont en mémoire vive, il n’y a rien en mémoire vidéo.

  • Le rendu du frame 0 commence. La puce 3D a besoin de la texture A, elle la transfère donc dans sa mémoire locale pour pouvoir l’utiliser. La copie en mémoire centrale est conservée, cela est certes plus coûteux en espace mémoire mais cela permet de transférer les textures exclusivement dans un sens : de la mémoire système vers la mémoire vidéo ce qui est plus efficace.

  • Le rendu du frame 0 se poursuit. La puce 3D a maintenant besoin de la texture B, comme précédemment celle-ci est transférée sans problème particulier puisque la place nécessaire est disponible en mémoire vidéo :

  • Le rendu du frame 0 touche à sa fin. La puce 3D réclame maintenant la texture C. Cette fois la situation se complique : il ne reste plus que 2 Mo sur la carte vidéo ce qui est insuffisant pour stocker la texture C, le driver analyse donc les diverses textures stockées sur la carte, détermine que la texture utilisée il y a le plus longtemps est la texture A : celle-ci est donc écrasée par la texture C pour permettre de terminer le rendu. Comme nous l’avons vu précédemment une copie de la texture A est toujours présente en mémoire vidéo, il n’y a donc pas besoin de l’y recopier avant de l’écraser par la texture C.

  • Le rendu du frame 0 est maintenant terminé, la puce peut passer au frame 1. Comme pour la frame précédente (la scène a peu changée entre deux frames), la première texture utilisée est la texture A mais celle-ci n’est pas en mémoire vidéo, il faut donc l’y recopier. La texture utilisée le moins récemment est la texture B celle-ci se retrouve donc écrasée par la texture A.

  • Le rendu du frame 1 se poursuit et maintenant la texture B est nécessaire. Pas de chance elle était présente il y a peu mais viens d’être remplacée par la texture A ! Il faut donc la transférer de nouveau alors que nous venons de l’évincer de la mémoire vidéo. Elle va prendre la place de la texture C qui n’a pas été utilisée depuis la fin du frame 0.

  • Le rendu du frame 1 se termine et décidément nous jouons de malchance car c’est maintenant la texture C que nous venons encore une fois de remplacer, qui doit prendre place en mémoire vidéo. La texture qui va être remplacée est la texture A inutilisée de puis le début du frame 1.

  • Le rendu du frame 1 est terminé celui du frame 2 peut commencer… En réclamant la texture A !

Comme nous venons de le voir dans cet exemple la façon de gérer la mémoire vidéo par une carte graphique peut vite conduire à une exploitation sous optimale du bus graphique. Ainsi dans cet exemple on transfère 9 Mo de données à chaque frame sans compter que 2 Mo de mémoire vidéo restent totalement inutilisés !

Tous ces transferts entre mémoire centrale et mémoire vidéo peuvent donc rapidement saturer le bus d’autant que ce n’est pas seulement la texture elle-même qui doit être transférée mais toute la pile de mipmaps. Ce dernier point est non négligeable, un rapide calcul montrant que cela représente un tiers de la taille de la texture initiale en plus à transférer. De plus chaque texture et ses mipmaps doivent se situer dans une portion contiguë de la mémoire vidéo, ce qui est propice à la fragmentation de cette dernière. Regardons ainsi ce qui se passe dans cette situation : nous disposons toujours de 8 Mo de mémoire pour nos textures mais cette fois ci deux textures (A et C) font 3Mo et une fait 1 Mo (B).

Puis la texture B n’est plus nécessaire elle est donc libérée.

Nous voulons maintenant charger la texture D de 2Mo, a priori nous avons assez d’espace dans la mémoire vidéo mais la fragmentation de cette dernière ne nous permet pas de trouver une portion de 2Mo de mémoire contiguë. Il faut donc remplacer une des textures en place. Comme vous pouvez le voir si l’utilisation du bus graphique n’est pas toujours optimale, ce n’est guère mieux du côté de l’utilisation de la mémoire vidéo.

Les solutions précédentes : AGP Texturing et Virtual Texturing

Face à ces problèmes plusieurs solutions ont été proposées. La première a été l’AGP Texturing, largement vanté par Intel lors de la sortie de ses chipsets 440LX qui étaient les premiers à supporter le bus AGP. Ce dernier dispose d’un mode appelé DIME pour DIrect Memory Execute qui autorise la puce 3D à utiliser les textures directement depuis la mémoire système. Ainsi les textures n’ont plus besoin d’être rapatriée dans la mémoire locale de la carte : l’utilisation du bus et de la mémoire vidéo sont bien plus efficaces et celle-ci peut conserver une taille modeste étant donné qu’elle ne sert plus qu’à stocker le frame buffer.

Pour permettre une telle magie il y a évidemment un peu de technique derrière notamment au niveau du chipset. Les puces 3D nécessitent l’accès à des zones contiguës de mémoire comme nous l’avons vu, or de nos jours la mémoire principale est virtualisée et découpée en pages de quelques Ko qui peuvent être localisées n’importe où pour garantir une utilisation optimale de l’espace disponible. Pour duper la puce 3D, le chipset intègre donc un mécanisme appelé GART (Graphics Address Remapping Table) qui stocke l’adresse physique des pages en mémoire et assure la traduction depuis les adresses virtuelles utilisées par la puce 3D, faisant ainsi apparaître l’ensemble des pages dédiées à la mémoire AGP comme une zone contiguë de mémoire.

Une autre solution visant à résoudre ces problèmes d’utilisation du bus et de la mémoire graphique s’appelle le Virtual Texturing et fut principalement promue par 3DLabs. Le principe est le même que pour le système de mémoire virtuelle utilisé par nos CPU : au lieu de travailler avec des adresses physiques, notre puce 3D va utiliser des adresses virtuelles. Le GPU voit désormais un espace logique linéaire entièrement adressable sans se soucier qu’en fait celui-ci est découpé en pages de 4 Ko qui peuvent être soit dans la mémoire de la carte soit dans la mémoire principale.

Lorsque le GPU a besoin des données d’une texture il utilise une adresse virtuelle ; celle-ci est automatiquement transformée en une adresse physique par un mécanisme de type TLB afin de déterminer où se situe la page. Si cette dernière n’est pas en mémoire vidéo, alors un transfert DMA l’y déplace, et le rendu peut désormais s’effectuer.

Cette technique résout de nombreux problèmes observés précédemment. Premièrement l’utilisation du bus est optimisée vu qu’au lieu de ramener des textures entières accompagnées de leurs piles de mipmaps seule une page de 4 Ko correspondant à la portion réellement nécessaire est transférée. Il n’y a donc pas une augmentation brutale du trafic sur le bus graphique dès qu’une texture est nécessaire au rendu, mais une augmentation progressive et répartie au cours du temps au fur et à mesure qu’une plus grosse portion de la texture est affichée. Ainsi, prenons un exemple radical : considérons une texture RGBA de 2048*2048 mipmappée qui devient visible au loin dans une scène 3D. Même si initialement celle-ci est tellement petite que seuls quelques texels du niveau de mipmap le plus élevé sont nécessaires, la texture entière est rapatriée engendrant aussitôt 21 Mo de transfert de données sur le bus. Avec le Virtual Texturing seul les quelques Ko correspondant à ce niveau de mipmap sont transférés et au fur et à mesure que la caméra s’approche de la texture les niveaux de mipmaps suivants sont à leurs tours transférés.

Le Virtual Texturing répond également au problème de la fragmentation de la mémoire vidéo. Vu que désormais les textures sont stockées sous forme de pages de 4 Ko et que celles-ci n’ont plus besoin d’être physiquement contiguës, l’utilisation de l’espace mémoire est optimisé.

A première vue ces techniques semblent séduisantes. Pourtant la technique qui les a supplanté est beaucoup moins intelligente, et les constructeurs ont utilisés la force brute pour résoudre le problème : doter nos cartes vidéo d’une quantité de mémoire locale toujours plus importante. Ainsi on peut trouver aujourd’hui des monstres équipés de 512Mo de mémoire vidéo, même si l’intérêt n’est pas flagrant. En revanche 256 Mo semblent nécessaires pour permettre aux cartes haut de gamme de s’exprimer.

Pourquoi les techniques présentées ici ont échouées ? Si cela est évident pour l’AGP texturing ça l’est moins pour le Virtual Texturing. En effet l’AGP texturing a été victime de la qualité des chipsets et leurs drivers. Mais surtout la mémoire centrale s’est vite retrouvée largement dépassée en terme de bande passante par rapport aux mémoires locales intégrées aux cartes graphiques. Ainsi si l’AGP Texturing pouvait faire illusion du temps de l’i740 qui restera une des rares cartes utilisant complètement le mode DIME, il est vite apparu qu’il briderait trop la génération de cartes suivantes. Exit donc l’AGP Texturing.

L’échec du Virtual Texturing est plus surprenant, fondamentalement le principe technique est bon, n’engendre pas de pertes de performances et résout plusieurs problèmes de façon élégante. Pourtant hormis 3DLabs, aucun autre constructeur ne suivra cette voie. Il est difficile de penser que ce soit le surcoût matériel pour intégrer un mécanisme de virtualisation de la mémoire locale qui ait freiné ATI et nVidia, il n’y a donc pas d’explication logique à l’échec de cette technologie qui dans tous les cas devrait renaître dans une version nettement plus évoluée avec WGF (la prochaine API graphique de Microsoft), et peut être même avant… mais ne brûlons pas les étapes !