« Les cartes graphiques/Les architectures de type tiled rendering » : différence entre les versions

Un livre de Wikilivres.
Contenu supprimé Contenu ajouté
mAucun résumé des modifications
Ligne 1 : Ligne 1 :
Précédemment, dans ce cours, nous avons vu que certaines portions d'une scène 3D ne sont pas affichées à l'écran. Soit parce qu'elles sont en-dehors du champ de vision de la caméra, soit parce qu'elles sont masquées par un autre objet, ou encore parce que la face avant d'un objet cache la partie arrière. Il est primordial ne ne pas texturer et calculer les portions de l'image qui ne sont pas visibles à l'écran, mais ce n'est pas facile à faire. Il est possible d'éliminer rapidement les parties arrières cachées des objets, ainsi que ce qui est hors du champ de vision de la caméra, dès l'étape de rasterisation, soit assez tôt. Mais les objets cachés par d'autres plus proches posent problèmes, car le ''z-buffering'' est effectué dans les ROP. Et c'est sans compter les effets de transparence, qui font qu'un objet devant un autre peut ne pas le masquer. En conséquence, de nombreux pixels inutiles seront coloriés et éclairés par les pixels shaders, sans qu'on puisse y faire grand-chose.
Les cartes graphiques normales ne peuvent pas toujours éliminer les pixels cachés par des zones opaques de manière précoce. Vu que la détection des pixels masqués s'effectue dans les ROP, de nombreux pixels inutiles seront coloriés et éclairés par les pixels shaders. Or, la profondeur d'un pixel est connue dès la fin de l'étape de rasterisation, ce qui permet de détecter précocement si celui-ci sera ou non calculé. On peut penser que comparer les valeurs de profondeur en sortie du rasterizer serait une bonne chose, mais cela ne marcherait pas aussi bien que prévu. En effet, sur les cartes graphiques normales, l'ordre de soumission des triangles est aléatoire : un objet peut en cacher un autre sans que ces deux objets soient rendus consécutivement. Or, effectuer un test de profondeur précoce ne fonctionne que si les objets soumis à la carte graphique sont triés par leur valeur de profondeur, ce qui n'est jamais le cas. Et effectuer le tri des objets avant d'effectuer un test de profondeur serait nettement plus lent que d'effectuer le test de profondeur dans les ROP. Mais il existe des solutions alternatives, qui demandent d'adapter les cartes graphiques usuelles avec quelques optimisations, ou repenser totalement l'architecture des cartes graphiques. Dans ce chapitre, nous allons voir les deux solutions en détail.

Cependant, la profondeur d'un pixel est connue dès la fin de l'étape de rasterisation, ce qui permet de détecter précocement si celui-ci sera ou non calculé. On peut penser que comparer les valeurs de profondeur en sortie du rasterizer serait une bonne chose, mais cela ne marcherait pas aussi bien que prévu. En effet, sur les cartes graphiques normales, l'ordre de soumission des triangles est aléatoire : un objet peut en cacher un autre sans que ces deux objets soient rendus consécutivement. Or, effectuer un test de profondeur précoce ne fonctionne que si les objets soumis à la carte graphique sont triés par leur valeur de profondeur, ce qui n'est jamais le cas. Et effectuer le tri des objets avant d'effectuer un test de profondeur serait nettement plus lent que d'effectuer le test de profondeur dans les ROP. Mais il existe des solutions alternatives, qui demandent d'adapter les cartes graphiques usuelles avec quelques optimisations, ou repenser totalement l'architecture des cartes graphiques. Dans ce chapitre, nous allons voir les deux solutions en détail.


==Le ''Tiled rendering''==
==Le ''Tiled rendering''==

Version du 22 juillet 2021 à 14:44

Précédemment, dans ce cours, nous avons vu que certaines portions d'une scène 3D ne sont pas affichées à l'écran. Soit parce qu'elles sont en-dehors du champ de vision de la caméra, soit parce qu'elles sont masquées par un autre objet, ou encore parce que la face avant d'un objet cache la partie arrière. Il est primordial ne ne pas texturer et calculer les portions de l'image qui ne sont pas visibles à l'écran, mais ce n'est pas facile à faire. Il est possible d'éliminer rapidement les parties arrières cachées des objets, ainsi que ce qui est hors du champ de vision de la caméra, dès l'étape de rasterisation, soit assez tôt. Mais les objets cachés par d'autres plus proches posent problèmes, car le z-buffering est effectué dans les ROP. Et c'est sans compter les effets de transparence, qui font qu'un objet devant un autre peut ne pas le masquer. En conséquence, de nombreux pixels inutiles seront coloriés et éclairés par les pixels shaders, sans qu'on puisse y faire grand-chose.

Cependant, la profondeur d'un pixel est connue dès la fin de l'étape de rasterisation, ce qui permet de détecter précocement si celui-ci sera ou non calculé. On peut penser que comparer les valeurs de profondeur en sortie du rasterizer serait une bonne chose, mais cela ne marcherait pas aussi bien que prévu. En effet, sur les cartes graphiques normales, l'ordre de soumission des triangles est aléatoire : un objet peut en cacher un autre sans que ces deux objets soient rendus consécutivement. Or, effectuer un test de profondeur précoce ne fonctionne que si les objets soumis à la carte graphique sont triés par leur valeur de profondeur, ce qui n'est jamais le cas. Et effectuer le tri des objets avant d'effectuer un test de profondeur serait nettement plus lent que d'effectuer le test de profondeur dans les ROP. Mais il existe des solutions alternatives, qui demandent d'adapter les cartes graphiques usuelles avec quelques optimisations, ou repenser totalement l'architecture des cartes graphiques. Dans ce chapitre, nous allons voir les deux solutions en détail.

Le Tiled rendering

Tiled rendering architecture.

La première solution demande d'utiliser une classe de carte 3D légèrement différente de celles vues précédemment. Sur ces architectures, l'écran/image à rendre est découpé en rectangles, rendus indépendamment, uns par uns. Ces rectangles sont appelés des tiles, d'où le nom d'architectures à tiles donné à ce type de cartes graphiques.

Le rendu se fait tile par tile. Le rendu commence par calculer tout ce qui a trait à la géométrie associée à une tile, avant d'en rendre les pixels. Cette séparation entre rendu de la géométrie et traitement des pixels n'est pas anodin et ressemble à ce qui se fait avec la rasterisation normale. Sauf qu'ici, le calcul de la géométrie ne se fait pas triangle par triangle mais tile par tile. La carte 3D calcule toute la géométrie d'une tile, puis mémorise le résultat en mémoire vidéo et effectue ensuite le traitement des pixels de la tile. Cette séparation est intéressante car elle permet à la carte graphique de calculer la géométrie de plusieurs tiles pendant que le traitement des pixels est en train de se faire. Les tiles calculées en avance sont placées dans une file d'attente en mémoire vidéo, dont elles sortent pour subir l'étape de traitement des pixels.

L'élimination des pixels et triangles cachés s'effectue dès que la profondeur est disponible, c'est à dire à l'étape de rasterization. Sur ces architectures, le résultat des calculs géométriques est mémorisé en mémoire vidéo, avant d'être traité tile par tile. Chaque tile se voit attribuer la liste des triangles qu'elle contient : cette liste est appelée la Display List, et elle est enregistrée en mémoire vidéo. Par la suite, il suffit de rasterizer, placer les textures et exécuter les shaders chaque tile, avant d'envoyer le tout aux ROP. L'architecture globale d'une carte graphique à tiles change peu comparé à une carte à rasterization, si ce n'est que le rasterizer est modifié et qu'il est suivi d'une unité d'élimination des pixels cachés : l'Image Synthesis Processor, ou ISP.

Le rasterizer se voit ajouter un nouveau rôle : décider dans quelle tile se trouve un triangle. Pour cela, le rasterizer va calculer le rectangle qui contient un triangle (souvenez-vous le chapitre sur la rasterization), et va vérifier das quelle tile celui-ci est inclus : cela demande de faire quelques comparaison entre les sommets du rectangle et les sommets des tiles. L'Image Synthesis Processor remplace en quelque sorte le Z-Buffer et les circuits d'élimination des pixels cachés. Une architecture à tile a juste besoin d'un Z-Buffer pour la tile en cours de traitement, là où les cartes graphiques normales ont besoin d'un Z-buffer pour toute l'image. De plus, les tiles sont tellement petites que l'on peut stocker tout le Z-Buffer dans une mémoire tampon integrée dans l'ISP. Cette mémoire tampon réduit fortement les besoins en bande passante et en débit mémoire, ce qui rend inutile de nombreuses optimisations, comme la compression du Z-buffer.

Early-Z

Les architectures à base de Tiles ne sont pas la seule solution pour éviter le calcul des pixels cachés. Les concepteurs de cartes graphiques usuelles (sans tiled rendering) ont inventé des moyens pour détecter une partie des pixels qui ne seront pas visibles, avant que ceux-ci n'entrent dans l'unité de texture. Ces techniques sont des techniques d'early-Z. Mais ces techniques nuisent au rendu si les shaders peuvent bidouiller la profondeur ou la transparence d'un pixel. Pour éliminer tout problème, les drivers de la carte graphique doivent analyser les shaders et décider si le test de profondeur précoce peut être effectué ou non. Il existe plusieurs techniques d'early-Z, qui sont présentes depuis belle lurette dans nos cartes graphiques. Celles-ci peuvent être classées en deux catégories : le zmax, et le zmin. Il est parfaitement possible d'utiliser le zmax conjointement avec le zmin, ce qui donne des techniques hybrides.

Z-Max

Les deux techniques z-max et z-min découpent l'écran en tiles. Le Z-max consiste à vérifier si la tile à rendre est situé derrière des tiles déjà rendues pour la masquer cas échéant. Pour cela, il suffit de savoir quelle est la tile la plus profonde déjà rendue. Précisémment, il suffit de conserver la profondeur de cette tile et de faire les vérifications de profondeur. Le zmax consiste donc à vérifier si le triangle à rendre est situé derrière le pixel le plus profond de la tile. Ces techniques ont un gros défaut : il faut calculer la valeur maximale des pixels de la tile, ce qui demande de comparer les profondeurs de tous les pixels. Ce genre de chose s'effectue dans les ROPs, et demande parfois de lire les profondeurs depuis la mémoire vidéo…

La première technique de Z-Max est celle du Hierarchical Z. Dans les grandes lignes, cette technique consiste à conserver dans une mémoire cache (rarement en mémoire vidéo) une copie basse-résolution du tampon de profondeur, qui mémorise la valeur maximale de la profondeur pour chaque tile. Cette copie basse-résolution est mise à jour par les ROPs, en même temps que le Z-Buffer. Il existe d'autres techniques qui permettent d'éliminer ce genre de problèmes, comme le Depth Filter ou le Mid-texturing.

Z-Min

Avec le Z-min, on utilise la profondeur maximale des sommets du triangle dans les calculs. Cette valeur est comparée avec la valeur de profondeur minimale dans la tile. Si la profondeur du pixel à rendre est plus petite, cela veut dire que le pixel n'est pas caché et qu'il n'y a pas besoin d'effectuer de test de profondeur dans les ROPs. Le calcul de la profondeur minimale de la tile est très simple : il suffit de mémoriser la plus petite valeur rencontrée et la mettre à jour à chaque rendu de pixel. Par besoin de lire toutes les profondeurs de la tile d'un seul coup, ou quoique ce soit d'autre, comme avec le zmax. Cette méthode est particulièrement adaptée aux rendus dans lesquels les recouvrements de triangles sont relativement rares. Il faut dire que cette méthode ne rejette pas beaucoup de pixels comparé à la technique du zmax. En contrepartie, elle n'utilise pas beaucoup de circuits comparé au zmax : c'est pour cela qu'elle est surtout utilisée dans les cartes graphiques pour mobiles.