Wikilivres
frwikibooks
https://fr.wikibooks.org/wiki/Accueil
MediaWiki 1.46.0-wmf.23
first-letter
Média
Spécial
Discussion
Utilisateur
Discussion utilisateur
Wikilivres
Discussion Wikilivres
Fichier
Discussion fichier
MediaWiki
Discussion MediaWiki
Modèle
Discussion modèle
Aide
Discussion aide
Catégorie
Discussion catégorie
Transwiki
Discussion Transwiki
Wikijunior
Discussion Wikijunior
TimedText
TimedText talk
Module
Discussion module
Event
Event talk
Fonctionnement d'un ordinateur/L'architecture de base d'un ordinateur
0
65780
763642
762914
2026-04-13T20:30:44Z
Mewtow
31375
/* Les coprocesseurs */
763642
wikitext
text/x-wiki
Dans les chapitres précédents, nous avons vu comment représenter de l'information, la traiter et la mémoriser avec des circuits. Mais un ordinateur n'est pas qu'un amoncellement de circuits et est organisé d'une manière bien précise. Il est structuré autour de trois circuits principaux :
* un '''processeur''', qui manipule l'information et donne un résultat ;
* une '''mémoire''' qui mémorise les données à manipuler ;
* les '''entrées/sorties''', qui permettent à l'ordinateur de communiquer avec l'extérieur.
[[File:Architecture Von Neumann.png|centre|vignette|upright=2|Architecture d'un système à mémoire.]]
Pour faire simple, le processeur est un circuit qui s'occupe de faire des calculs. Rien d'étonnant à cela. Je rappelle que tout est codé par des nombres dans un ordinateur, ce qui fait que manipuler des nombres revient simplement à faire des calculs. Un ordinateur n'est donc qu'une grosse calculatrice améliorée, et le processeur est le composant qui fait les calculs.
La mémoire s'occupe purement de la mémorisation des données, des nombres sur lesquelles faire des calculs. Pour être plus précis, il y a deux mémoires : une pour les données proprement dites, une autre pour le programme à exécuter. La première est la '''mémoire RAM''', la seconde est la '''mémoire ROM'''. Nous détaillerons ce que sont ces deux mémoires dans la suite du chapitre, mais sachez que nous avions déjà rencontré ces deux types de mémoires dans les chapitres sur les registres et les mémoires adressables.
Les entrées-sorties permettent au processeur et à la mémoire de communiquer avec l'extérieur et d'échanger des informations avec des périphériques. Les '''périphériques''' regroupent, pour rappel, tout ce est branché sur un ordinateur, mais n'est pas à l'intérieur de celui-ci.
Le processeur, les mémoires et les entrées-sorties communiquent ensemble via un '''réseau d'interconnexions'''. Le terme est assez barbare, mais rien de compliqué sur le principe. C'est juste un ensemble de fils électriques qui relie les différents éléments d'un ordinateur. Les interconnexions sont souvent appelées le bus de communication, mais le terme est un abus de langage, comme on le verra plus bas.
Afin de simplifier les explications, on va supposer que le réseau d'interconnexion est le suivant. Tout est connecté au processeur. Il y a des interconnexions entre le processeur et la mémoire RAM, d'autres interconnexions entre processeur et mémoire ROM, et d'autres entre le processeur et les entrées-sorties. Nous verrons que d'autres réseaux d'interconnexions fusionnent certaines interconnexions, pour les partager entre la ROM et la RAM, par exemple. Mais pour le moment, gardez le schéma ci-dessous en tête.
[[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre]]
==Les mémoires RAM et ROM==
La mémoire est le composant qui mémorise des informations, des données. Dans la majorité des cas, la mémoire est composée de plusieurs '''cases mémoire''', chacune mémorisant plusieurs bits, le nombre de bits étant identique pour toutes les cases mémoire. Dans le cas le plus simple, une case mémoire mémorise un '''octet''', un groupe de 8 bits. Mais les mémoires modernes mémorisent plusieurs octets par case mémoire : elles ont des cases mémoires de 16, 32 ou 64 bits, soit respectivement 2/4/8 octets. De rares mémoires assez anciennes utilisaient des cases mémoires contenant 1, 2, 3, 4, 5, 6 7, 13, 17, 23, 36 ou 48 bits. Mais ce n'était pas des mémoires électroniques, aussi nous allons les passer sous silence.
Tout ce qu'il faut savoir est que la quasi-totalité des mémoires électronique a un ou plusieurs octets par case mémoire. Pour simplifier, vous pouvez imaginer qu'une mémoire RAM est un regroupement de registre, chacun étant une case mémoire. C'est une description pas trop mauvaise pour décrire les mémoires RAM, qu'on abordera dans ce qui suit.
{|class="wikitable"
|+ Contenu d'une mémoire, case mémoire de 16 bits (deux octets)
|-
! Case mémoire N°1
| 0001 0110 1111 1110
|-
! Case mémoire N°2
| 1111 1110 0110 1111
|-
! Case mémoire N°3
| 0001 0000 0110 0001
|-
! Case mémoire N°4
| 1000 0110 0001 0000
|-
! Case mémoire N°5
| 1100 1010 0110 0001
|-
! ...
| ...
|-
! Case mémoire N°1023
| 0001 0110 0001 0110
|-
! Case mémoire N°1024
| 0001 0110 0001 0110
|}
Dans ce cours, il nous arrivera de partir du principe qu'il y a un octet par case mémoire, par souci de simplification. Mais ce ne sera pas systématique. De plus, il nous arrivera d'utiliser le terme adresse pour parler en réalité de la case mémoire associée, par métonymie.
===La capacité mémoire===
Bien évidemment, une mémoire ne peut stocker qu'une quantité finie de données. Et à ce petit jeu, certaines mémoires s'en sortent mieux que d'autres et peuvent stocker beaucoup plus de données que les autres. La '''capacité''' d'une mémoire correspond à la quantité d'informations que celle-ci peut mémoriser. Plus précisément, il s'agit du nombre maximal de bits qu'une mémoire peut contenir. Elle est le produit entre le nombre de cases mémoire, et la taille en bit d'une case mémoire.
Toutes les mémoires actuelles utilisant des cases mémoire d'un ou plusieurs octets, ce qui nous arrange pour compter la capacité d'une mémoire. Au lieu de compter cette capacité en bits, on préfère mesurer la capacité d'une mémoire avec le nombre d'octets qu'elle contient. Mais les mémoires des PC font plusieurs millions ou milliards d'octets. Pour se faciliter la tâche, on utilise des préfixes pour désigner les différentes capacités mémoires. Vous connaissez sûrement ces préfixes : kibioctets, mébioctets et gibioctets, notés respectivement Kio, Mio et Gio.
{|class="wikitable"
|-
!Préfixe!!Capacité mémoire en octets!!Puissance de deux
|-
||Kio||1024||2<sup>10</sup> octets
|-
||Mio||1 048 576||2<sup>20</sup> octets
|-
||Gio||1 073 741 824||2<sup>30</sup> octets
|}
On peut se demander pourquoi utiliser des puissances de 1024, et ne pas utiliser des puissances un peu plus communes ? Dans la majorité des situations, les électroniciens préfèrent manipuler des puissances de deux pour se faciliter la vie. Par convention, on utilise souvent des puissances de 1024, qui est la puissance de deux la plus proche de 1000. Or, dans le langage courant, kilo, méga et giga sont des multiples de 1000. Quand vous vous pesez sur votre balance et que celle-ci vous indique 58 kilogrammes, cela veut dire que vous pesez 58 000 grammes. De même, un kilomètre est égal à 1000 mètres, et non 1024 mètres.
Autrefois, on utilisait les termes kilo, méga et giga à la place de nos kibi, mebi et gibi, par abus de langage. Mais peu de personnes sont au courant de l'existence de ces nouvelles unités, et celles-ci sont rarement utilisées. Et cette confusion permet aux fabricants de disques durs de nous « arnaquer » : Ceux-ci donnent la capacité des disques durs qu'ils vendent en kilo, méga ou giga octets : l’acheteur croit implicitement avoir une capacité exprimée en kibi, mébi ou gibi octets, et se retrouve avec un disque dur qui contient moins de mémoire que prévu.
===Lecture et écriture : mémoires ROM et RWM===
Pour simplifier grandement, on peut grossièrement classer les mémoires en deux types : les ''Read Only Memory'' et les ''Read Write Memory'', aussi appelées mémoires ROM et mémoires RWM. Pour les '''mémoires ROM''', on ne peut pas modifier leur contenu. On peut y récupérer une donnée ou une instruction : on dit qu'on y accède en lecture. Mais on ne peut pas modifier les données qu'elles contiennent. Quant aux '''mémoires RWM''', on peut y accéder en lecture (récupérer une donnée stockée en mémoire), mais aussi en écriture : on peut stocker une donnée dans la mémoire, ou modifier une donnée existante.
Tout ordinateur contient au minimum une ROM et une RWM (souvent une mémoire RAM), les deux n'ont pas exactement le même rôle. Pour simplifier, la mémoire ROM mémorise le programme à exécuter, la mémoire RWM stocke des données. Il a existé des ordinateurs où la mémoire RWM était une mémoire magnétique, voire acoustique, mais ce n'est plus le cas de nos jours. Pour les ordinateurs modernes, la mémoire RWM est une mémoire électronique. Pour faire la différence avec ces anciennes mémoires RWM, elle est appelée la '''mémoire RAM'''. Il s'agit d'une mémoire qui stocke temporairement des données que le processeur doit manipuler (on dit qu'elle est volatile). Elle s'efface complètement quand on coupe l'alimentation de l'ordinateur.
Outre le programme à exécuter, la mémoire ROM peut mémoriser des constantes, des données qui ne changent pas. Elles ne sont jamais modifiées et gardent la même valeur quoi qu'il se passe lors de l'exécution du programme. En conséquence, elles ne sont jamais accédées en écriture durant l'exécution du programme, ce qui fait que leur place est dans une mémoire ROM. La mémoire RWM est alors destinée aux données temporaires, qui changent ou sont modifiées lors de l'exécution du programme, et qui sont donc manipulées aussi bien en lecture et en écriture. La mémoire RWM mémorise alors les variables du programme à exécuter, qui sont des données que le programme va manipuler. Pour les systèmes les plus simples, la mémoire RWM ne sert à rien de plus.
[[File:Espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=2.5|Espaces d'adressage sur une archi harvard modifiée]]
Pour donner un exemple de données stockées en ROM, on peut prendre l'exemple des anciennes consoles de jeu 8 et 16 bits. Les jeux vidéos sur ces consoles étaient placés dans des cartouches de jeu, précisément dans une mémoire ROM à l'intérieur de la cartouche de jeu. La ROM mémorisait non seulement le code du jeu, le programme du jeu vidéo, mais aussi les niveaux et les ''sprites'' et autres données graphiques.
Une conséquence est que les consoles 8/16 bits n'avaient pas besoin de beaucoup de RAM, comparé aux ordinateurs de l'époque, vu qu'une grande partie des données utiles étaient dans une ROM directement accessible par le processeur. À l'opposé, les micro-ordinateurs devaient copier les données d'un jeu depuis une disquette dans la mémoire RAM, ce qui demandait d'avoir plus de RAM. Le passage au support CD sur les consoles 32 bits a eu la même conséquence. Le processeur ne pouvant pas lire directement le CD à sa guise, il fallait copier les données du CD en RAM. D'où l'apparition de temps de chargement assez longs, inexistants sur support cartouche.
===L'adressage mémoire===
Sur une mémoire RAM ou ROM, on ne peut lire ou écrire qu'une case mémoire, qu'un registre à la fois : une lecture ou écriture ne peut lire ou modifier qu'une seule case mémoire. Techniquement, le processeur doit préciser à quel case mémoire il veut accéder à chaque lecture/écriture. Pour cela, chaque case mémoire se voit attribuer un nombre binaire unique, l''''adresse''', qui va permettre de le sélectionner et de l'identifier celle-ci parmi toutes les autres. En fait, on peut comparer une adresse à un numéro de téléphone (ou à une adresse d'appartement) : chacun de vos correspondants a un numéro de téléphone et vous savez que pour appeler telle personne, vous devez composer tel numéro. Les adresses mémoires en sont l'équivalent pour les cases mémoire.
[[File:Adressage mémoire.png|centre|vignette|upright=2|Exemple : on demande à la mémoire de sélectionner la case mémoire d'adresse 1002 et on récupère son contenu (ici, 17).]]
L'adresse mémoire est générée par le processeur. Le processeur peut parfaitement calculer des adresses, en extraire du programme qu'il exécute, et bien d'autres choses. Nous détaillerons d'ailleurs les mécanismes pour dans les chapitres portant sur les modes d'adressage du processeur. Mais pour le moment, nous avons juste besoin de savoir que c'est le processeur qui envoie des adresses aux mémoires RAM et ROM.
Les adresses générées par le processeur sont alors envoyées à la RAM ou la ROM via une connexion dédiée, un ensemble de fils qui connecte le processeur à la mémoire : le '''bus d'adresse mémoire'''. L'adresse sélectionne une case mémoire, le processeur peut alors récupérer la donnée dedans pour une lecture, écrire une donnée pour l'écriture. Pour cela, un second ensemble de fil connecte le processeur à la RAM/ROM, mais cette fois-ci pour échanger des données. Il s'agit du '''bus de données mémoire'''. Les deux sont souvent regroupés sous le terme de '''bus mémoire'''.
Un ordinateur contient toujours une RAM et une ROM, ce qui demande aux bus mémoire de s'adapter à la présence de deux mémoires. Il y a alors deux solutions, illustrées dans les deux schémas ci-dessous. Avec la première, il y a un seul bus mémoire partagé entre la RAM et la ROM, comme illustré ci-dessous. Une autre solution utilise deux bus séparés : un pour la RAM et un autre pour la ROM. Nous verrons les différences pratiques entre les deux à la fin du chapitre. Pour le moment, nous allons partir du principe qu'il y a un bus pour la mémoire ROM, et un autre bus pour la RAM.
[[File:CPT-System-Architecture-gapfill1-ANS.svg|centre|vignette|upright=2|Architecture avec une ROM et une RAM.]]
[[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre]]
===L'alignement mémoire : introduction===
Plus haut, nous avions dit qu'il y a une adresse par case mémoire, chaque case mémoire contenant un ou plusieurs octets. Mais les processeurs modernes partent du principe que la mémoire a un octet par adresse, pas plus. Et ce même si la mémoire reliée au processeur utilise des cases mémoires de 2, 3, 4 octets ou plus. D'ailleurs, la majorité des mémoires RAM actuelle a des cases mémoires de 64 bits, soit 8 octets par case mémoire. Les raisons à cela sont multiple, mais nous les verrons en détail dans le chapitre sur l'alignement mémoire. Toujours est-il qu'il faut distinguer les '''adresses mémoire''' et les '''adresses d'octet''' gérées par le processeur.
Le processeur génère des adresses d'octet, qui permettent de sélectionner un octet bien précis. L'adresse d'octet permet de sélectionner un octet parmi tous les autres. Mais la mémoire ne comprend pas directement cette adresse d'octet. Heureusement, l'octet en question est dans une case mémoire bien précise, qui a elle-même une adresse mémoire bien précise. L'adresse d'octet est alors convertie en une adresse mémoire, qui sélectionne la case mémoire adéquate, celle qui contient l'octet voulu. La case mémoire entière est lue, puis le processeur ne récupère que les données adéquates. Pour cela, des circuits d'alignement mémoire se chargent de faire la conversion entre adresses du processeur et adresse mémoire. Nous verrons cela dans le détail dans le chapitre sur l'alignement mémoire.
Il existe des mémoires qui n'utilisent pas d'adresses mémoire, mais passons : ce sera pour la suite du cours.
==Le processeur==
Dans les ordinateurs, l'unité de traitement porte le nom de '''processeur''', ou encore de '''''Central Processing Unit''''', abrévié en CPU. Le rôle principal du processeur est de faire des calculs. La plupart des processeurs actuels supportent au minimum l'addition, la soustraction et la multiplication. Quelques processeurs ne gèrent pas la division, qui est une opération très gourmande en circuit, peu utilisée, très lente. Il arrive que des processeurs très peu performants ne gèrent pas la multiplication, mais c'est assez rare.
Un processeur ne fait pas que des calculs. Tout processeur est conçu pour effectuer un nombre limité d'opérations bien précises, comme des calculs, des échanges de données avec la mémoire, etc. Ces opérations sont appelées des '''instructions'''. Les plus intuitives sont les '''instructions arithmétiques''', qui font des calculs, comme l'addition, la soustraction, la multiplication, la division. Mais il y a aussi des '''instructions d'accès mémoire''', qui échangent des données entre la mémoire RAM et le processeur. Les autres instructions ne sont pas très intuitives, aussi passons-les sous silence pour le moment, tout deviendra plus clair dans les chapitres sur le processeur.
===Le processeur exécute un programme, une suite d'instructions===
Tout processeur est conçu pour exécuter une suite d'instructions dans l'ordre demandé, cette suite s'appelant un '''programme'''. Ce que fait le processeur est défini par la suite d'instructions qu'il exécute, par le programme qu'on lui demande de faire. Les instructions sont exécutées dans un ordre bien précis, les unes après les autres. L'ordre en question est décidé par le programmeur. La totalité des logiciels présents sur un ordinateur sont des programmes comme les autres.
Le programme à exécuter est stockée dans la mémoire de l'ordinateur. C'est ainsi que l'ordinateur est rendu programmable : modifier le contenu de la mémoire permet de changer le programme exécuté. Mine de rien, cette idée de stocker le programme en mémoire est ce qui a fait que l’informatique est ce qu'elle est aujourd’hui. C'est la définition même d'ordinateur : appareil programmable qui stocke son programme dans une mémoire modifiable.
Une instruction est codée comme les données : sous la forme de suites de bits. Telle suite de bit indique qu'il faut faire une addition, telle autre demande de faire une soustraction, etc. Pour simplifier, nous allons supposer qu'il y a une instruction par adresse mémoire. Sur la grosse majorité des ordinateurs, les instructions sont placées les unes à la suite des autres dans l'ordre où elles doivent être exécutées. Un programme informatique n'est donc qu'une vulgaire suite d'instructions stockée quelque part dans la mémoire de l'ordinateur.
{|class="wikitable"
|+ Exemple de programme informatique
|-
! Adresse
! Instruction
|-
! 0
| Copier le contenu de l'adresse 0F05 dans le registre numéro 5
|-
! 1
| Charger le contenu de l'adresse 0555 dans le registre numéro 4
|-
! 2
| Additionner ces deux nombres
|-
! 3
| Charger le contenu de l'adresse 0555
|-
! 4
| Faire en XOR avec le résultat antérieur
|-
! ...
| ...
|-
! 5464
| Instruction d'arrêt
|}
Pour exécuter une suite d'instructions dans le bon ordre, le processeur détermine à chaque cycle quelle est la prochaine instruction à exécuter. Pour cela, le processeur mémorise l'adresse de l'instruction en cours dans un registre : le '''Program Counter'''. Je rappelle que des instructions consécutives sont dans des adresses consécutives. Pour passer à la prochaine instruction, il suffit donc d'incrémenter le ''program counter''.
: Si une instruction prend plusieurs octets, plusieurs adresses, il suffit de l'incrémenter du nombre d'octets/adresses.
D'autres processeurs font autrement : chaque instruction précise l'adresse de la suivante, directement dans la suite de bit représentant l'instruction en mémoire. Ces processeurs n'ont pas besoin de calculer une adresse qui leur est fournie sur un plateau d'argent. Sur des processeurs aussi bizarres, pas besoin de stocker les instructions en mémoire dans l'ordre dans lesquelles elles sont censées être exécutées. Mais ces processeurs sont très très rares et peuvent être considérés comme des exceptions à la règle.
Nous venons de voir qu'un processeur contient un registre appelé le ''program counter''. Mais il n'est pas le seul. Pour pouvoir fonctionner, tout processeur doit mémoriser un certain nombre d’informations nécessaires à son fonctionnement, qui sont mémorisées dans des '''registres de contrôle'''. La plupart ont des noms assez barbares (registre d'état, ''program counter'') et nous ne pouvons pas en parler à ce moment du cours. Nous les verrons en temps voulu, mais il est important de préciser qu'ils existent.
===L'intérieur d'un processeur===
Fort de ce que nous savons, nous pouvons expliquer ce qu'il y a à l'intérieur d'un processeur. Le premier point est qu'un processeur fait des calculs, ce qui implique qu'il contient des circuits de calcul. Ils sont regroupés dans une ou plusieurs '''unités de calcul'''. Nous avons déjà vu comment fabriquer une unité de calcul simple, dans un chapitre dédié, et c'est la même qui est présente dans un processeur. Du moins dans les grandes lignes, les circuits des processeurs modernes étant particulièrement optimisés. Il en est de même pour les autres circuits de calcul comme ceux pour les multiplications/division/autres.
Si le processeur fait des calculs, qu'en est-il des opérandes ? Et où sont mémorisés les résultats des opérations ? Pour cela, le processeur incorpore des '''registres généraux'''. Les registres généraux servent à mémoriser les opérandes et résultats des instructions. Ils mémorisent des données, contrairement aux registres de contrôle mentionnés plus haut. Le nombre de registres généraux dépend grandement du processeur. Les tout premiers processeurs se débrouillaient avec un seul registre, mais les processeurs actuels utilisent plusieurs registres, pour mémoriser plusieurs opérandes/résultats. Mais la présence de registres est source de pas mal de petites complications. Par exemple, il faut échanger les données entre la RAM et les registres, il faut gérer l'adressage des registres, etc. Il s'agit là de détails que nous expliquerons dans les chapitres sur le processeur.
Le processeur contient enfin un circuit pour interpréter les instructions, appelé l''''unité de contrôle'''. Elle lit les instructions depuis la mémoire, interprète la suite de bit associée, et commande le reste du processeur pour qu'il exécute l'instruction. Ses fonctions sont assez variées, mais nous allons simplifier en disant qu'elle configure l'unité de calcul et les registres pour faire le bon calcul. Son rôle est d'analyser la suite de bit qui constitue l'instruction, et d'en déduire quelle opération effectuer. Elle gère aussi l'accès à la mémoire RAM, et notamment ce qui est envoyé sur son bus d'adresse.
: Dans ce qui suit, on suppose que le ''program counter'' fait partie de l'unité de contrôle.
Pour résumer, un processeur contient une unité de calcul, des registres et une interface avec la mémoire RAM. Le tout est interconnecté, afin de pouvoir échanger des données. L’ensemble forme le '''chemin de données''', nom qui trahit le fait que c'est là que les données se déplacent et sont traitées. Il faut aussi ajouter l'unité de contrôle pour commander le tout. Elle lit les instructions en mémoire, puis commande le chemin de données pour que l'instruction soit exécutée correctement.
[[File:Microarchitecture d'un processeur.png|centre|vignette|upright=2|Microarchitecture d'un processeur]]
Un processeur parait donc assez simple expliqué comme ça, mais il y a de nombreuses subtilités. L'une d'entre elle est liée aux registres, et notamment à la manière dont on les utilise. Pour expliquer ces subtilités, nous allons voir comment les premiers processeurs fonctionnaient, avant de passer aux processeurs un peu plus modernes. Les tout premiers processeurs n'utilisaient qu'un seul registre, ce qui fait que l'utilisation des registres était très simple. Le passage à plusieurs registres a complexifié le tout.
===D'où viennent les adresses ?===
Il est maintenant temps de répondre à une question qui s'était posée dans la section sur l'adressage : d'où viennent les adresses envoyées à la mémoire ? Pour ce qui est des adresses des instructions, on connait déjà la réponse : c'est le ''program counter'' qui gère tout. Mais pour les données, il y a deux possibilités, qui correspondent à deux types de données : les données statiques et les données dynamiques.
Les '''données statiques''' sont les plus simples : elles existent durant toute la durée de vie du programme. Tant que celui-ci s'exécute, il aura besoin de ces données. En conséquence, il leur réserve une place en mémoire, qui est toujours la même. La donnée se situe donc à une adresse bien précise, qui ne change jamais. Attention cependant : les données peuvent être modifiées, changer de valeur. Le programme écrit dans les donnée statiques, c'est même assez fréquent. Ce sont ne sont pas forcément des données constantes !
Pour les données statiques, elles sont toujours placées à la même adresse mémoire. Pour le dire autrement, l'adresse mémoire est une constante, qui ne change pas. L'adresse est connue avant d’exécuter le programme, le programme a été codé pour utiliser cette adresse pour telle donnée, on a réservé une adresse pour la donnée voulue. Et même si vous quittez le programme et vous le relancez plusieurs jours après, l'adresse mémoire sera la même avant et après.
Et cela permet d'utiliser l''''adressage direct'''. L'idée est que les instructions précisent donc l'adresse à lire ou écrire. Pour cela, l'adresse est intégrée dans l’instruction elle-même. Pour rappel, l'instruction est codée par une suite de bit en mémoire RAM/ROM, dont certains précisent l'opération à faire, les autres servant à autre chose. L'idée est que certains bits précisent l'adresse mémoire de la donnée à lire. Les instructions sont donc du genre :
* ''LOAD 56'' - lit l'adresse numéro 56 et copie son contenu dans un registre;
* ''STORE R5 , adress 99'', copie le registre R5 dans l'adresse 99 ;
* ''ADD R5 , adress 209'' : additionne le registre R5 avec le contenu de l'adresse 209.
S'il existe des données statiques, c'est signe qu'il existe des '''données dynamiques'''. Ces dernières sont des données qui sont créées ou détruites selon les besoins. Pour comprendre d'où viennent ces données dynamiques, prenons le cas d'une personne qui écrit du texte sur un ordinateur. Le texte qu'il écrit est mémorisé dans la RAM de l’ordinateur, puis est sauvegardé sur le disque dur quand il sauvegarde son document. Au fur et à mesure qu'il écrit du texte, la RAM utilisée par ce texte augmente. Donc, ce texte est une donnée dynamique, dont la taille varie dans le temps.
Pour gérer des données dynamiques, la plupart des systèmes d'exploitation incorporent des fonctionnalités d''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent réclamer de la mémoire au système d'exploitation, pour y placer les données qu'ils souhaitent. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free().
Avec l'allocation mémoire, les données n'ont pas de place fixe en mémoire. Leur adresse mémoire peut varier d'une exécution du programme à l'autre. Pire que ça : les données peuvent être déplacées dans la mémoire RAM, si besoin. En clair : l'adresse est déterminée lors de l'exécution du programme. L'adresse est alors soit calculée, soit lue depuis la mémoire RAM, soit déterminée autrement. Toujours est-il qu'elle se retrouve dans un registre général. Pour gérer ces adresse variables, les processeurs utilisent l''''adressage indirect'''. L'adressage indirect permet d'utiliser une adresse qui est dans un registre de données. L'adresse en question peut être envoyée à la mémoire RAM sans problème, le processeur peut automatiquement connecter le registre adéquat sur le bus d'adresse.
Au tout début de l'informatique, les processeurs ne supportaient que l'adressage direct, pas plus. L'adressage indirect n'était tout simplement pas possible. Avec l'adressage direct, l'adresse à lire est extraite directement des instructions, par l'unité de contrôle. Le bus d'adresse de la RAM est alors relié directement à l'unité de contrôle, le bus de données est relié aux registres.
[[File:Architecture Harvard avec adressage direct uniquement.png|centre|vignette|upright=2|Architecture Harvard avec adressage direct uniquement]]
Mais dès les années 70, l'adressage indirect est apparu, rendant la programmation bien plus simple. Mais cela a demandé quelques adaptation au niveau du processeur. Avec l'adressage indirect, le bus d'adresse est connecté aux registres. Il a donc fallu rajouter un multiplexeur pour que le processeur décide de relier le bus d'adresse soit aux registres, soit à l'unité de contrôle.
===Un ordinateur peut avoir plusieurs processeurs===
La plupart des ordinateurs n'ont qu'un seul processeur, ce qui fait qu'on désigne avec le terme d''''ordinateurs mono-processeur'''. Mais il a existé (et existe encore) des '''ordinateurs multi-processeurs''', avec plusieurs processeurs sur la même carte mère. L'idée était de gagner en performance : deux processeurs permettent de faire deux fois plus de calcul qu'un seul, quatre permettent d'en faire quatre fois plus, etc. C'est très courant sur les supercalculateurs, des ordinateurs très puissants conçus pour du calcul industriel ou scientifique, mais aussi sur les serveurs ! Dans le cas le plus courant, ils utilisent plusieurs processeurs identiques : on utilise deux processeurs Core i3 de même modèle, ou quatre Pentium 3, etc.
Pour utiliser plusieurs processeurs, les programmes doivent être adaptés. Pour cela, il y a plusieurs possibilités :
* Une première possibilité, assez intuitive, est d’exécuter des programmes différents sur des processeurs différents. Par exemple, on exécute le navigateur web sur un processeur, le lecteur vidéo sur un autre, etc.
* La seconde option est de créer des programmes spéciaux, qui utilisent plusieurs processeurs. Ils répartissent les calculs à faire sur les différents processeurs. Un exemple est la lecture d'une vidéo sur le web : un processeur peut télécharger la vidéo pendant le visionnage et bufferiser celle-ci, un autre processeur peut décoder la vidéo, un autre décoder l'audio. De tels programmes restent des suites d'instructions, mais ils sont plus complexes que les programmes normaux, aussi nous les passons sous silence.
* La troisième option est d’exécuter le même programme sur les différents processeurs, mais chaque processeur traite son propre ensemble de données. Par exemple, pour un programme de rendu 3D, quatre processeurs peuvent s'occuper chacun d'une portion de l'image.
[[File:Architecture de Von Neumann Princeton multi processeurs.svg|centre|vignette|upright=2|Architecture de Von Neumann Princeton multi processeurs]]
De nos jours, les ordinateurs grand public les plus utilisés sont dans un cas intermédiaire, ils ne sont ni mono-, ni multi-processeur. Ils n'ont qu'un seul processeur, dans le sens où si on ouvre l'ordinateur et qu'on regarde la carte mère, il n'y a qu'un seul processeur. Mais ce processeur est en réalité assez similaire à un regroupement de plusieurs processeurs dans le même boitier. Il s'agit de '''processeurs multicœurs''', qui contiennent plusieurs cœurs, chaque cœur pouvant exécuter un programme tout seul.
La différence entre cœur et processeur est assez difficile à saisir, mais pour simplifier : un cœur est l'ensemble des circuits nécessaires pour exécuter un programme. Chaque cœur dispose de toute la machinerie électronique pour exécuter un programme, à savoir des circuits aux noms barbares comme : un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits d'un processeur ne sont présents qu'en un seul exemplaire dans un processeur multicœur, comme les circuits de communication avec la mémoire ou les circuits d’interfaçage avec la carte mère.
Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Un processeur double-cœur est équivalent à avoir deux processeurs dans l'ordinateur, un processeur quadruple-cœur est équivalent à avoir quatre processeurs dans l'ordinateur, etc. Ces processeurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés.
===Les coprocesseurs===
Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs qui complémentaient un processeur principal. Les ordinateurs de ce type avaient un processeur principal, le '''CPU''', qui était secondé par un ou plusieurs coprocesseurs. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle.
Les coprocesseurs les plus connus sont les '''coprocesseurs pour le rendu 2D/3D'''. Ils ont eu leur heure de gloire sur les anciennes consoles de jeux vidéo, comme Super Nintendo, la Playstation et autres consoles de cette génération ou antérieure. Ils s'occupaient respectivement de calculer les graphismes des jeux vidéos. De nos jours, ils ont été remplacés par des cartes graphiques, ou des ''Graphic Processing Units'', qui ne sont pas considérées comme des coprocesseurs.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
L'accès aux périphériques est quelque chose sur lequel nous passerons plusieurs chapitres dans ce cours. Mais sachez que l'accès aux périphériques peut demander pas mal de puissance de calculs. Le CPU principal peut faire ce genre de calculs par lui-même, mais il n'est pas rare qu'un '''coprocesseur d'IO''' soit dédié à l'accès aux périphériques. Un exemple assez récent est celui de la console de jeu Nintendo 3DS. Elle disposait d'un processeur principal de type ARM9, d'un coprocesseur pour les divisions qu'on abordera plus bas, et d'un second processeur ARM7. L'ARM 7 était utilisé comme coprocesseur d'I/O, ainsi que pour l'émulation de la console GBA.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont un peu à part des autres. Ils sont spécialisés dans les calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882.
Un exemple récent de coprocesseur est celui utilisé sur la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques.
==Les entrées-sorties==
Tous les circuits vus précédemment traitent des données codées en binaire. Ceci dit, les données ne sortent pas de n'importe où : l'ordinateur contient des composants électroniques qui traduisent des informations venant de l’extérieur en nombres. Ces composants sont ce qu'on appelle des '''entrées'''. Par exemple, le clavier est une entrée : l'électronique du clavier attribue un nombre entier (''scancode'') à une touche, nombre qui sera communiqué à l’ordinateur lors de l'appui d'une touche. Pareil pour la souris : quand vous bougez la souris, celle-ci envoie des informations sur la position ou le mouvement du curseur, informations qui sont codées sous la forme de nombres. La carte son évoquée il y a quelques chapitres est bien sûr une entrée : elle est capable d'enregistrer un son, et de le restituer sous la forme de nombres.
S’il y a des entrées, on trouve aussi des '''sorties''', des composants électroniques qui transforment des nombres présents dans l'ordinateur en quelque chose d'utile. Ces sorties effectuent la traduction inverse de celle faite par les entrées : si les entrées convertissent une information en nombre, les sorties font l'inverse : là où les entrées encodent, les sorties décodent. Par exemple, un écran LCD est un circuit de sortie : il reçoit des informations, et les transforme en image affichée à l'écran. Même chose pour une imprimante : elle reçoit des documents texte encodés sous forme de nombres, et permet de les imprimer sur du papier. Et la carte son est aussi une sortie, vu qu'elle transforme les sons d'un fichier audio en tensions destinées à un haut-parleur : c'est à la fois une entrée, et une sortie.
Les '''entrées-sorties''' incluent toutes les entrées et sorties, et même certains composants qui sont les deux à la fois. Il s'agit d'un terme générique, qui regroupe des composants forts différents. Dans ce qui va suivre, nous allons parfois parler de périphériques au lieu d'entrées-sorties, mais les deux termes ne sont pas équivalents. Dans le détail, les entrées-sorties regroupent :
* Les '''périphériques''' sont les composants connectés sur l'unité centrale. Exemple : les claviers, souris, webcam, imprimantes, écrans, clés USB, disques durs externes, la Box internet, etc.
* Les '''cartes d'extension''', qui se connectent sur la carte mère via un connecteur, comme les cartes son ou les cartes graphiques.
* D'autres composants sont soudés à la carte mère mais sont techniquement des entrées-sorties : les cartes sons soudées sur les cartes mères actuelles, par exemple.
===L'interface avec le reste de l'ordinateur===
Les entrées-sorties sont très diverses, fonctionnent très différemment les unes des autres. Mais du point de vue du reste de l'ordinateur, les choses sont relativement standardisées. Du point de vue du processeur, les entrées-sorties sont juste des paquets de registres ! Tous les périphériques, toutes les entrées-sorties contiennent des '''registres d’interfaçage''', qui permettent de faire l'intermédiaire entre l'entrée/sortie et le reste de l'ordinateur. L'entrée/sortie est conçu pour réagir automatiquement quand on écrit dans ces registres.
[[File:Registres d'interfaçage.png|centre|vignette|upright=2|Registres d'interfaçage.]]
Les registres d’interfaçage sont assez variés. Les plus évidents sont les '''registres de données''', qui permettent l'échange de données entre le processeur et les périphériques. Pour échanger des données avec l'entrée/sortie, le processeur a juste à lire ou écrire dans ces registres de données. On trouve généralement un registre de lecture et un registre d'écriture, mais il se peut que les deux soient fusionnés en un seul registre d’interfaçage de données. Si le processeur veut envoyer une donnée à une entrée/sortie, il a juste à écrire dans ces registres. Inversement, s'il veut lire une donnée, il a juste à lire le registre adéquat.
Mais le processeur ne fait pas que transmettre des données à l'entrée/sortie. Le processeur lui envoie aussi des « commandes », des valeurs numériques auxquelles l'entrée/sortie répond en effectuant un ensemble d'actions préprogrammées. En clair, ce sont l'équivalent des instructions du processeur, mais pour l'entrée/sortie. Par exemple, les commandes envoyées à une carte graphique peuvent être : affiche l'image présente à cette adresse mémoire, calcule le rendu 3D à partir des données présentes dans ta mémoire, etc. Pour recevoir les commandes, l'entrée/sortie contient des ''registres de commande'' qui mémorisent les commandes envoyées par le processeur. Quand le processeur veut envoyer une commande à l'entrée/sortie, il écrit la commande en question dans ce ou ces registres.
Enfin, beaucoup d'entrée/sortie ont un ''registre d'état'', lisible par le processeur, qui contient des informations sur l'état de l'entrée/sortie. Ils servent notamment à indiquer au processeur que l'entrée/sortie est disponible, qu'il est en train d’exécuter une commande, qu'il est occupé, qu'il y a un problème, qu'il y a une erreur de configuration, etc.
===Les adresses des registres d’interfaçage===
Les registres des périphériques sont identifiés par des adresses mémoires. Et les adresses sont conçues de façon à ce que les adresses des différentes entrées/sorties ne se marchent pas sur les pieds. Chaque entrée/sortie, chaque registre, chaque contrôleur a sa propre adresse. D'ordinaire, certains bits de l'adresse indiquent quel est le destinataire. Certains indiquent quel est l'entrée/sortie voulue, les restants indiquant le registre de destination.
Il existe deux organisations possibles pour les adresses des registres d’interfaçages. La première possibilité est de séparer les adresses pour les registres d’interfaçage et les adresses pour la mémoire. Le processeur doit avoir des instructions séparées pour gérer les périphériques et adresser la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire.
[[File:Espaces d'adressages séparés entre mémoire et périphérique.png|centre|vignette|upright=2.5|Espaces d'adressages séparés entre mémoire et périphérique]]
L'autre méthode mélange les adresses mémoire et des entrées-sorties. Si on prend par exemple un processeur de 16 bits, où les adresses font 16 bits, alors les 65536 adresses possibles seront découpées en deux portions : une partie ira adresser la RAM/ROM, l'autre les périphériques. On parle alors d''''entrées-sorties mappées en mémoire'''. L'avantage est que le processeur n'a pas besoin d'avoir des instructions séparées pour les deux.
[[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]]
Pour résumer, communiquer avec une entrée/sortie est similaire à ce qu'on a avec les mémoires. Il suffit de lire ou écrire dans des registres d’interfaçage, qui ont chacun une adresse mémoire. Le problème est que le système d'exploitation ne connaît pas toujours le fonctionnement d'une entrée/sortie : il faut installer un programme qui va s'exécuter quand on souhaite communiquer avec l'entrée/sortie, et qui s'occupera de tout ce qui est nécessaire pour le transfert des données, l'adressage du périphérique, etc. Ce petit programme est appelé un driver ou '''pilote de périphérique'''. La « programmation » périphérique est très simple : il suffit de savoir quoi mettre dans les registres, et c'est le pilote qui s'en charge.
==Les architectures Harvard et Von Neumann==
Après avoir vu le processeur, les mémoires et les entrées-sorties, voyons voir comment le tout est interconnecté. Tous les ordinateurs ne sont pas organisés de la même manière, pour ce qui est de leurs bus. Mais pour comprendre pourquoi, nous devons regarder qui communique avec qui, dans un ordinateur. Pour rappel, les données sont placées en mémoire RAM, alors que les instructions sont placées en mémoire ROM. Le processeur lit des instructions dans la mémoire ROM, il lit et écrit dans la mémoire RAM, et accède aux registres d’interfaçage des entrées-sorties. Il y a donc besoins de trois interconnexions : CPU-ROM, CPU-RAM et CPU-IO.
[[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre]]
Il parait intéressant d'utiliser trois interconnexions, au minimum CPU-ROM, CPU-RAM et CPU-IO. Néanmoins, faire ainsi a de nombreux désavantages. Déjà, il faut pouvoir brancher tout ça sur le processeur. Et celui-ci n'a pas forcément assez de broches pour. Aussi, il est parfois préférable de mutualiser des bus, à savoir de connecter plusieurs composants sur un même bus. Par exemple, on peut mutualiser le bus pour la mémoire RAM et pour la mémoire ROM. Il faut dire que les deux bus sont des bus mémoire, avec un bus d'adresse, un bus de données, et surtout : des bus de commande similaires. Les mutualiser est alors très simple, et permet d'économiser pas mal de broches.
[[File:Réseau d'interconnexion avec un processeur au centre et une architecture Harvard.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre et une architecture Harvard]]
Cette mutualisation nous amène naturellement à parler de la distinction entre les architectures Harvard d'un côté et les architectures Von Neumann de l'autre. Elle est très liée au fait d'utiliser soit un bus mémoire unique, soit des bus séparés pour la ROM et la RAM. Voyons cela en détail.
===Les architectures Harvard et Von Neumann : des bus séparés ou unifiés===
Avec l''''architecture Harvard''', la mémoire ROM et la mémoire RAM sont reliées au processeur par deux bus séparés. Il y a un bus RAM pour la mémoire RAM, un bus ROM pour la mémoire ROM. L'avantage de cette architecture est qu'elle permet de charger une instruction et une donnée simultanément : une instruction chargée sur le bus relié à la mémoire programme, et une donnée chargée sur le bus relié à la mémoire de données. Et cela simplifie fortement la conception du processeur.
[[File:Harvard Architecture.png|centre|vignette|upright=2|Architecture Harvard, avec une ROM et une RAM séparées.]]
Avec l''''architecture Von Neumann''', mémoire ROM et mémoire RAM sont reliées au processeur par un bus unique. Le bus unique qui relie processeur, RAM et ROM, s'appelle le '''bus mémoire'''. Un défaut de ces architecture est qu'elles ne peuvent pas charger une instruction et une donnée en même temps. Et cela pose quelques problèmes pour la conception du processeur. Par contre, nous verrons dans ce qui suit qu'utiliser un bus mémoire partagé est bien plus flexible et permet des choses que les architectures Harvard ne peuvent pas faire.
[[File:Architecture Von Neumann, avec deux bus séparés.png|centre|vignette|upright=2|Architecture Von Neumann, avec deux bus séparés.]]
===Les architectures Harvard et Von Neumann : des espaces d'adressage séparés ou unifiés===
La distinction précédente se base sur les connexions entre RAM, ROM et processeur. Mais il existe une autre distinction, très liée, qui est souvent utilisée comme seconde définition des architectures Harvard/Von Neumann. Elle est liée aux adresses mémoire que le processeur peut gérer. Prenons un processeur 16 bits, par exemple, qui gère naturellement des adresses de 16 bits. Il peut gérer 2^16 adresses, soit 64 kibioctets de mémoire. L'ensemble de ces adresses est appelé un '''espace d'adressage'''. Mais comment cet espace d'adressage est utilisé pour adresser une RAM et une ROM ?
Sur les architectures Harvard, le processeur voit deux mémoires séparées avec leur lot d'adresses distinctes. Une même adresse peut donc correspondre soit à la mémoire ROM, soit à la mémoire RAM, suivant le bus utilisé. L'espace d'adressage est donc doublé, dupliqué, avec un pour la ROM, un autre pour la RAM. Rien d'étonnant à cela : il y a deux bus d'adresses, chacun correspondant à un espace d'adressage.
[[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]]
Avec l'architecture Von Neumann, la RAM et la ROM doivent se partager les adresses mémoires disponibles. Il n'y a qu'un seul espace d'adressage qui est coupé en deux, avec une partie pour la ROM et une autre pour la RAM. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM occupe une partie des adresses, la mémoire RAM utilise le reste. La répartition des adresses est réalisée par les circuits de décodage d'adresse mentionnés plus haut.
[[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]]
Les '''architectures Harvard modifiées''' sont des intermédiaires entre architectures Harvard et architectures Von Neumann, bien qu'elles penchent bien plus du côté des architectures Harvard. Précisons que la terminologie n'est pas claire, beaucoup d'auteurs mettent des définitions différentes derrière ces deux termes. Mais dans ce cours, nous utiliserons une définition très stricte de ce qu'est une architecture Harvard modifiée.
Une architecture Harvard modifiée est une architecture Harvard, où le processeur peut lire des données constantes depuis la mémoire ROM. Nous avions vu plus haut que les mémoires ROM peuvent mémoriser, en plus d'un programme exécutable, des données constantes, qui ne varient pas. Les architectures Harvard pures ne permettent pas de lire des données de ce genre depuis la mémoire ROM, alors que les architectures Harvard modifiées le permettent.
Une architecture Harvard modifiée dispose d'une instruction pour lire les données en mémoire RWM, et d'une instruction pour lire des données en mémoire ROM. Il y a donc deux versions de l'instruction LOAD, qui copient la donnée dans un registre général, mais dont la source de la donnée est différente. Une autre possibilité, plus rare, est que une instruction de copie, qui copie une constante depuis la mémoire ROM vers la mémoire RAM. Le cas le plus commun est l'utilisation de deux instructions LOAD séparées.
[[File:Espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=2.5|Espaces d'adressage sur une archi harvard modifiée]]
Ceci étant dit, revenons à la distinction entre architecture Harvard et Von Neumann. Il faut noter que la RAM et la ROM n'ont pas forcément la même taille. Et ce que ce soit sur une architecture Harvard que sur une architecture Von Neumann, mais c'est plus facile à expliquer sur une architecture Harvard.
On peut par exemple imaginer une architecture Harvard qui utilise des adresses de 16 bits pour la ROM, et seulement 8 bits pour la RAM. Le résultat est qu'il peut adresser 64 kibioctets de ROM, mais seulement 256 octets de RAM. Les deux bus d'adresse sont alors de taille différente, l'un faisant 8 bits, l'autre 16. Quelques processeurs 8 bits étaient dans ce cas, comme on le verra dans le chapitre sur les CPU 8bits. Mais d'autres processeurs utilisent des valeurs différentes, avec par exemple des adresses de 16 bits pour la RAM, mais de 20 bits pour la ROM, ou inversement.
Sur une architecture Von Neumann, tout dépend de comment les adresses sont réparties. La solution la plus simple découpe l'espace d'adressage en deux parties égales, avec la RAM qui est dans la moitié basse (qui part de l'adresse 0 jusqu'à l'adresse au milieu), alors que la ROM est dans la moitié haute (entre l'adresse du milieu et l'adresse maximale). Mais ce n'est pas la seule possibilité, la limite entre RAM et ROM peut être mise n'importe où. Prenons par exemple un processeur 32 bits, capable de gérer 4 milliards d'adresse. Il est parfaitement possible de réserver 128 mébioctets de poids fort à la mémoire ROM, et de laisser le reste à la mémoire RAM.
===Le décodage d'adresse sur les architectures Von Neumann===
Pour résumer, les architectures Harvard et Von Neumann se distinguent sur deux points :
* L'accès à la RAM et à la ROM se font par des bus séparés sur l'architecture Harvard, sur le même bus avec l'architecture Von Neumann.
* Les adresses pour la mémoire ROM et la mémoire RAM sont séparées sur les architectures Harvard, partagées sur l’architecture Von Neumann.
Les architectures Von Neumann utilisent donc un seul bus pour connecter la RAM et la ROM au processeur. Mais cela ne parait pas intuitif : comment deux composants peuvent se connecter aux mêmes fils ? Parce que c'est ce qu'implique le fait de partager un bus. Si je prends une mémoire RAM et une mémoire ROM, toutes deux de 8 bits, elles seront connectées à un bus mémoire de 8 bits. Intuitivement, on se dit qu'il y aura des conflits, du genre : la RAM et la ROM vont accéder au bus en même temps, comment savoir si une adresse est destinée à la RAM ou la ROM, etc ?
Tous ces problèmes sont résolus avec une solution très simple : à chaque instant, seule une mémoire est connectée au bus. L'idée est que les mémoires sont connectées ou déconnectées du bus selon les besoins. Si le processeur veut envoyer lire une donnée en mémoire RAM, il déconnecte la mémoire ROM du bus. Et inversement, s'il veut lire une instruction, il déconnecte la RAM et connecte la ROM.
Pour cela, les mémoires RAM et ROM possèdent une entrée ''Chip Select'' ou ''Output Enable'', qui agit comme une sorte de bouton ON/OFF. Lorsqu'on met un 1 sur cette entrée, la mémoire se connectera au bus. Ses entrées et sorties fonctionneront normalement, elle pourra recevoir des adresses, envoyer ou recevoir des données, tout sera normal. Par contre, si on met un 0 sur cette entrée, la mémoire se "désactive", ses entrée-sorties ne répondent plus aux sollicitations extérieures. Pire que ça : elles sont électriquement déconnectées.
Au total, tout cela demande de gérer deux bit ''Chip Select''/''Output Enable'' : un pour la RAM, un pour la ROM. Et ces deux bits sont configurés pour chaque accès mémoire, pour chaque lecture ou écriture. Pour cela, un circuit de '''décodage d'adresse''' prend en entrée l'adresse mémoire à lire/écrire, et active/désactive les mémoires RAM/ROM selon les besoins. Il prend l'adresse et configure les bits ''Chip Select''/''Output Enable''.
[[File:Décodage d'adresse sur une architecture Von Neumann.png|centre|vignette|upright=2|Décodage d'adresse sur une architecture Von Neumann.]]
L'implémentation la plus simple réserve la moitié des adresses pour la RAM, l'autre moitié pour la ROM. Typiquement, la ROM prend la moitié basse, la RAM la moitié haute. Dans ce cas, activer/désactiver la RAM et la ROM se fait avec seulement le bit de poids fort de l'adresse. Si le bit de poids fort est à 1, alors on accède à la RAM et la ROM doit être désactivée. Mais si ce bit est à 0, alors on accède à la moitié basse et il faut désactiver la RAM.
Une remarque intéressante : le fait de séparer la mémoire en deux parts égales permet de simuler une architecture Harvard à partir d'une architecture Von Neumann. Par exemple, le tout premier processeur d'Intel, le 4004, était l'un de ceux là. La RAM et la ROM sont reliés au même bus, et il y a donc un unique espace d'adressage, qui est séparé en deux parties égales. Le truc est que le processeur traite les deux parties égales comme deux espaces d'adressage séparés. Le processeur se débrouille pour cacher le fait qu'il y a un espace d'adressage unique coupé en deux, ce qui fait que les programmeurs voient bien deux espaces d'adressages distincts.
[[File:Décodage d'adresse sur une architecture Von Neumann basique.png|centre|vignette|upright=2|Décodage d'adresse sur une architecture Von Neumann basique.]]
Pour résumer, quand une adresse est envoyée sur le bus, les deux mémoires vont la recevoir mais une seule va répondre et se connecter au bus. Le décodage d'adresse garantit que seule la mémoire adéquate réponde à un accès mémoire. Le décodage d'adresse est réalisé par la carte mère, par un composant dédié.
Le mécanisme peut être utilisé pour combiner plusieurs RAM en une seule, idem avec les ROM. Pour comprendre l'idée, je vais prendre l'exemple de l'IBM PC, un des tout premier PC existant. Nous étudierons ce PC dans une section dédiée, à la fin du chapitre, aussi je vais passer rapidement dessus. Tout ce que je vais faire est vous présenter la carte mère du PC, et vous demander de faire est de compter les mémoires ROM et mémoires RAM sur la carte mère :
[[File:IBM 5150 Motherboard.svg|centre|vignette|upright=3|Carte mère de l'IBM 5150, un modèle de l'IBM PC.]]
Si vous remarquerez qu'il y a 5 mémoires ROM et 8 à 32 mémoires RAM. Le fait est que le processeur voit les différentes mémoires ROM comme une seule mémoire ROM. Idem avec les mémoires RAM : elle font chacune 2 kibioctets, et l'ensemble est vu par le processeur comme une seule RAM de 16 à 64 kibioctets. Et cela grâce aux circuits de décodage d'adresse, qui sont situés en haut à droite de la carte mère.
Pour comprendre l'idée, prenons l'exemple d'un processeur 16 bits, capable de gérer 64 kibioctets de mémoire. L'espace d'adressage est découpé en quatre portions, de 16 kibioctets chacune. Une portion est réservée à une ROM de 16 kibioctet, les autres sont chacune réservée à une RAM de 16 kibioctet. Le décodage d'adresse sélectionne alors la mémoire adéquate en utilisant les deux bits de poids fort de l'adresse.
* S'ils valent 00, alors c'est la mémoire ROM qui est activée, connectée au bus.
* S'ils valent 01, alors c'est la première mémoire RAM qui est connectée au bus.
* S'ils valent 10, alors c'est la seconde mémoire RAM qui est connectée au bus.
* S'ils valent 11, alors c'est la troisième mémoire RAM qui est connectée au bus.
[[File:Décodage d'adresse sur une architecture Von Neumann, utilisant plusieurs RAM et une ROM.png|centre|vignette|upright=3|Décodage d'adresse sur une architecture Von Neumann, utilisant plusieurs RAM et une ROM]]
===L'impact sur la conception du processeur===
Plus haut, j'ai parlé d'un des avantages des architectures Harvard : elles peuvent lire une instruction en même temps qu'elles accèdent à une donnée. La donnée est lue/écrite en RAM, alors que l'instruction est lue en ROM. Et cela permet de simplifier l'intérieur du processeur. Pas de beaucoup, mais c'est déjà ça de pris. Voyons maintenant comment cela impacte l'intérieur du processeur. Tout ce dont vous avez à vous rappeler est la séparation entre chemin de données et unité de contrôle, et que les registres généraux sont dans le premier, le ''program counter'' dans la seconde.
Avec une architecture Harvard, les instructions et les données passent par des bus différent : bus ROM pour les instructions, bus RAM pour les données. L'intuition nous dit que le bus pour la mémoire ROM est connecté à l'unité de contrôle, alors que le bus pour la RAM est connecté au chemin de données. Et dans les grandes lignes, c'est vrai. La logique est imparable pour ce qui est des bus de données. Mais il y a une petite subtilité pour les bus d'adresse.
Pour comprendre comment le processeur exploite ces deux bus, voyons ce qui transite dessus. Pour la mémoire ROM, elle reçoit l'adresse de l'instruction à lire, elle renvoie l'instruction adéquate. Pour cela, le ''program counter'' est envoyé sur le bus d'adresse, l'instruction sur le bus de données. Pour la mémoire RAM, elle échange des données avec les registres généraux, les registres pour les données. Les adresses utilisées pour la RAM viennent elles soit du chemin de données, soit de l'unité de contrôle, tout dépend du mode d'adressage. Mais le ''program counter'' n'est pas impliqué.
[[File:Architecture Harvard - échanges de données.png|centre|vignette|upright=2|Architecture Harvard - échanges de données]]
Les architectures Harvard modifiées doivent cependant rajouter une connexion entre le bus ROM et les registres généraux. C'est nécessaire pour charger une donnée constante depuis la mémoire ROM. Rappelons que la donnée constante est copiée dans un registre général, donc dans le chemin de données.
[[File:Architecture Harvard modifiée - implémentation du processeur.png|centre|vignette|upright=2|Architecture Harvard modifiée - implémentation du processeur]]
Avec les architectures Von Neumann, il y a un seul bus qui est relié à la fois au chemin de données et à l'unité de contrôle. Si le processeur lit une instruction, le bus doit être relié à l'unité de contrôle. Par contre, s'il accède à une donnée, il doit être relié au chemin de données (le bus d'adresse peut éventuellement être connecté au séquenceur, si celui-ci fournit l'adresse à lire). Il faut donc utiliser un paquet de multiplexeurs et de démultiplexeurs pour faire la connexion au bon endroit.
[[File:Architecture Von Neumann - implémentation du processeur.png|centre|vignette|upright=2|Architecture Von Neumann - implémentation du processeur]]
Une instruction se fait en deux temps : on charge l'instruction depuis la mémoire ROM, puis on l'exécute. Avec une architecture Harvard, tout cela se fait en un seul cycle d'horloge, vu que charger la ROM et accéder aux données peut se faire en même temps. Pas avec les architectures Von Neumann, qui doivent libérer le bus mémoire après avoir chargé une instruction. Elles n'ont pas le choix : elles chargent l'instruction lors d'un premier cycle d'horloge, puis l'exécutent lors du second.
Pour cela, ils incorporent un registre appelé le '''registre d'instruction''', qui mémorise l'instruction chargée. L'instruction est copiée dans ce registre lors du premier cycle, puis est utilisée lors du second cycle. Le registre permet de ne pas oublier l’instruction entre les deux cycles. Le registre d'instruction est obligatoire sur les architectures Von Neumann. En comparaison, il est facultatif sur les architectures Harvard. Elles peuvent en avoir un, pour des raisons techniques, mais ce n'est pas obligatoire.
[[File:Registre d'instruction.png|centre|vignette|upright=2|Registre d'instruction.]]
===Les architectures Von Neumann sont plus flexibles===
Sur les architectures Harvard, le processeur sait faire la distinction entre programme et données. Les données sont stockées dans la mémoire RAM, le programme est stocké dans la mémoire ROM. Les deux sont séparés, accédés par le processeur sur des bus séparés, et c'est ce qui permet de faire la différence entre les deux. Il est impossible que le processeur exécute des données ou modifie le programme. Du moins, tant que la mémoire qui stocke le programme est bien une ROM.
Par contre, sur les architectures Von Neumann, il est impossible de distinguer programme et données, sauf en ajoutant des techniques de protection mémoire avancées. La raison est qu'il est impossible de faire la différence entre donnée et instruction, vu que rien ne ressemble plus à une suite de bits qu'une autre suite de bits. Et c'est à l'origine d'un des avantages majeur de l'architecture Von Neumann : il est possible que des programmes soient copiés dans la mémoire RWM et exécutés dans celle-ci.
Un cas d'utilisation familier est celui de votre ordinateur personnel. Le système d'exploitation et les autres logiciels sont copiés en mémoire RAM à chaque fois que vous les lancez. Mais cet exemple implique un disque dur, ce qui rend les choses plus compliquées que prévu. Un autre exemple serait la compilation de code à la volée, mais il ne sera pas très parlant.
Un exemple plus adapté serait celui où la ROM mémorise un programme compressée dans la mémoire ROM, qui est décompressé pour être exécuté en mémoire RAM. Le programme de décompression est stocké en mémoire ROM et est exécuté au lancement de l’ordinateur. Cette méthode permet d'utiliser une mémoire ROM très petite et très lente, tout en ayant un programme rapide (si la mémoire RWM est rapide).
Il est aussi possible de créer des programmes qui modifient leurs propres instructions : cela s'appelle du '''code auto-modifiant'''. Ce genre de choses servait autrefois sur des ordinateurs rudimentaires, au tout début de l'informatique. A l'époque, les adresses à lire/écrire devaient être écrites en dur dans le programme, dans les instructions exécutées. Pour gérer certaines fonctionnalités des langages de programmation qui ont besoin d'adresses modifiables, comme les tableaux, on devait corriger les adresses au besoin avec du code auto-modifiant. De nos jours, le code automodifiant est utilisée occasionnellement pour rendre un programme indétectable dans la mémoire (les virus informatiques utilisent beaucoup ce genre de procédés).
L'impossibilité de séparer données et instructions est à l'origine de problèmes assez fâcheux. Il est parfaitement possible que le processeur charge et exécute des données, qu'il prend par erreur pour des instructions. C'est le cas quand des pirates informatiques arrivent à exploiter des bugs. Il arrive que des pirates informatiques vous fournissent des données corrompues, qui contiennent un virus ou un programme malveillant est caché dans les données. Les bugs en question permettent d'exécuter ces données, donc virus. Pour éviter cela, le système d'exploitation peut marquer certaines zones de la mémoire comme non-exécutable, c’est-à-dire que le système d'exploitation interdit d’exécution de quoi que ce soit qui est dans cette zone. Mais ce n'est pas parfait.
Toujours est-il que tout cela est impossible sur les architectures Harvard. Et ce serait très limitant. Imaginez : pas possible de lancer un programme depuis le disque dur ou une clé USB, le programme doit impérativement être dans une mémoire ROM, pas de compilation à la volée, etc. Que des techniques très utilisées dans l'informatique moderne. Malgré ses défauts, les architectures Von Neumann ne sont pas les plus utilisées pour rien. Les architectures Harvard sont concrètement utilisées uniquement dans l'informatique embarquée, sur des microcontrôleurs très spécifiques.
==Le bus de communication avec les entrées-sorties==
Le processeur, la mémoire et les entrées-sorties sont connectées par un ou plusieurs '''bus de communication'''. Ce bus n'est rien d'autre qu'un ensemble de fils électriques sur lesquels on envoie des zéros ou des uns. Pour communiquer avec la mémoire, il y a trois prérequis qu'un bus doit respecter : pouvoir sélectionner la case mémoire (ou l'entrée-sortie) dont on a besoin, préciser à la mémoire s'il s'agit d'une lecture ou d'une écriture, et enfin pouvoir transférer la donnée. Pour cela, on doit donc avoir trois bus spécialisés, bien distincts, qu'on nommera le bus de commande, le bus d'adresse, et le bus de donnée.
* Le '''bus de données''', sur lequel s'échangent les données entre les composants.
* Le '''bus de commande''' pour configurer la mémoire et les entrées-sorties.
* Le '''bus d'adresse''', facultatif, permet de préciser quelle adresse mémoire il faut lire/écrire.
Chaque composant possède des entrées séparées pour le bus d'adresse, le bus de commande et le bus de données. Par exemple, une mémoire RAM possédera des entrées sur lesquelles brancher le bus d'adresse, d'autres sur lesquelles brancher le bus de commande, et des broches d'entrée-sortie pour le bus de données. Précisons cependant que le bus de commande n'est pas exactement le même entre des mémoires RAM/ROM et des entrées-sorties.
[[File:Bus general schematic.svg|centre|vignette|upright=2|Contenu d'un bus, généralités.]]
===Le réseau d'interconnexion : généralités===
Reprenons où nous nous étions arrêté. Avant de voir les architectures Harvard et Von Neumann, nous avions dit que le processeur, les mémoires et les entrées-sorties sont reliées entre eux par un réseau d'interconnexion. Nous venons de voir qu'il est possible de mutualiser certains bus, notamment celui de la mémoire RAM et celui de la mémoire ROM. Mais il est possible de faire la même chose pour les entrées-sorties. Là encore, il est possible de regrouper le bus mémoire avec les bus pour les entrées-sorties. Voyons ce que cela implique.
{|
|[[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec une architecture Harvard.]]
|[[File:Réseau d'interconnexion avec un processeur au centre et une architecture Harvard.png|centre|vignette|upright=2|Interconnexions d'une architecture Von Neumann.]]
|}
Avant de poursuivre, nous devons préciser quelque chose d'important. Sur les ordinateurs modernes, les entrées-sorties peuvent accéder à la mémoire RAM. Les ordinateurs modernes intègrent des techniques de '''''Direct Memory Access''''' (DMA) qui permettent aux entrées-sorties de lire ou d'écrire en mémoire RAM. Les transferts DMA se font sans intervention du processeur. Ils permettent de copier un bloc de plusieurs octets, dans deux sens : de la mémoire RAM vers une entrée-sortie, ou inversement. Le DMA demande d'ajouter un circuit dédié sur la carte mère : le contrôleur DMA. Il effectue la copie d'un paquet d'octets de la RAM vers l'entrée-sortie ou dans l'autre sens.
[[File:Réseau d'interconnexion avec un processeur au centre, et direct memory access.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre, et direct memory access]]
===Les bus systèmes===
La première solution utilise un bus unique, celui-ci est appelé le '''bus système''', aussi appelé ''backplane bus''. Le bus système est connecté à la mémoire RAM, la mémoire ROM, au processeur, et aux entrées-sorties. Tous les composants présents dans l'ordinateur sont connectés à ce bus, sans exception. De tels bus avaient pour avantage la simplicité. Le processeur n'est connecté qu'à un seul bus, ce qui utilise peu de broches et économise des fils. La mutualisation des bus est totale, le câblage est plus simple, la fabrication aussi.
[[File:Architecture minimale d'un ordinateur.png|centre|vignette|upright=2|Architecture minimale d'un ordinateur.]]
Un bus système contient un bus d'adresse, de données et de commande. Un bus système se marie bien avec des entrées-sorties mappées en mémoire. La conséquence est que le bus d'adresse ne sert pas que pour l'accès à la mémoire RAM/ROM, mais aussi pour l'accès aux entrées-sorties. Il y a moyen d'implémenter un système d'adresse séparés avec, mais c'est pas l'idéal.
[[File:Architecture Von Neumann avec les bus.png|centre|vignette|upright=2|Architecture Von Neumann avec les bus.]]
Un bus système n'a pas de limitations quant aux échanges de données. Le processeur peut communiquer directement avec les mémoires et les entrées-sorties, les entrées-sorties peuvent communiquer avec la mémoire RAM, etc. Notamment, un bus système peut implémenter le ''Direct Memory Access''. Il suffit juste de connecter un contrôleur DMA sur le bus système. Le contrôleur DMA est considéré comme une entrée-sortie, ses registres sont mappés en mémoire et sont donc accessibles directement par le processeur.
[[File:Bus système avec controleur DMA.png|centre|vignette|upright=2|Bus système avec contrôleur DMA.]]
Si on suit la définition à la lettre, un bus système est systématiquement une architecture Von Neumann, vu que la mémoire ROM et la mémoire RAM sont reliées sur le bus système. La conséquence est que les circuits de décodage d'adresse sont présents. Ils sont toujours sur la carte mère, et sont plus ou moins à côté du bus système. Cependant, le décodage d'adresse est parfois étendu pour tenir compte des entrées-sorties.
Les entrées-sorties soudées sur la carte mère ont elles aussi des entrées ''Chip Select'' ou quelque chose de similaire. Le décodage d'adresse peut alors les activer ou les désactiver suivant l'adresse envoyée sur le bus d'adresse. C'est ce qui arrive quand le processeur écrit dans un registre d’interfaçage : il envoie l'adresse de ce registre sur le bus d'adresse, le circuit de décodage d'adresse active seulement l'entrée-sortie associée.
Il faut noter que ce n'est pas systématique, il existe des techniques pour se passer de décodage d'adresse. Mais nous en reparlerons dans le chapitre sur les bus de communication.
[[File:Chipselectfr.png|centre|vignette|upright=1.5|Exemple détaillé.]]
Les bus systèmes sont certes très simples, mais ils ont aussi des désavantages. Par exemple, il faut éviter que le processeur et les entrées-sorties se marchent sur les pieds, ils ne peuvent pas utiliser le bus en même temps. De tels conflits d'accès au bus système sont fréquents et ils réduisent la performance, comme on le verra dans le chapitre sur les bus. De plus, un bus système a le fâcheux désavantage de relier des composants allant à des vitesses très différentes : il arrivait fréquemment qu'un composant rapide doive attendre qu'un composant lent libère le bus. Le processeur était le composant le plus touché par ces temps d'attente.
Elle était utilisée sur les tout premiers ordinateurs, pour sa simplicité. Elle était parfaitement adaptée aux anciens composants, qui allaient tous à la même vitesse. De nos jours, les ordinateurs à haute performance ne l'utilisent plus trop, mais elle est encore utilisée sur certains systèmes embarqués, en informatique industrielle dans des systèmes très peu puissants.
===Les bus d'entrées-sorties===
Les bus systèmes ont de nombreux problèmes, ce qui fait que d'anciens ordinateurs faisaient autrement. A la place d'un bus système unique, ils utilisent un bus séparé pour les mémoires, et un autre séparé pour les entrées-sorties. Le bus spécialisé pour la mémoire est appelé le '''bus mémoire''', l'autre bus est appelé le '''bus d'entrées-sorties'''. Le bus mémoire est généralement relié à la fois à la mémoire RAM et à la mémoire ROM, les exceptions ne sont pas rares, cependant.
[[File:Bus mémoire séparé du bus pour les IO.png|centre|vignette|upright=2|Bus mémoire séparé du bus pour les IO]]
Les bus d'entrée-sorties peuvent être spécialisés et simplifiés. Par exemple, ils peuvent avoir un bus de commande différent de celui de la mémoire, qui utilise nettement moins de fils. Le bus d'adresse peut aussi être réduit, et utiliser des adresses plus courtes que celles du bus mémoire. Les bus de données peuvent aussi être de taille différentes. Il est ainsi possible d'avoir un bus mémoire capable de lire/écrire 64 bits à la fois, alors que la communication avec les entrées-sorties se fait octet par octet ! En général, les bus d'entrée-sortie sont assez petits, ils ont une taille de 8 ou 16 bits, même si le bus mémoire est plus grand. Cela permet de ne pas gaspiller trop de broches. Ajouter un bus d'entrée-sortie n'est donc pas très gourmand en broches et en fils.
: Il est en théorie possible d'avoir une fréquence différente pour les deux bus, avec un bus mémoire ultra-rapide et un bus pour les entrées-sorties est un bus moins rapide. Mais il faut que le processeur soit prévu pour, et c'est très rare.
Niveau performances, le processeur peut théoriquement accéder à la mémoire en attendant qu'une entrée/sortie réponde, mais il faut que le processeur soit prévu pour, et ce n'est pas de la tarte. Par contre, cela implique d'avoir des adresses séparées pour les registres d’interfaçage et la mémoire. En clair : pas d'entrée-sortie mappée en mémoire ! Un autre problème est que les entrées-sorties ne peuvent pas communiquer avec la mémoire directement, elles doivent passer par l'intermédiaire du processeur. En clair : pas de ''Direct Memory Access'' ! Les deux sont des défauts rédhibitoires pour les programmeurs système, notamment pour ceux qui codent les pilotes de périphériques.
Pour résumer, les défauts sont assez problématiques : pas d'entrées-sorties mappées en mémoire, pas de ''Direct Memory Access'', économie de broches limitée. Les deux premiers sont des défauts majeurs, qui font que de tels bus ne sont pas utilisés dans les ordinateurs modernes. A la place, ils utilisent une troisième solution, distincte des bus systèmes et des bus d'entrée-sorties.
===Les bus avec répartiteur===
Il existe une méthode intermédiaire, qui garde deux bus séparés pour la mémoire et les entrées-sorties, mais élimine les problèmes de brochage sur le processeur. L'idée est d'intercaler, entre le processeur et les deux bus, un '''circuit répartiteur'''. Il récupère tous les accès et distribue ceux-ci soit sur le bus mémoire, soit sur le bus des périphériques. Le ou les répartiteurs s'appellent aussi le '''''chipset''''' de la carte mère.
C'était ce qui était fait à l'époque des premiers Pentium. À l'époque, la puce de gestion du bus PCI faisait office de répartiteur. Elle mémorisait des plages mémoires entières, certaines étant attribuées à la RAM, les autres aux périphériques mappés en mémoire. Elles utilisaient ces plages pour faire la répartition.
[[File:IO mappées en mémoire avec séparation des bus.png|centre|vignette|upright=2|IO mappées en mémoire avec séparation des bus]]
Niveau adresses des registres d'interfacage, il est possible d'avoir soit des adresses unifiées avec les adresses mémoire, soit des adresses séparées.
L'usage d'un répartiteur ne pose pas de problèmes particuliers pour implémenter le DMA. La seule contrainte est que le contrôleur DMA soit intégré dans le répartiteur. Les échanges entre IO et mémoire passent par le répartiteur, qui fait le pont entre bus mémoire et bus des IO.
[[File:Implémentation du DMA avec un répartiteur.png|centre|vignette|upright=2|Implémentation du DMA avec un répartiteur]]
==Les microcontrôleurs et ''system on chip''==
Parfois, on décide de regrouper la mémoire, les bus, le CPU et les ports d'entrée-sortie dans un seul circuit intégré, un seul boitier. L'ensemble forme alors ce qu'on appelle un '''''System on Chip''''' (système sur une puce), abrévié en SoC. Le nom est assez explicite : un SoC comprend un système informatique complet sur une seule puce de silicium, microprocesseurs, mémoires et périphériques inclus. Ils incorporent aussi des ''timers'', des compteurs, et autres circuits très utiles.
[[File:ARMSoCBlockDiagram.svg|centre|vignette|upright=2|SoC basé sur un processeur ARM, avec des entrées-sorties typiques de celles d'un µ-contrôleur. Le support du bus CAN, d'Ethernet, du bus SPI, d'un circuit de PWM (génération de signaux spécifiques), de convertisseurs analogique-digital et inverse, sont typiques des µ-contrôleurs.]]
Le terme SoC regroupe des circuits imprimés assez variés, aux usages foncièrement différents et à la conception distincte. Les plus simples d’entre eux sont des microcontrôleurs, qui sont utilisés pour des applications à base performance. Les plus complexes sont utilisés pour des applications qui demandent plus de puissance, nous les appellerons SoC haute performance.
La relation entre SoC et microcontrôleurs est assez compliquée à expliquer, la terminologie n'est pas clairement établie. Il existe quelques cours/livres qui séparent les deux, d'autres qui pensent que les deux sont très liés. Dans ce cours, nous allons partir du principe que tous les systèmes qui regroupent processeur, mémoire et quelques périphériques/entrées-sorties sont des SoC. Les microcontrôleurs sont donc un cas particulier de SoC, en suivant cette définition.
===Les microcontrôleurs===
Les '''microcontrôleurs''' sont des composants utilisés dans l'embarqué ou d'informatique industrielle. Leur nom trahit leur rôle. Ils sont utilisés pour contrôler de l'électroménager, des chaines de fabrication dans une usine, des applications robotiques, les alarmes domestiques, les voitures. De manière générale, on les trouve dans tous les systèmes dits embarqués et/ou temps réel. Ils ont besoin de s'interconnecter à un grand nombre de composants et intègrent pour cela un grand nombre d'entrée-sorties. Les microcontrôleurs sont généralement peu puissants, et doivent consommer peu d'énergie/électricité.
[[File:Microcontroller 8051.gif|centre|vignette|upright=2.5|Microcontrôleur Intel 8051.]]
Un microcontrôleur tend à intégrer des entrées-sorties assez spécifiques, qu'on ne retrouve pas dans les SoC destinés au grand public. Un microcontrôleur est typiquement relié à un paquet de senseurs et son rôle est de commander des moteurs ou d'autres composants. Et les entrées-sorties intégrées sont adaptées à cette tâche. Par exemple, ils tendent à intégrer de nombreux convertisseurs numériques-analogiques pour gérer des senseurs. Ils intègrent aussi des circuits de génération de signaux PWM spécialisés pour commander des moteurs, le processeur peut gérer des calculs trigonométriques (utiles pour commander la rotation d'un moteur), etc.
Fait amusant, on en trouve dans certains périphériques informatiques. Par exemple, les anciens disques durs intégraient un microcontrôleur qui contrôlait plusieurs moteurs/ Les moteurs pour faire tourner les plateaux magnétiques et les moteurs pour déplacer les têtes de lecture/écriture étaient commandés par ce microcontrôleur. Comme autre exemple, les claviers d'ordinateurs intègrent un microcontrôleur connecté aux touches, qui détecte quand les touches sont appuyées et qui communique avec l'ordinateur. Nous détaillerons ces deux exemples dans les chapitres dédiés aux périphériques et aux disques durs, tout deviendra plus clair à ce moment là. La majorité des périphériques ou des composants internes à un ordinateur contiennent des microcontrôleurs.
===Les SoC haute performance===
Les SoC les plus performants sont actuellement utilisés dans les téléphones mobiles, tablettes, ''Netbook'', ''smartphones'', ou tout appareil informatique grand public qui ne doit pas prendre beaucoup de place. La petite taille de ces appareils fait qu'ils gagnent à regrouper toute leur électronique dans un circuit imprimé unique. Mais les contraintes font qu'ils doivent être assez puissants. Ils incorporent des processeurs assez puissants, surtout ceux des ''smartphones''. C'est absolument nécessaire pour faire tourner le système d'exploitation du téléphone et les applications installées dessus.
Niveau entrées-sorties, ils incorporent souvent des interfaces WIFI et cellulaires (4G/5G), des ports USB, des ports audio, et même des cartes graphiques pour les plus puissants d'entre eux. Les SoC incorporent des cartes graphiques pour gérer tout ce qui a trait à l'écran LCD/OLED, mais aussi pour gérer la caméra, voire le visionnage de vidéo (avec des décodeurs/encodeurs matériel). Par exemple, les SoC Tegra de NVIDIA incorporent une carte graphique, avec des interfaces HDMI et VGA, avec des décodeurs vidéo matériel H.264 & VC-1 gérant le 720p. Pour résumer, les périphériques sont adaptés à leur utilisation et sont donc foncièrement différents de ceux des microcontrôleurs.
[[File:Phone hardware.png|centre|vignette|upright=2|Hardware d'un téléphone. On voit qu'il est centré autour d'un SoC, complété par de la RAM, un disque dur de faible capacité, de quoi gérer les entrées utilisateurs (l'écran tactile, les boutons), et un modem pour les émissions téléphoniques/2G/3G/4G/5G.]]
Un point important est que les processeurs d'un SoC haute performance sont... performants. Ils sont le plus souvent des processeurs de marque ARM, qui sont différents de ceux utilisés dans les PC fixe/portables grand public qui sont eux de type x86. Nous verrons dans quelques chapitres en quoi consistent ces différences, quand nous parlerons des jeux d'instruction du processeur. Autrefois réservé au monde des PCs, les processeurs multicœurs deviennent de plus en plus fréquents pour les SoC de haute performance. Il n'est pas rare qu'un SoC incorpore plusieurs cœurs. Il arrive même qu'ils soient foncièrement différents, avec plusieurs cœurs d'architecture différente.
La frontière entre SoC haute performance et microcontrôleur est de plus en plus floue. De nombreux appareils du quotidien intègrent des SoC haute performance, d'autres des microcontrôleurs. Par exemple, les lecteurs CD/DVD/BR et certains trackers GPS intègrent un SoC ou des processeurs dont la performance est assez pêchue. À l'opposé, les systèmes domotiques intègrent souvent des microcontrôleurs simples. Malgré tout, les deux cas d'utilisation font que le SoC/microcontrôleur est connecté à un grand nombre d'entrées-sorties très divers, comme des capteurs, des écrans, des LEDs, etc.
[[File:GPS tracker Hardware Architecture.png|centre|vignette|upright=2|Hardware d'un tracker GPS.]]
==Étude de quelques exemples d'architectures==
Après avoir vu la théorie, nous allons voir des exemples réels d'ordinateurs. Dans ce qui suit, nous allons voir des ordinateurs assez anciens, pour une raison simple : ils collent assez bien à l''''architecture de base''' vue plus haut, avec un CPU, une RAM et une ROM, quelques entrées-sorties. Tous les ordinateurs modernes, mais aussi dans les smartphones, les consoles de jeu et autres, utilisent une architecture grandement modifiée et améliorée, avec un grand nombre de périphériques, disques durs/SSD, un grand nombre de mémoires différentes, etc.
Il pourrait sembler pertinent d’étudier des microcontrôleurs ou des ''System On Chip'', en premier lieu. Mais nous éviterons soigneusement de tels systèmes pour le moment. La raison est qu'ils ont un grand nombre d'entrées-sorties, qui sont peu familières. Attendez-vous à avoir près d'une vingtaine ou centaine d'entrée-sorties différentes pour de tels systèmes. Le tout est très complexe, bien trop pour un premier exemple. A la place, nous allons voir précisément des exemples plus simples : les premiers PC, et des consoles de jeu 8 et 16 bits.
Bien que ce soit des systèmes très simples, ils sont cependant plus complexes que l'architecture de base. Et leur avantages/désavantages sont un peu inverse l'un de l'autre. Si on devait résumer les différences, on aurait ceci :
* Les PC ont plus d'entrées-sorties que les consoles, bien que nettement moins que pour les microcontrôleurs/SoC.
* Les PC utilisent des disques durs, les consoles font avec soit des cartouches de jeu, soit des CD/DVD.
* Les PC utilisent des cartes électroniques séparées pour le son et l'écran, les consoles utilisent des circuits soudés sur la carte mère, qui sont souvent des co-processeurs.
* Les PC ont une mémoire ROM soudées sur la carte mère, les consoles 8 bits font sans.
Les PC et micro-ordinateurs ont plus d'entrées-sorties que les consoles. Même si on mets de côté les périphériques, ils ont aussi beaucoup de composants soudées sur la carte mère. En comparaison, les consoles de jeu 8/16 bits se débrouillent avec : une cartouche de jeu et une manette en entrée, une sortie vidéo et une sortie son.
Un autre point important est l'absence de disque dur ou de lecteur CD. La présence d'un disque dur ou d'un lecteur CD/DVD complexifie tout de suite l'architecture des PC. Il faut leur réserver un bus dédié ou les connecter à un bus système, cela demande d'ajouter des circuits sur la carte mère, etc. Et surtout, il faut expliquer comment l'ordinateur exécute des programmes, ce qui demande de parler de l'interaction avec le disque dur et la ROM du BIOS. Rien de tout cela sur les consoles de jeu 8 et 16 bits. Elles utilisent à la place des cartouches de jeu, qui intègrent une mémoire ROM, pour mémoriser les données du jeu, voire son code. Pas besoin de parler des mémoires de stockage, on est beaucoup plus proche de l'architecture de base avec une ROM unique.
Par contre, n'allez pas croire que tout est rose avec les consoles 8/16 bits. Il y a quelques différences qui font qu'elles sont plus complexes qu'un PC sur certains points.
Les PC utilisent des cartes électroniques à brancher sur la carte mère pour alimenter l'écran et les hauts-parleurs/casques, alors que les consoles de jeu utilisent des souvent co-processeurs dédiés pour le son et les graphismes. La différence parait mineure, mais elle avantage les consoles. Nous avons déjà expliqué ce que sont les co-processeurs plus haut, aussi les co-processeurs des consoles nous paraitrons familiers. On n'a pas à s’embêter à expliquer ce que sont les cartes d'extension, les bus associés et tout ce qui va avec, cela peut être retardé pour la section sur l'architecture des PC.
La gestion de la cartouche de jeu est aussi un peu subtile à comprendre, bien que ce soit bien plus simple à comprendre qu'un système avec un disque dur. Les cartouches de jeu intègrent une mémoire ROM, pour mémoriser les données du jeu, voire son code. Et le processeur doit exécuter le code depuis cette mémoire ROM. La conséquence est que les consoles 8/16 bits utilisent une architecture Harvard, avec un bus relié à la cartouche pour lire les instructions. Mais si ce n'était que ça... Les cartouches mémorisent aussi les données pour les graphismes, ce qui fait que le co-processeur vidéo doit lui aussi lire la cartouche pour récupérer ces données...
===L'architecture de la TurboGraphX-16===
La console PC Engine, aussi appelée TurboGraphX, est une ancienne console 8 bits. Elle contient un processeur 65C02, 8 kibioctets de RAM, un port manettes, une carte son et une carte vidéo. La '''carte son''' est le composant qui s'occupe de commander les haut-parleurs et de gérer tout ce qui a rapport au son. La '''carte graphique''' est le composant qui est en charge de calculer les graphismes, tout ce qui s'affiche à l'écran. Sur cette console, les cartes son et graphique ne sont PAS des co-processeurs, ce sont des circuits électroniques dits fixes. C'est totalement différent de ce qu'on a sur les consoles modernes, aussi le préciser est important.
Bien que la carte graphique ne soit pas un processeur, elle a 64 kibioctets de RAM rien que pour elle. La RAM en question est séparée de la RAM normale, c'est un circuit intégré séparé. Et c'est un cas très fréquent, qui reviendra par la suite. La majeure partie des cartes graphiques dispose de leur propre '''mémoire vidéo''', totalement réservée à la carte graphique. La RAM vidéo est connectée à la carte graphique via un bus séparé. Le processeur est souvent connecté à ce bus, afin de pouvoir écrire des données dedans, mais ce n'est pas le cas ici.
[[File:Architecture de la PC Engine, aussi appelée TurboGrafx-16.png|centre|vignette|upright=2.5|Architecture de la PC Engine, aussi appelée TurboGrafx-16]]
L'architecture de la console était particulièrement simple. Le processeur était le centre de l'architecture, tout était connecté dessus. Il y a un bus pour la cartouche de jeu, un autre pour la RAM, un autre pour les manettes, un autre pour carte son, et un dernier pour la carte graphique. Le fait d'avoir un bus par composant est assez rare et ce n'est le cas ici que parce des conditions particulières sont remplies. Déjà, il y a peu d'entrée-sorties. Ensuite, les bus font tous 8 bits, vu que le processeur est un CPU 8 bits. Avec 5 connexions de 8 bits, le tout utilise 40 broches, ce qui est beaucoup, mais totalement gérable. Par contre, les choses changerons pour les autres consoles.
Au final, l'organisation des bus peut s'expliquer avec ce qu'on a vu dans la section sur les bus de communication. La console utilise une architecture Harvard, car la ROM et la RAM utilisent des bus différents. De plus, il y a des bus dédiés aux entrées-sorties, séparés des bus mémoire. Enfin, la carte graphique a droit à ses propres bus pour lire dans la cartouche et dans sa RAM vidéo dédiée.
===L'architecture de la console de jeu NES===
Maintenant, nous allons voir la console de Jeu Famicom, aussi appelée la NES en occident. Elle a une architecture centrée sur un processeur Ricoh 2A03, similaire au processeur 6502, un ancien processeur autrefois très utilisé et très populaire. Le processeur est associé à 2 KB de mémoire RAM.
Sur certaines cartouches, on trouve une RAM utilisée pour les sauvegardes, qui est adressée par le processeur directement. Première variation par rapport à l'architecture de la console précédente : l'ajout de la RAM pour les sauvegardes dans les cartouches.
Niveau carte graphique, une différence importante est que la carte graphique est connectée à la cartouche de jeu via un autre bus, afin de pouvoir lire les sprites et textures du jeu dans la cartouche.
[[File:Architecture de la NES.png|centre|vignette|upright=2.5|Architecture de la NES]]
La différence avec l'architecture précédente est que des bus ont été fusionnés. Comme dit plus haut, le système utilise une architecture Harvard, vu que la ROM est dans la cartouche, alors que la RAM est soudée à la carte mère. Par contre, la Famicon utilise un bus dédié aux entrées-sorties. Il est utilisé pour la carte son et la carte graphique, seules les manettes sont sur un bus à part. Ce qui fait qu'on devrait plutôt parler de bus de sorties, mais passons... L'essentiel est qu'on n'est plus tout à fait dans le cas de la console précédente, avec un bus par composant.
===L'architecture de la SNES===
L'architecture de la SNES est illustrée ci-dessous. Les changements pour le processeur et la RAM sont mineurs.La RAM a augmenté en taille et passe à 128 KB. Pareil pour la RAM de la carte vidéo, qui passe à 64 KB. Par contre, on remarque un changement complet au niveau des bus, de la carte graphique et de la carte son.
[[File:Architecture de la SNES.png|centre|vignette|upright=2|Architecture de la SNES]]
La console utilise un '''bus système unique''', sur lequel tout est connecté : ROM, RAM, entrées-sorties, etc. La seule exception est pour les manettes, qui sont encore connectées directement sur le processeur, via un bus séparé. La transition vers un bus système s'explique par le fait que la console est maintenant de 16 bits, ce qui fait que les bus doivent être plus larges. Le processeur adresse des mémoires RAM et ROM plus grandes, ce qui double la taille de leurs bus. De plus, les entrées-sorties aussi ont besoin d'un bus plus large. Le processeur n'ayant pas un nombre illimité de broches, la seule solution est de fusionner les bus en un seul bus système.
Un autre changement est que la carte graphique est maintenant composée de deux circuits séparés. Encore une fois, il ne s'agit pas de coprocesseurs, mais de circuits non-programmables. Par contre, la carte son est remplacée par deux coprocesseurs audio ! De plus, les deux processeurs sont connectés à une mémoire RAM dédiée de 64 KB, comme pour la carte graphique. L'un est un processeur 8 bits (le DSP), l'autre est un processeur 16 bits.
Un point très intéressant : certains jeux intégraient des coprocesseurs dans leurs cartouches de jeu ! Par exemple, les cartouches de Starfox et de Super Mario 2 contenait un coprocesseur Super FX, qui gérait des calculs de rendu 2D/3D. Le Cx4 faisait plus ou moins la même chose, il était spécialisé dans les calculs trigonométriques, et diverses opérations de rendu 2D/3D. En tout, il y a environ 16 coprocesseurs d'utiliser et on en trouve facilement la liste sur le net. La console était conçue pour, des pins sur les ports cartouches étaient prévues pour des fonctionnalités de cartouche annexes, dont ces coprocesseurs. Ces pins connectaient le coprocesseur au bus des entrées-sorties. Les coprocesseurs des cartouches de NES avaient souvent de la mémoire rien que pour eux, qui était intégrée dans la cartouche.
===L'architecture de la Megadrive et de la néo-géo===
Passons maintenant à la console de jeu Megadrive, une console 16 bits. Elle a une architecture similaire à celle de la néo-géo, une autre console bien plus puissante, sorti à peu près en même temps. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 et le Motorola 68000 étaient deux processeurs très populaires à l'époque. Le Z80 est une sorte de version améliorée de l'Intel 8088 utilisé sur les anciens PC et de nombreuses consoles utilisaient des Z80 comme processeur principal. Il était familier pour les programmeurs de l'époque, pour son cout réduit, sa bonne disponibilité, et bien d'autres avantages liés à sa production de masse.
Le Z80 est utilisé comme co-processeur audio. Il commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens.
Le Motorola 68000 était un processeur 16 bits, alors que le Z80 est un processeur 8 bits. Et cette différence fait que l'on ne peut pas connecter directement les deux sur le même bus, ou du moins pas facilement. La solution retenue est d'utiliser deux bus séparés : un bus de 16 bits connecté au 68000, un bus de 8 bits connecté au Z80. Le premier bus est un bus système sur lequel est connecté le 68000, 64 kibioctets de RAM, la cartouche de jeu, et la carte graphique. Le second bus est un bus de 8 bits, plus court, relié au Z80, à un synthétiseur sonore, et 8 kibioctets de RAM
Les deux bus sont connectés à un '''''chipset''''', un circuit répartiteur, qui fait le pont entre les deux bus. Les manettes sont connectées sur le ''chipset''. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'un programme pré-déterminé sur le Z80.
: Pour ceux qui savent ce qu'est une interruption, les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Cet exemple nous montre que les bus systèmes sont certes très simples, mais aussi inflexibles. Ils fonctionnent bien quand les composants branchés dessus sont tous des composants 8 bits, ou sont tous de 16 bits, ou tous 32 bits. Mais dès qu'on mélange composants 8, 16, 32 ou 64 bits, les choses deviennent plus compliquées. Il est alors préférable d'utiliser des bus séparés, avec des répartiteurs pour faire le pont entre les différents bus. Et nous verrons que le problème s'est posé lui aussi sur les PC.
===L'architecture des anciennes consoles Playstation : beaucoup de co-processeurs===
Les consoles que nous venons d'aborder étaient des consoles 8 ou 16 bits. A partir des consoles 32 bits, leur architecture s'est rapprochée de celle des PC, avec un usage plus complexes de répartiteurs. La XBOX était très semblable à un PC : le processeur était un Pentium 3 modifié, la carte graphique était une Geforce 3 modifiée, les 64 mébioctets de RAM était la même mémoire DDR que celle des PC, le répartiteur secondaire était un ''chipset'' nForce de NVIDIA, etc. Mais les Playstation 1, 2 et 3 se distinguent de leur contemporains. Elles disposent de très nombreux co-processeurs, qui sont en plus très variés.
La Playstation 1 a été une des premières console à utiliser les CD-ROM comme support de stockage, en remplacement des cartouches. La conséquence est que la console contient une mémoire ROM, soudée à la carte mère, de 512 kibioctets. Elle contient aussi 2 mébioctets de RAM, une carte graphique avec 1 mébioctet de mémoire vidéo, un processeur, et de quoi gérer les périphériques. Il y a un co-processeur audio spécialisé, avec 512 kibioctets de RAM, ce qui nous est familier. Par contre, les autres co-processeurs ne le sont pas.
Déjà, le lecteur de CD-ROM est associé à des circuits sur la carte mère, il y a tout un sous-système dédié au lecteur de CD. Il y a un contrôleur qui sert d'interface avec le lecteur proprement dit, mais aussi deux co-processeurs audio et 32 kibioctets de RAM. Les co-processeurs audio servent à lire des CD sans trop utiliser le second co-processeur audio, ils lui servent de complément.
Ensuite, le processeur incorpore plusieurs cœurs, avec un cœur principal et plusieurs co-processeurs. Le premier est un co-processeur système, qui est utilisé pour gérer la mémoire cache intégrée au processeur, pour des fonctionnalités appelées interruptions et exceptions, ainsi que pour configurer le processeur. Le second est un co-processeur arithmétique spécialisé dans les calculs en virgule flottante, très importants pour le rendu 3D. Enfin, il y a un décodeur vidéo, qui n'est pas un co-processeur, mais un circuit non-programmable, spécialisé dans le décodage vidéo. De nos jours, ce circuit aurait été intégré dans la carte graphique, mais il était intégré dans le processeur sur la Playstation 2.
Pour le reste, le processeur est la figure centrale de la console. Il est connecté à 4 bus : un pour la RAM, un pour la carte graphique, un pour les manettes, un autre pour le reste. Le dernier bus est connecté au système audio et au système pour le lecteur CD. Ce serait un bus d'entrée-sortie, s'il n'était pas connecté à la mémoire ROM. Vous avez bien lu : la mémoire ROM est reliée au bus d'entrée-sortie.
[[File:Architecture de la Playstation.png|centre|vignette|upright=2.5|Architecture de la Playstation]]
La Playstation 2 est composé d'un processeur, couplé à 32 Mébioctets de RAM, et d'un paquet de co-processeurs. Plus de co-processeurs que la PS1. Le processeur principal n'est pas la même que celui de la PS1, mais il a une architecture similaire. Il intègre un décodeur vidéo sur le même circuit intégré, ainsi que deux co-processeur. Les co-processeurs ne sont cependant pas les mêmes.
Le co-processeur système disparait et est remplacé par un second co-processeur arithmétique. Les deux co-processeurs arithmétiques sont spécialisés dans les nombres flottants, avec quelques différences entre les deux. Par exemple, le second co-processeur gérait des calculs trigonométriques, des exponentielles, des logarithmes, et d'autres fonctions complexes du genre ; mais pas le premier co-processeur. Ils sont reliés à 4 kibioctets de RAM pour le premier, 16 kibioctets de RAM pour le second ; qui sont intégrées dans le processeur et non-représentés dans le diagramme ci-dessous.
La PS2 intègre aussi un co-processeur d'entrées-sorties. Pour information, il s'agit du processeur principal de la Playstation 1, qui est ici utilisé différemment, suivant que l'on place un jeu PS1 ou PS1 dans la console. Si on met un jeu PS1, il est utilisé pour émuler la Playstation 1, afin de faire tourner le jeu PS1 sur la PS2. Si on met un jeu PS2, il est utilisé comme co-processeur d'entrée-sortie et fait l'interface entre CPU et entrées-sorties. Il est relié à 2 mébioctets de RAM, soit exactement la même quantité de mémoire que la Playstation 1.
Tous les périphériques sont connectés au co-processeur d'entrées-sortie. Pour cela, le co-processeur d'entrées-sortie est relié à deux bus dédiés aux périphériques. Le premier bus est relié aux manettes, aux ports USB et aux ports pour cartes mémoires. Le second bus est relié à la carte son, la carte réseau, le lecteur DVD, et un port PCMIA. Notons que la carte son intègre un co-processeur audio, qui n'est pas représenté dans le diagramme ci-dessous.
[[File:Playstation 2 architecture.png|centre|vignette|upright=2.5|Playstation 2 architecture]]
==L'architecture des PC et son évolution==
Après avoir vu les consoles, nous allons maintenant voir les anciens PC, des années 80 ou 90. Le tout premier PC était techniquement l''''IBM PC'''. Par la suite, de nombreux ordinateurs ont tenté de reproduire l'IBM PC originel, avec parfois quelques modifications mineures. De tels ordinateurs ''IBM PC compatibles'', ont été très nombreux, pour des raisons diverses. Le fait d'utiliser des composants banalisés, facilement disponibles, ainsi qu'une bonne documentation de l'IBM PC originel, a grandement aidé. Les IBM PC compatibles ont progressivement évolué pour donner les PC actuels. L'IBM PC compatible a donné naissance à de nombreux standards divers.
===L'IBM PC originel et l'IBM PC XT===
[[File:IBM PC XT 02.jpg|vignette|IBM PC XT.]]
Nous allons commencer par voir l'IBM PC originel, et son successeur : l'IBM Personal Computer XT. Nous les appelerons tous deux l'IBM PC. L'IBM PC utilisait un processeur Intel 8088, qui était un processeur 8 bits. Ils utilisaient un bus système unique, appelé le '''bus XT'''. Le bus système allait à 4.77 MHz, soit la même fréquence que le processeur. C'était un bus de 8 bits, ce qui collait parfaitement avec les processeurs 8 bits commercialisés par Intel à l'époque.
L'IBM PC comprenait une mémoire ROM avec de quoi faire fonctionner le PC. La ROM en question contenait un programme minimal, appelé le '''BIOS''', sans lequel le PC ne fonctionnait pas du tout. Il servait de base pour le système d'exploitation et MS-DOS ne fonctionnait pas sans elle. De nos jours, son rôle est plus limité : sans elle, le PC ne démarre pas. Mais nous détaillerons cela dans le prochain chapitre.
En plus de la ROM pour le BIOS, l'IBM PC avait quatre mémoires ROM dédiée au langage de programmation BASIC. Lorsque le PC démarrait, il ne bootait pas un système d'exploitation, mais lançait l'interpréteur pour le langage BASIC. De nos jours, ce serait l'équivalent d'un ordinateur qui boote directement sur du Python, à savoir la console Python que vous avez peut-être déjà utilisé si vous avez testé Python. Ceux qui ont déjà touché à un ordinateur de l'époque savent ce que ca veut dire, mais c'est malheureusement très difficile à expliquer sans ce genre d'expérience. Toujours est-il que c'était une sorte de norme à l'époque
: les ordinateurs bootaient généralement sur un interpréteur BASIC.
[[File:XT Bus pins.svg|vignette|Connecteur du bus XT.]]
Les PC étaient conçus pour qu'on branche des '''cartes d'extension''', à savoir des cartes électroniques qu'on branchait sur la carte mère, à l'intérieur du PC. Les cartes d'extension de l'époque étaient surtout des cartes son ou des cartes graphiques, mais aussi des cartes pour brancher des péripéhriques. par exemple, on pouvait ajouter deux cartes graphiques dans l'IBM PC originel : l'''IBM Monochrome Display Adapter'' et/ou la ''IBM Color Graphics Adapter''. De nos jours, les cartes son sont intégrées à la carte mère, mais les cartes graphiques sont restées des cartes d'extension.
Les cartes d'extension étaient branchées sur un '''connecteur XT''', qui était directement relié au bus XT. Le connecteur XT est illustré ci-contre, mais ne vous en souciez pas trop pour le moment. La carte mère de l'IBM PC avait 5 connecteurs de ce type, qu'on pouvait peupler avec autant de cartes d'extension. L'IBM Personal Computer XT est passé à 8 connecteurs XT, soit trois de plus.
Pour ce qui est des périphériques, l'IBM PC avait plusieurs connecteurs : un port série, un port parallèle, un port pour le clavier, et un port pour un lecteur cassette. Le clavier et le lecteur cassette étaient connectés directement sur la carte mère, qui contenait quelques circuits pour gérer le clavier. Par contre, les deux premiers n'étaient pas connectés à la carte mère. Le port série était en réalité une carte d'extension, branchée sur un connecteur XT. Et il en est de même pour le port parallèle.
Pour ce qui est des supports de stockage, l'IBM PC originel n'avait pas de disque dur et n'avait que des lecteurs de disquette. De plus, le lecteur de disquette n'était pas connecté directement sur la carte mère, mais était connecté à une carte d'extension, branchée sur un connecteur XT. La carte d'extension avait deux connecteurs, un par lecteur de disquette, ce qui fait que les deux lecteurs de disquettes pouvaient être branchés sur une seule carte d'extension. L'IBM Personal Computer XT a ajouté un disque dur, sauf sur quelques sous-modèles spécifiques.
Le PC avait aussi un petit haut-parleur capable de faire des bips.
Pour résumer, l'IBM PC originel se reposait beaucoup sur les cartes d'extension, sa carte mère contenait peu de choses. Enfin, peu de choses... Il y avait un processeur Intel 8088, éventuellement un coprocesseur flottant 8087, de la RAM, de la ROM, et des circuits intégrés assez divers. En voici la liste, certains vous seront familiers, d'autres vous seront inconnus à ce stade du cours :
* les circuits de décodage d'adresse ;
* un contrôleur DMA intel 8273 ;
* un contrôleur d'interruption 8259 ;
* un contrôleur de bus Intel 8288 pour gérer le bus XT ;
* un générateur d'horloge Intel 8284 et un diviseur de fréquence ;
* un ''timer'' Intel 8253, le même que celui étudié dans le chapitre sur les ''timers'' ;
* un contrôleur parallèle 8255.
Les multiplexeurs, registres et portes logiques, sont des circuits de décodage d'adresse, qui permettent de combiner plusieurs RAM en une seule, idem avec la mémoire ROM. Si vous verrez qu'il y a 5 mémoires ROM : une ROM pour le BIOS, et quatre autres ROM pour le BASIC. Les 4 ROM du BASIC sont combinées en une seule mémoire ROM. Pour les RAM, il y en a 8 à 32, qui sont combinées en une seule RAM de 16 à 64 kibioctets.
[[File:IBM 5150 Motherboard.svg|centre|vignette|upright=3|Carte mère de l'IBM 5150, un modèle de l'IBM PC.]]
===L'architecture d'un IBM PC compatible 16 bits===
Les PC suivants sont passés à des processeurs 16 bits, mais c'était toujours des processeurs x86 d'Intel, à savoir des Intel 286 et 386. La RAM a grossi, quelques entrées-sorties ont été ajoutées, mais l'architecture globale est plus moins resté le même. C'est surtout au niveau du bus et des périphériques que les changements majeurs ont eu lieu.
[[File:ISA Bus pins.svg|vignette|Connecteur ISA.]]
Les PC 16 bits utilisaient un bus système unique, sur lequel tout était connecté : le processeur, la RAM, la ROM, les cartes d'extension et tout le reste. Le bus en question s'appelait le '''bus AT''', mais il a rapidement été renommé en '''bus ISA''' (''Industry Standard Architecture''). Le bus ISA était prévu pour avoir une compatibilité avec le bus 8 bits de l'IBM PC originel. D'ailleurs, cela se ressent jusque dans le connecteur utilisé : le connecteur ISA est un connecteur XT qu'on a fusionné avec un second connecteur pour l'étendre de 8 à 16 bits.
Les PC 16 bits avaient toujours un port série, un port parallèle, un clavier, un lecteur de disquette et des cartes d'extension. Des disques durs pouvaient être ajoutés, aussi. Mais pour ces périphériques, un changement majeur a eu lieu comparé à l'IBM PC originel. L'IBM PC originel utilisait des cartes d'extension pour tout, sauf le clavier. Mais maintenant, les périphériques ne sont plus connectés à une carte d'extension. A la place, les circuits de la carte d'extension sont déplacés sur la carte mère. Mais n'allez pas croire qu'ils étaient connectés directement au bus ISA, il y avait des intermédiaires.
Le clavier était relié à un '''contrôleur de clavier''', qui faisait l'interface entre le connecteur du clavier et le bus ISA. Le contrôleur de clavier était appelé le ''Keyboard Controler'', abrévié en KB. Il recevait ce qui est tapé au clavier et traduisait cela en quelque chose de compréhensible par l'ordinateur.
Les autres périphériques étaient connectés à un circuit intégré dédié : l''''Intel 82091AA'''. Il était connecté au lecteur de disquette, au port série et au port parallèle. Il servait d'intermédiaire entre ces périphériques et le bus ISA. Vous pouvez le voir comme une sorte de répartiteur, mais qui ne serait pas connecté sur le processeur et la RAM
Enfin, il ne faut pas oublier les autres composants présents sur l'IBM PC originel. Le BIOS est toujours là, de même que les ''timers'' Intel 8253 PIT, le contrôleur d'interruption Intel 8259 et le contrôleur DMA Intel 8237. Les PC 16 bits ont aussi intégré une ''Real Time Clock'' (RTC). Pour rappel, c'est un composant qui permet au PC de mémoriser la date et l'heure courante, à la seconde près. Le tout est résumé dans le schéma ci-dessous.
[[File:Architecture de l'IBM PC compatible.png|centre|vignette|upright=2.5|Architecture de l'IBM PC compatible]]
Un point important est que le bus ISA allait à la même fréquence que le processeur, vu que c'était un bus système. Les processeurs de l'époque étaient des CPU 286 d'Intel, ou le 386 d'Intel. Les Intel 286 allaient de 4 MHz minimum, à 25 MHz maximum. Le 386, quant à lui, allait de 12 à 40 MHz. Le bus ISA devait aller à cette fréquence, il était synchrone avec le processeur.
Par la suite, les processeurs ont gagné en performance, ce qui fait que le bus ISA est devenu trop lent pour le processeur. Une idée a alors été de conserver le bus ISA, pour des raisons de compatibilité, mais de le reléguer comme bus secondaire. L'ordinateur contient alors deux bus : un bus système, et un bus ISA secondaire. Le lien entre les deux est réalisé par un '''pont ISA''', ''ISA Bridge'' en anglais. Le bus ISA fonctionnait alors sa fréquence usuelle, alors que le bus système était beaucoup plus rapide. Le bus système fonctionnait à une fréquence bien plus élevée, ce qui fait que le processeur pouvait communiquer à pleine vitesse, notamment avec la RAM. Le processeur n'était alors plus forcé à aller à la même fréquence que le bus ISA
[[File:Architecture de l'IBM PC compatible avec bridge ISA.png|centre|vignette|upright=2.5|Architecture de l'IBM PC compatible avec bridge ISA]]
Les PC de l'époque intégraient donc plusieurs bus séparés. Vous avez bien lu : plusieurs bus ! Ici, il s'agit de ce que j'appelle des '''bus en cascade''', à savoir qu'un bus est connecté à un autre bus par un intermédiaire. Au passage, si j'aborde ces exemples, car c'est pareil sur les ordinateurs modernes. Le pont ISA a été remplacé par des circuits différents, mais qui ont un rôle assez similaire. Le ''chipset'' de votre carte mère n'est qu'un lointain descendant du pont ISA, qui s'interface avec des bus différents.
===L'arrivée des standards AT et IDE pour les disques durs===
Initialement, les disques durs étaient placés dans l'ordinateur et étaient connectés sur le bus ISA, via une carte d'extension ISA. En clair, il fallait connecter le disque dur sur une carte d'extension, et non sur la carte mère. Les cartes d'extension en question permettaient de connecter un ou plusieurs disques durs, parfois des lecteurs de disquette supplémentaires. Les cartes ISA de ce type faisaient juste l'interface entre le bus ISA et les disques durs, rien de plus. L'interface en question a été standardisée, ce qui a donné le standard ''AT Bus Attachment'', qui a été abrévié en ATA.
Et ce n'était pas que pour les disques durs, de nombreux composants étaient dans ce cas. Une carte d'extension servait d'intermédiaire entre eux et la carte mère. Les cartes d'extension en question étaient appelées des ''Host bus adapter''.
[[File:Acculogic sIDE-4 Controller ISA.jpg|centre|vignette|upright=2|Carte ISA d'interface disque dur, de marque Acculogic.]]
Mais les choses ont rapidement évoluées, que ce soit du côté des cartes mères que du côté des disques durs. Le '''standard IDE''' a permis de brancher un disque dur directement sur la carte mère, sans passer par une carte d'interface ISA. Pour cela, la carte mère réservait un connecteur ISA pour le disque dur, renommé '''connecteur ATA'''. Pour que cela soit possible, il a fallu rajouter des circuits sur la carte mère. Tout ce qui était sur les cartes d'interface ISA s'est retrouvé sur la carte mère.
[[File:Ajout des ports IDE sur la carte mère.png|centre|vignette|upright=2|Ajout des ports IDE sur la carte mère]]
En réalité, les connecteurs ATA étaient des connecteurs ISA simplifiés. Un connecteur ISA avait en tout 98 broches, alors qu'un connecteur ATA n'en contient que 40. Les broches qui étaient inutiles pour les disques durs ont simplement été enlevées. Et qui dit connecteur spécialisé, dit câble spécialisé. Les disques durs étaient branchés sur le connecteur AT grâce à un câble ATA, sur lequel on pouvait connecter deux disques durs.
[[File:ATA Plug.svg|centre|vignette|upright=2|Connecteur ATA.]]
[[File:ATA cables.jpg|centre|vignette|upright=2|Cable ATA.]]
Il était donc possible de connecter deux disques durs sur un seul connecteur ATA. Et cette possibilité est devenue d'autant plus utile par la suite. A partir de la version 2, ATA supportait aussi les lecteurs de disquettes, les lecteurs de CD/DVD, et bien d'autres supports de stockage. Il était alors possible de connecter un lecteur CD et un disque dur sur un seul connecteur. Les cartes mères avaient généralement deux connecteurs ATA, et n'avaient pas besoin de plus. C'était suffisant pour connecter un disque dur, un lecteur de disquette et un lecteur CD, configuration courante entre les années 90 et 2000.
Un câble est donc connecté à deux supports de stockage. Pour distinguer les deux, le standard ATA ajoute une possibilité de configuration. Sur un câble, il doit y avoir un support de stockage "maitre" et un support "esclave". C'était la terminologie de l'époque, que je reproduis ici, même si elle est fortement trompeuse. N'allez pas croire que cela implique que l'un ait des avantages sur l'autre. Le support 'maitre" n'a pas droit à plus de bande passante, il n'a pas la priorité sur l'autre, rien du tout. Il s'agit juste d'un nombre qui permet de savoir avec qui le processeur communique, qui vaut 0 pour le premier support, 1 pour l'autre. Une sorte d'adresse de 1 bit, si l'on veut.
[[File:ATA-Konfiguration02.png|centre|vignette|upright=2|Configuration ATA.]]
Pour configurer un support de stockage en mode "maitre" ou "esclave", le support de stockage avait quelques pins dédiés. Il suffisait de placer un détrompeur en plastique sur les pins adéquats. Les pins se trouvaient à l'arrière du disque dur ou du lecteur de CD/DVD/Disquette/autre.
[[File:HDD Master and Slave Description.jpg|centre|vignette|upright=2|Configuration ''Master/Slave''.]]
===L'architecture d'un PC avec un processeur Intel 486===
Maintenant, passons aux ordinateurs 32 bits, avec l'exemple d'un PC avec un processeur 486 d'Intel. A cette époque, le bus ISA était devenu trop limité et était en place d'être remplacé par le bus PCI, qui avait la même fonction. De nombreuses cartes d'extension utilisaient déjà ce standard et étaient branchées sur des connecteurs PCI dédiés, différents des connecteurs ISA. Intuitivement, on se dit que le bus PCI remplaçait le bus ISA, mais les choses étaient plus compliquées. Les disques durs gardaient leur connecteur ATA, et ne passaient pas par le bus PCI. Ils avaient un bus IDE séparé, qui était un bus ISA modifié.
Là encore, les processeurs étaient devenus beaucoup plus rapides que le bus PCI. Les deux allaient à des fréquences assez différentes, ce qui fait que le bus PCI était séparé du bus système. Il y avait alors deux implémentations possibles.
* La première utilise un répartiteur unique, relié au processeur, à la RAM, au bus PCI, et au bus IDE.
* La seconde utilise un bus système séparé du bus PCI, avec un '''pont PCI''' pour faire l'interface entre les deux.
Le '''''System Controler''''' était un circuit intégré, placé sur la carte mère, qui peut servir soit de pont PCI, soit de répartiteur. Le répartiteur PCI sert d'intermédiaire avec le bus PCI, mais aussi avec le bus IDE, utilisé pour les disques durs, aussi appelé le bus ''Parallel ATA''. Il peut aussi être connecté au processeur, à la mémoire RAM, ainsi qu'à la mémoire cache, mais cela ne sert que quand il est utilisé comme répartiteur.
[[File:Architecture d'un PC utilisant un bus PCI, implémentation avec un répartiteur.png|centre|vignette|upright=2|Architecture d'un PC utilisant un bus PCI, implémentation avec un répartiteur]]
Pour des raisons de compatibilité, le bus ISA avait été conservé, aux côtés du bus PCI. Il y avait un pont ISA en plus du pont/répartiteur PCI. Une implémentation possible aurait été de connecter les deux ponts ISA et PCI à un bus système unique. Mais cette solution n'a pas été retenue. La raison est que le bus PCI et le bus ISA ont des performances très différentes. Le bus PCI est très rapide, le bus ISA beaucoup plus lent. La différence est d'un ordre de grandeur, environ. Dans ces conditions, il est possible de faire passer les communications ISA à travers le bus PCI. Pour cela, le pont ISA est directement connecté sur le pont PCI, comme illustré ci-dessous.
Et il en est de même pour le bus dédié aux disques durs. En effet, les disques durs étaient autrefois reliés au bus ISA, mais cela a changé depuis. Ils disposent maintenant de leur propre bus dédié, le '''bus IDE''', qui est un bus ISA simplifié. Et ce bus ISA simplifié était connecté directement sur le pont PCI.
[[File:Architecture de l'IBM PC compatible avec pont PCI.png|centre|vignette|upright=2|Architecture de l'IBM PC compatible avec pont PCI]]
Dans ce qui va suivre, nous allons étudier un exemple qui utilise un bus système séparé, avec un pont PCI, sans répartiteur. Voilà pour les grandes lignes, mais le schéma ci-dessous montre que tout est plus complexe. Vous remarquerez des connexions optionnelles entre le pont PCI et la mémoire RAM et la mémoire cache. La raison est que le pont PCI peut aussi servir de répartiteur en remplacement du bus système. Concrètement, on peut alors retirer le bus système. La mémoire, le bus PCI, le bus ISA, le bus IDE, le processeur et la RAM sont alors connectés au répartiteur PCI, qui sert d'intermédiaire central entre tous ces composants. Mais ce n'est pas la solution qui a été retenue dans notre exemple.
[[File:Intel486-Typ PCI System.png|centre|vignette|upright=2|PC IBM compatible avec un 486, un bus PCI et un bus ISA. Le ''host bus'' est le bus système.]]
Le pont ISA sert ici d'intermédiaire entre le bus système et le bus ISA. De plus, il a été amélioré sur de nombreux points. Il inclut notamment des circuits qui étaient autrefois sur la carte mère, à savoir le contrôleur DMA 82C87 et le contrôleur d'interruption 82C59, ainsi que les ''timers'' Intel 82C54. Les composants restants sont eux reliés sur un quatrième bus : le bus X, l'ancêtre du bus ''Low Pin Count''. Le bus X était celui du BIOS, du contrôleur de clavier, de la ''Real Time Clock'', et du contrôleur de périphérique 82091AA d'Intel.
[[File:ISA Bridge schematic.png|centre|vignette|upright=2|ISA Bridge.]]
===L'architecture des PC des années 90-2000===
Par la suite, les ponts PCI et ISA ont évolué avec l'évolution des bus de l'ordinateur. Le bus ISA a progressivement été remplacé par d'autres bus, comme le bus ''Low Pin Count'', le bus PCI a été remplacé par le PCI Express, d'autres bus ont été ajoutés, etc. Mais la séparation du ''chipset'' en deux a été conservée.
[[File:Chipset schematic.svg|vignette|upright=1.0|Chipset séparé en northbridge et southbridge.]]
Le pont PCI et le pont ISA ont été remplacés respectivement par le '''pont nord''' et le '''pont sud''', plus connus par leurs noms anglais de ''northbridge'' et de ''southbridge''. Le pont nord servait d'interface entre le processeur, la mémoire et la carte graphique et est connecté à chacun par un bus dédié. Il intégrait aussi le contrôleur mémoire. Le pont sud est le répartiteur pour les composants lents, à savoir l'USB, l'Ethernet, etc. Le bus qui relie le processeur au pont nord était appelé le '''''Front Side Bus''''', abrévié en FSB.
[[File:IMac Chipset.png|centre|vignette|upright=2|Chipset séparé en northbridge et southbridge.]]
Un point important est que le bus PCI est devenu un bus assez lent, ce qui fait qu'il a finit par être connecté au pont sud. Le pont PCI est donc devenu le pont sud, dans le courant des années 2000. Durant un moment, un équivalent du pont ISA a subsisté dans un circuit de '''''Super IO'''''. Concrètement, il s'occupait du lecteur de disquette, du port parallèle, du port série, et des ports PS/2 pour le clavier et la souris. Mais il ne gérait pas le bus ISA, mais son remplaçant, le bus ''Low Pin Count''.
[[File:Motherboard diagram fr.svg|centre|vignette|upright=1.5|Carte mère avec circuit Super IO.]]
===L'architecture des PC depuis les années 2000===
Depuis la sortie du processeur AMD Athlon 64, le pont nord a été fusionné dans le processeur. La fusion ne s'est pas faite en une fois, des fonctionnalités ont progressivement été progressivement intégrées dans le processeur. Le pont sud est resté, mais il a alors été progressivement connecté directement au processeur. La raison derrière cette intégration est que les processeurs avaient de plus en plus de transistors à leur disposition. Ils en ont profité pour intégrer le pont nord. Et cela permettait de simplifier le câblage des cartes mères, sans pour autant rendre vraiment plus complexe la fabrication du processeur. Les industriels y trouvent leur compte.
La première étape a été l'intégration du contrôleur mémoire a été intégré au processeur. Concrètement, le résultat était que la mémoire RAM n'était plus connectée au pont nord, mais était connectée directement au processeur ! Il y a donc eu un retour d'un bus mémoire, mais spécialisé pour la mémoire RAM. En théorie, une telle intégration permet diverses optimisations quant aux transferts avec la mémoire RAM. Les transferts ne passent pas par un répartiteur, ce qui réduit le temps d'accès à la mémoire RAM. Ajoutons de sombres histoires de prefetching, d'optimisation des commandes, et j'en passe. Toujours est-il que le pont nord ne servait alors d'intermédiaire que pour les ports PCI Express, et le pont sud.
[[File:X58 Block Diagram.png|centre|vignette|upright=2|Chipset X58 d'Intel.]]
Par la suite, la carte graphique fût aussi connectée directement sur le processeur. Le processeur incorpore pour cela des contrôleurs PCI-Express. Le pont nord a alors disparu complétement, son intégration dans le processeur était complète. Sur les cartes mères Intel récentes, le pont sdud subsiste, il est appelé le ''Platform Controler Hub'', ou PCH. L'organisation des bus sur la carte mère qui résulte de cette connexion du processeur à la carte graphique, est illustrée ci-dessous, avec l'exemple du PCH.
[[File:Intel 5 Series architecture.png|centre|vignette|upright=2|Intel 5 Series architecture]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=L'interface électrique entre circuits intégrés et bus
| prevText=L'interface électrique entre circuits intégrés et bus
| next=La hiérarchie mémoire
| nextText=La hiérarchie mémoire
}}
</noinclude>
a1zkol9m03gvnkjnjib1uly2q2w5e83
Fonctionnement d'un ordinateur/Architectures multiprocesseurs et multicœurs
0
65962
763645
762884
2026-04-13T20:34:36Z
Mewtow
31375
763645
wikitext
text/x-wiki
Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache.
Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle.
Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire.
Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers.
: Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs.
==Le multiprocesseur asymétrique==
Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents.
Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire.
Un exemple de multicoeurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Les interruptions inter-processeurs==
Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre.
Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions.
[[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]]
On peut préciser le processeur de destination en configurant certains registres du IO-APIC, afin que celui-ci redirige la demande d'interruption d'un local APIC vers celui sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser quel est le type de transfert : doit-on envoyer l'interruption au processeur émetteur, à tous les autres processeurs, à un processeur particulier. Dans le dernier cas, certains bits du registre permettent de préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre.
==Le partage des caches==
Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache.
===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés===
Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages.
{|
|[[File:Caches dédiés.png|vignette|Caches dédiés]]
|[[File:Caches partagés.png|vignette|Cache partagé]]
|}
Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé.
Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop.
À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes.
[[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]]
Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches.
Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent.
===Le partage des caches adapté à une hiérarchie de caches===
Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur.
[[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]]
Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs.
Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc.
Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances.
[[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]]
D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé.
[[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]]
===Les caches partagés centralisés et distribués===
Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs.
Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache.
Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe.
[[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]]
La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé.
===Les caches virtualisés===
Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''.
Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs.
Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible.
Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2.
Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible.
==Le réseau d'interconnexion entre cœurs==
Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère.
Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs.
[[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]]
===Le bus partagé entre plusieurs cœurs===
Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique.
[[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]]
Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux.
[[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]]
Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique.
L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches
[[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]]
===Le réseau d'interconnexion entre plusieurs cœurs===
Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe.
Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs.
{|
|[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]]
|[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]]
|}
Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs.
Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus.
===Les architectures en ''tile''===
Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''.
[[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]]
Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée.
Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties.
[[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]]
==Annexe : les architectures à cœurs conjoints==
Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances.
Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120.
Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''.
[[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]]
La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées.
[[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les architectures parallèles
| prevText=Les architectures parallèles
| next=Architectures multithreadées et Hyperthreading
| nextText=Architectures multithreadées et Hyperthreading
}}
</noinclude>
i1wo9a4hxv07ra11dpiybgqxxdm32ud
763646
763645
2026-04-13T20:34:53Z
Mewtow
31375
763646
wikitext
text/x-wiki
Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache.
Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle.
Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire.
Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers.
: Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs.
==Les interruptions inter-processeurs==
Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre.
Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions.
[[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]]
On peut préciser le processeur de destination en configurant certains registres du IO-APIC, afin que celui-ci redirige la demande d'interruption d'un local APIC vers celui sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser quel est le type de transfert : doit-on envoyer l'interruption au processeur émetteur, à tous les autres processeurs, à un processeur particulier. Dans le dernier cas, certains bits du registre permettent de préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre.
==Le partage des caches==
Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache.
===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés===
Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages.
{|
|[[File:Caches dédiés.png|vignette|Caches dédiés]]
|[[File:Caches partagés.png|vignette|Cache partagé]]
|}
Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé.
Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop.
À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes.
[[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]]
Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches.
Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent.
===Le partage des caches adapté à une hiérarchie de caches===
Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur.
[[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]]
Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs.
Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc.
Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances.
[[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]]
D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé.
[[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]]
===Les caches partagés centralisés et distribués===
Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs.
Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache.
Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe.
[[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]]
La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé.
===Les caches virtualisés===
Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''.
Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs.
Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible.
Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2.
Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible.
==Le réseau d'interconnexion entre cœurs==
Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère.
Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs.
[[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]]
===Le bus partagé entre plusieurs cœurs===
Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique.
[[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]]
Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux.
[[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]]
Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique.
L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches
[[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]]
===Le réseau d'interconnexion entre plusieurs cœurs===
Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe.
Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs.
{|
|[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]]
|[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]]
|}
Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs.
Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus.
===Les architectures en ''tile''===
Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''.
[[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]]
Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée.
Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties.
[[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]]
==Annexe : les architectures à cœurs conjoints==
Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances.
Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120.
Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''.
[[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]]
La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées.
[[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]]
==Le multiprocesseur asymétrique==
Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents.
Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire.
Un exemple de multicoeurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les architectures parallèles
| prevText=Les architectures parallèles
| next=Architectures multithreadées et Hyperthreading
| nextText=Architectures multithreadées et Hyperthreading
}}
</noinclude>
j89a75yg0hnncbiwl8a9tqvin10omr1
763647
763646
2026-04-13T20:35:16Z
Mewtow
31375
763647
wikitext
text/x-wiki
Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache.
Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle.
Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire.
Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers.
: Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs.
==Les interruptions inter-processeurs==
Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre.
Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions.
[[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]]
On peut préciser le processeur de destination en configurant certains registres du IO-APIC, afin que celui-ci redirige la demande d'interruption d'un local APIC vers celui sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser quel est le type de transfert : doit-on envoyer l'interruption au processeur émetteur, à tous les autres processeurs, à un processeur particulier. Dans le dernier cas, certains bits du registre permettent de préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre.
==Le partage des caches==
Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache.
===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés===
Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages.
{|
|[[File:Caches dédiés.png|vignette|Caches dédiés]]
|[[File:Caches partagés.png|vignette|Cache partagé]]
|}
Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé.
Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop.
À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes.
[[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]]
Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches.
Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent.
===Le partage des caches adapté à une hiérarchie de caches===
Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur.
[[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]]
Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs.
Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc.
Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances.
[[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]]
D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé.
[[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]]
===Les caches partagés centralisés et distribués===
Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs.
Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache.
Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe.
[[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]]
La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé.
===Les caches virtualisés===
Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''.
Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs.
Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible.
Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2.
Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible.
==Le réseau d'interconnexion entre cœurs==
Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère.
Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs.
[[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]]
===Le bus partagé entre plusieurs cœurs===
Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique.
[[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]]
Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux.
[[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]]
Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique.
L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches
[[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]]
===Le réseau d'interconnexion entre plusieurs cœurs===
Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe.
Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs.
{|
|[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]]
|[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]]
|}
Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs.
Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus.
===Les architectures en ''tile''===
Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''.
[[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]]
Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée.
Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties.
[[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]]
==Le multiprocesseur/multicoeur asymétrique==
Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents.
Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire.
Un exemple de multicoeurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Annexe : les architectures à cœurs conjoints==
Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances.
Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120.
Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''.
[[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]]
La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées.
[[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les architectures parallèles
| prevText=Les architectures parallèles
| next=Architectures multithreadées et Hyperthreading
| nextText=Architectures multithreadées et Hyperthreading
}}
</noinclude>
dnbt3cs9d1hb6wym3irxedjqvp8hh0g
Les cartes graphiques/Les Render Output Target
0
67394
763651
763573
2026-04-13T23:47:08Z
Mewtow
31375
/* Les fonctions des ROP */
763651
wikitext
text/x-wiki
Pour rappel, les étapes précédentes du pipeline graphiques manipulaient non pas des pixels, mais des fragments. Pour rappel, la distinction entre fragment et pixel est pertinente quand plusieurs objets sont l'un derrière l'autre. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. La couleur finale dépend de la couleur de tous ces points d'intersection. Intuitivement, l'objet le plus proche est censé cacher les autres et c'est donc lui qui décide de la couleur du pixel, mais cela demande de déterminer quel est l'objet le plus proche. De plus, certains objets sont transparents et la couleur finale est un mélange de la couleur de plusieurs points d'intersection.
Tout demande de calculer un pseudo-pixel pour chaque point d'intersection et de combiner leurs couleurs pour obtenir le résultat final. Les pseudo-pixels en question sont des '''fragments'''. Chaque fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont donc combinés pour obtenir la couleur finale de ce pixel. Pour résumer, la profondeur des fragments doit être gérée, de même que la transparence, etc.
Et c'est justement le rôle de l'étage du pipeline que nous allons voir maintenant. Ces opérations sont réalisées dans un circuit qu'on nomme le '''Raster Operations Pipeline''' (ROP), aussi appelé ''Render Output Target'', situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo.
==Les fonctions des ROP==
Les ROP incorporent plusieurs fonctionnalités qui sont assez diverses. Leur seul lien est qu'il est préférable de les implémenter en matériel plutôt qu'en logiciel, et en-dehors des unités de textures. Il s'agit de fonctionnalités assez simples, basiques, mais nécessaires au fonctionnement de tout rendu 3D. Elles ont aussi pour particularité de beaucoup accéder à la mémoire vidéo. C'est la raison pour laquelle le ROP est situé en fin de pipeline, proche de la mémoire vidéo. Voyons quelles sont ces fonctionnalités.
Sa fonction la plus importante est l'élimination des pixels cachés, grâce au tampon de profondeur. Pour chaque fragment, il lit le pixel correspondant dans le tampon de profondeur, fait la comparaison de profondeur, et met à jour le tampon de profondeur. Nous en avons déjà beaucoup parlé dans les chapitres précédents, notamment dans le chapitre sur les bases du rendu 3D et dans celui sur le rastériseur (avec l'élimination précoce des pixels cachés).
Une autre fonction est le mélange ''alpha'' et le test ''alpha'', pour gérer la transparence, qu'on a là encore vu dans le chapitre sur les bases du rendu 3D. Là encore, les ROPs lisent, pour chaque fragment, le pixel correspondant dans le ''framebuffer'', font le mélange ''alpha'', et enregistrent le résultat dans le ''framebuffer''.
Mais les ROPs ont d'autres fonctions, plus méconnues, qu'on n'a pas abordé dans les chapitres précédents.
===Le tampon de ''stencil''===
Le '''''stencil''''' est une fonctionnalité des API graphiques qui existe depuis très longtemps. Il sert pour générer des effets graphiques très variés, qu'il serait vain de lister ici. Il a notamment été utilisé pour calculer des ombres volumétriques (le moteur de DOOM 3 en faisait grand usage à la base), des réflexions simples, des lightmaps ou shadowmaps, et bien d'autres.
Pour le résumer, on peut le voir comme une sorte de tampon de profondeur où la coordonnée z est remplacée par u octet dont le programmeur peut faire ce qu'il veut. L'idée est que chaque pixel/fragment se voit attribuer une valeur entière, généralement codée sur un octet, que les programmeurs peuvent faire varier à loisir. L'octet ajouté est appelé l''''octet de ''stencil'''''. Il a une certaine valeur, qui est calculée par la carte graphique, généralement par les ''shaders''. Il ne remplace pas la coordonnée de profondeur, mais s'ajoute à celle-ci.
Les octets de ''stencil'' sont placés dans le tampon de profondeur. L'ensemble forme un tableau qui associe 32 bits à chaque" pixel : 24 bits pour une coordonnée z, 8 pour l'octet de ''stencil''. Lors du passage d'un fragment les ROPs, la carte graphique lit le pixel correspondant, dans le tampon de profondeur. Il récupére la coordonnée z, mais aussi l'octet de ''stencil''. Puis il compare l'octet de ''stencil'' avec celui du fragment traité. Si le test échoue, le fragment ne passe pas à l'étape de test de profondeur et est abandonné. S'il passe, le tampon de ''stencil'' est mis à jour. Par mis à jour, on veut dire que le ROP peut faire diverses manipulations dessus : l'incrémenter, le décrémenter, le mettre à 0, inverser ses bits, remplacer par l'octet de ''stencil'' du fragment, etc. Les opérations possibles sont bien plus nombreuses qu'avec le tampon de profondeur, qui se contente de remplacer la coordonnée z par celle du fragment.
===Les effets de brouillard===
Les '''effets de brouillard''' sont des effets graphiques assez intéressants. Ils sont nécessaires dans certains jeux vidéo pour l'ambiance (pensez à des jeux d'horreur comme Silent Hill), mais ils ont surtout été utilisés pour économiser des calculs. L'idée est de ne pas calculer les graphismes au-delà d'une certaine distance, sans que cela se voie.
L'idée est d'avoir un ''view frustum'' limité : le plan limite au-delà duquel on ne voit pas les objets est assez proche de la caméra. Mais si le plan limite est trop proche, cela donnera une cassure inesthétique dans le rendu. Pour masquer cette cassure, les programmeurs ajoutaient un effet de brouillard. Les objets au-delà du plan limite étaient totalement dans le brouillard, puis ce brouillard se réduisait progressivement en se rapprochant de la caméra, avant de s'annuler à partir d'une certaine distance.
Pour calculer le brouillard, on mélange la couleur finale du pixel avec une ''couleur de brouillard'', la couleur de brouillard étant pondérée par la profondeur. Au-delà d'une certaine distance, l'objet est intégralement dans le brouillard : le brouillard domine totalement la couleur du pixel. En dessous d'une certaine distance, le brouillard est à zéro. Entre les deux, la couleur du brouillard et de l'objet devront toutes les deux être prises en compte dans les calculs. La formule de calcul exacte varie beaucoup, elle est souvent linéaire ou exponentielle.
Notons que ce calcul implique à la fois de l'''alpha blending'' mais aussi la coordonnée de profondeur, ce qui en fait que son implémentation dans les ROPs est l'idéal. Aussi, les premières cartes graphiques calculaient le brouillard dans les ROP, en fonction de la coordonnée de profondeur du fragment. De nos jours, il est calculé par les ''pixel shaders'' et les ROP n'incorporent plus de technique de brouillard spécialisée. Vu que les pixels shaders peuvent s'en charger, cela fait moins de circuits dans les ROPs pour un cout en performance mineur. Et ce d'autant plus que les effets de brouillard sont devenus assez rares de nos jours. Autant les émuler dans les pixels shaders que d'utiliser des circuits pour une fonction devenue anecdotique.
===Les autres fonctions des ROPs===
Les ROPS implémentent aussi des techniques utilisées sur les ''blitters'' des anciennes cartes d'affichage 2D, comme l'application d''''opérations logiques''' sur chaque pixel enregistré dans le framebuffer. Les opérations logiques en question peuvent prendre une à deux opérandes. Les opérandes sont soit un pixel lu dans le ''framebuffer'', soit un fragment envoyé au ROP. Les opérations logiques à une opérande peuvent inverser, mettre à 0 ou à 1 le pixel dans le framebuffer, ou faire la même chose sur le fragment envoyé en opérande. Les opérations à deux opérandes lisent un pixel dans le framebuffer, et font un ET/OU/XOR avec le fragment opérande (une des deux opérandes peut être inversée). Elles sont utilisées pour faire du traitement d'image ou du rendu 2D, rarement pour du rendu 3D.
Les ROPs gèrent aussi des '''masques d'écritures''', qui permettent de décider si un pixel doit être écrit ou non en mémoire. Il est possible d'inhiber certaines écritures dans le tampon de profondeur ou le tampon de couleur, éventuellement le tampon de stencil. Inhiber la mise à jour d'un pixel dans le tampon de profondeur est utile pour gérer la transparence. Si un pixel est transparent, même partiellement, il ne faut pas mettre à jour le tampon de profondeur, et cela peut être géré par ce système de masquage. Les masquages des couleurs permettent de ne modifier qu'une seule composante R/G/B au lieu de modifier les trois en même temps, pour faire certains effets visuels.
==L'architecture matérielle d'un ROP==
Les ROP contiennent des circuits pour gérer la profondeur des fragments. Il effectuent un test de profondeur, à savoir que les fragments correspondant à un même pixel sont comparés pour savoir lequel est devant l'autre. Ils contiennent aussi des circuits pour gérer la transparence des fragments. Le ROP gère aussi l'antialiasing, de concert avec l'unité de rastérisation. D'autres fonctionnalités annexes sont parfois implémentées dans les ROP. Par exemple, les vielles cartes graphiques implémentaient les effets de brouillards dans les ROPs. Le tout est suivi d'une unité qui enregistre le résultat final en mémoire, où masques et opérations logiques sont appliqués.
Les différentes opérations du ROP doivent se faire dans un certain ordre. Par exemple, gérer la transparence demande que les calculs de profondeur se fassent globalement après ou pendant l'''alpha blending''. Ou encore, les masques et opérations logiques se font à la toute fin du rendu. L'ordre des opérations est censé être le suivant : test ''alpha'', test du ''stencil'', test de profondeur, ''alpha blending''. Du moins, la carte graphique doit donner l'impression que c'est le cas. Elle peut optimiser le tout en traitant le tampon de profondeur, de couleur et de ''stencil'' en même temps, mais donner les résultats adéquats.
Un ROP est typiquement organisé comme illustré ci-dessous. Notons que les circuits de gestion de la profondeur et de la transparence sont séparés dans les schémas, mais il s'agit là d'une commodité qui ne reflète pas forcément l'implémentation matérielle. Et si ces deux circuits sont séparés, ils communiquent entre eux, notamment pour gérer la profondeur des fragments transparents.
[[File:Render Output Pipeline-processor.png|centre|vignette|upright=2|Render Output Pipeline-processor]]
Les ROPs récupèrent les fragments calculés par les pixels shaders et/ou les unités de texture, via un circuit d'interconnexion spécialisé. Chaque ROP est connecté à toutes les unités de ''shader'', même si la connexion n'est pas forcément directe. Toute unité de ''shader'' peut envoyer des pixels à n'importe quel ROP. Les circuits d'interconnexion sont généralement des réseaux d'interconnexion de type ''crossbar'', comme illustré ci-contre (le premier rectangle rouge).
Le ROP effectue beaucoup de lectures et écritures en mémoire vidéo. Or, la bande passante mémoire est limitée, ce qui fait que le ROP est un goulot d'étranglement assez important pour le rendu 3D. Heureusement, de nombreuses optimisations permettent d'optimiser le tout. Elles agissent sur la lecture du tampon de profondeur, mais aussi sur le ''framebuffer''.
===Le ''fast clear'' du ''framebuffer''===
Une première optimisation porte sur le ''framebuffer''. Le ''framebuffer''est souvent réutilisé d'une image sur l'autre. Quand une image a été envoyée à l'écran, le ''framebuffer'' est remis à zéro pour accueillir une nouvelle image. Et ce avec ou sans ''double buffering''. La mise à zéro est censée se faire en remettant réellement le ''framebuffer'' à zéro, en écrivant des 0 pour chaque pixel du ''framebuffer''. Mais il y a moyen de s'en passer.
Pour cela, l'idée est que le ''framebuffer'' est découpé en ''tiles'', des carrés de 4, 8, 16 pixels de côté. Les ''tiles'' ont généralement la même taille que les ''tiles'' utilisées pour la rastérisation, mais passons sur ce détail. L'idée est de mémoriser, pour chaque ''tile'', si elle est mise à 0 ou non. Il suffit de cela d'un seul bit par ''tile'', appelé le bit RESET. L'ensemble des bits RESET est mémorisé dans une petite mémoire SRAM, intégrée aux ROPs.
Lorsqu'on souhaite remettre à zéro le ''framebuffer'', il suffit de mettre à 0 tous les bits RESET dans cette SRAM, pas besoin d’accéder à la mémoire vidéo. Avant toute lecture dans le ''framebuffer'', le ROP lit cette SRAM pour vérifier si la ''tile'' en question a été remise à 0. Si ce n'est pas le cas, il lit le pixel voulu depuis le ''framebuffer''. Mais si c'est le cas, alors le ROP ne fait pas la lecture et fournit un pixel à zéro à la place, qui est utilisé pour l'''alpha blending'' ou autre. La moindre écriture dans une ''tile'' met le bit RESET à 0 : la ''tile'' entière est considérée comme non-remise à zéro, même si un seul pixel a été modifié dedans.
Notons que l'usage d'une granularité par ''tile'' est un compromis. On peut ne peut pas utiliser un bit par pixel, car cela demanderait d'utiliser une SRAM énorme. De même, utiliser un seul bit pour tout le ''framebuffer'' ruinerait totalement l'optimisation : le ''framebuffer'' entier serait considéré comme non-RESET dès la première écriture d'un pixel dedans, on ne sauverait qu'un nombre trop limité d'accès mémoire.
===La z-compression===
La technique de '''z-compression''' compresse le tampon de profondeur. Plus précisément, elle découpe le tampon de profondeur en ''tiles'', en blocs carrés, qui sont compressés séparément les uns des autres. La taille des ''tiles'' est souvent la même que celle utilisée par le rastériseur pour la rastérisation grossière. Par exemple, la ''z-compression'' des cartes graphiques ATI radeon 9800, découpait le tampon de profondeur en ''tiles'' de 8 * 8 fragments, et les encodait avec un algorithme nommé DDPCM (''Differential differential pulse code modulation'').
Précisons que cette compression ne change pas la taille occupée par le tampon de profondeur, mais seulement la quantité de données lue/écrite. La raison est que les ''tiles'' doivent avoir une place fixe en mémoire. Par exemple, si une ''tile'' non-compressée prend 64 octets, on trouvera une ''tile'' tous les 64 octets en mémoire vidéo, afin de simplifier les calculs d'adresse, afin que le ROP sache facilement où se trouve la ''tile'' à lire/écrire. Avec une vraie compression, les ''tiles'' se trouveraient à des endroits très variables d'une image à l'autre.
Par contre, la z-compression réduit la quantité de données écrite dans le tampon de profondeur. Par exemple, au lieu d'écrire une ''tile'' non-compressée de 64 octets, on écrira une ''tile'' de seulement 6 octets, les 58 octets restants étant pas lus ou écrits. On obtient un gain en performance, pas en mémoire.
[[File:AMD HyperZ.svg|centre|vignette|upright=2|AMD HyperZ]]
Le format de compression ajoute un bit par ''tile'', qui indique si elle est compressée ou non. Le bit qui indique si la ''tile'' est compressée permet de laisser certaines ''tiles'' non-compressés, dans le cas où la compression ne permet pas de gagner de la place. La compression ajoute souvent un second bit, qui indique si la ''tile'' est à zéro ou non, sur le même modèle que pour le ''framebuffer''. Il accélère la remise à zéro du tampon de profondeur. Au lieu de réellement remettre tout le tampon de profondeur à 0, il suffit de réécrire un bit par ''tile''. Le gain en nombre d'accès mémoire peut se révéler assez impressionnant.
Les deux bits en question peuvent être placés à deux endroits différents. La première solution serait d'utiliser une portion de la mémoire vidéo, mais cela demanderait de faire deux lectures par accès au tampon de profondeur. La vraie solution est d'utiliser une SRAM reliée aux ROPs, qui est assez grande pour mémoriser tout le tampon de profondeur, du moins avec deux bits par ''tile''.
===Le cache de profondeur===
Une optimisation complémentaire ajoute une ou plusieurs mémoires caches dans le ROP, dans le circuit de profondeur. Ce '''cache de profondeur''' stocke des portions du tampon de profondeur qui ont été lues ou écrite récemment. Comme cela, pas besoin de les recharger plusieurs fois : on charge un bloc une fois pour toutes, et on le conserve pour gérer les fragments qui suivent.
Sur certaines cartes graphiques, les données dans le cache de profondeur sont stockées sous forme compressées dans le cache de profondeur, là encore pour augmenter la taille effective du cache. D'autres cartes graphiques ont un cache qui stocke des données décompressées dans le cache de profondeur. Tout est question de compromis entre accès rapide au cache et augmentation de la taille du cache.
Il faut savoir que les autres unités de la carte graphique peuvent lire le tampon de profondeur, en théorie. Cela peut servir pour certaines techniques de rendu, comme pour le ''shadowmapping''. De ce fait, il arrive que le cache de profondeur contienne des données qui sont copiées dans d'autres caches, comme les caches des processeurs de shaders. Le cache de profondeur n'est pas gardé cohérent avec les autres caches du GPU, ce qui signifie que les écritures dans le cache de profondeur ne sont pas propagées dans les autres caches du GPU. Si on modifie des données dans ce cache, les autres caches qui ont une copie de ces données auront une version périmée de la donnée. C'est souvent un problème, sauf dans le cas du cache de profondeur, pour lequel ce n'est pas nécessaire. Cela évite d'implémenter des techniques de cohérence des caches couteuses en circuits et en performance, alors qu'elles n'auraient pas d'intérêt dans ce cas précis.
===Le ''z-fast pass''===
Le ''z-fast pass'' améliore la performance des '''prépasses z''', une technique utilisée par de nombreux moteurs de jeux vidéo. L'idée est que le moteur de jeu effectue plusieurs passes, chacune faisant un truc précis, la prépasse z étant l'une de ces passes. Lors d'une prépasse z, le moteur de jeu calcule la scène 3D, rastérise l'image, et remplit le tampon de profondeur uniquement. Il le place pas de textures, ne calcule pas de pixels shaders, il se préoccupe uniquement des coordonnées de profondeur des pixels. Au final, le rendu ne donne que le tampon de profondeur, qui est utilisé par les passes suivantes.
L'utilité est très variable, mais il y a deux raisons pour effectuer une prépasse z : la performance, mais aussi certains effets graphiques. Par exemple, les effets d'occlusion ambiante "''screen space''" utilisent le tampon de profondeur pour faire leur travail. Il en est de même pour les ''shadowmaps'', qui effectuent une prépasse z par ombre à afficher. Une autre utilisation est que cela permet d'utiliser élimination des pixels cachés très performante. On effectue une prépasse z pour calculer le tampon de profondeur final, qui est ensuite utilisé par les passes suivantes pour éliminer les pixels cachés. Ainsi, les pixels cachés ne sont pas texturés et pixel shadés, avec certitude.
Toujours est-il qu'une prépasse z utilise les ROP "à moitié", dans le sens où seul le tampon de profondeur est utilisé, par la gestion des couleurs. Mais il se trouve que les circuits qui servent pour l''alpha blending'' peuvent être réutilisés pour faire les comparaisons de profondeur ! Le résultat est que les ROP peuvent fonctionner à double vitesse lors d'une prépasse z ! Cela demande cependant de concevoir les circuits du ROP pour en profiter. L'optimisation est parfois appelée le '''''z-fast pass'''''.
Tous les GPU depuis la Geforce FX en sont capables. Il y a cependant quelques contraintes. Premièrement, le ROP doit être configuré de manière à n’accéder qu'au tampon de profondeur, ils ne doivent pas dessiner dans le ''framebuffer''. L'''alpha blending'' doit être désactivé, de même que l'alpha-test. D'autres contraintes supplémentaires sont parfois présentes, surtout sur les vieux GPUs. Par exemple, l'antialiasing doit être désactivé lors de la prépasse z. Et mine de rien, cela ne marche que pour les prépasses z pures. Par exemple, certaines techniques de rendu différé augmentent la prépasse z pour que celle-ci ne calcule pas que le tampon de profondeur, mais aussi d'autres informations comme les normales : elles ne profitent pas de cette optimisation.
{{NavChapitre | book=Les cartes graphiques
| prev=Les unités de texture
| prevText=Les unités de texture
| next=Le support matériel du lancer de rayons
| nextText=Le support matériel du lancer de rayons
}}{{autocat}}
apf57q4jeyqc81jc1g9w28zk4rual02
763652
763651
2026-04-13T23:56:58Z
Mewtow
31375
763652
wikitext
text/x-wiki
Pour rappel, les étapes précédentes du pipeline graphiques manipulaient non pas des pixels, mais des fragments. Pour rappel, la distinction entre fragment et pixel est pertinente quand plusieurs objets sont l'un derrière l'autre. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. La couleur finale dépend de la couleur de tous ces points d'intersection. Intuitivement, l'objet le plus proche est censé cacher les autres et c'est donc lui qui décide de la couleur du pixel, mais cela demande de déterminer quel est l'objet le plus proche. De plus, certains objets sont transparents et la couleur finale est un mélange de la couleur de plusieurs points d'intersection.
Tout demande de calculer un pseudo-pixel pour chaque point d'intersection et de combiner leurs couleurs pour obtenir le résultat final. Les pseudo-pixels en question sont des '''fragments'''. Chaque fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont donc combinés pour obtenir la couleur finale de ce pixel. Pour résumer, la profondeur des fragments doit être gérée, de même que la transparence, etc.
Et c'est justement le rôle de l'étage du pipeline que nous allons voir maintenant. Ces opérations sont réalisées dans un circuit qu'on nomme le '''Raster Operations Pipeline''' (ROP), aussi appelé ''Render Output Target'', situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo.
==Les fonctions des ROP==
Les ROP incorporent plusieurs fonctionnalités qui sont assez diverses. Leur seul lien est qu'il est préférable de les implémenter en matériel plutôt qu'en logiciel, et en dehors des unités de textures. Il s'agit de fonctionnalités assez simples, basiques, mais nécessaires au fonctionnement de tout rendu 3D. Elles ont aussi pour particularité de beaucoup accéder à la mémoire vidéo. C'est la raison pour laquelle le ROP est situé en fin de pipeline, proche de la mémoire vidéo. Voyons quelles sont ces fonctionnalités.
Sa fonction la plus importante est l'élimination des pixels cachés, grâce au tampon de profondeur. Pour chaque fragment, il lit le pixel correspondant dans le tampon de profondeur, fait la comparaison de profondeur, et met à jour le tampon de profondeur. Nous en avons déjà beaucoup parlé dans les chapitres précédents, notamment dans le chapitre sur les bases du rendu 3D et dans celui sur le rastériseur (avec l'élimination précoce des pixels cachés).
Une autre fonction est le mélange ''alpha'' et le test ''alpha'', pour gérer la transparence, qu'on a là encore vu dans le chapitre sur les bases du rendu 3D. Là encore, les ROPs lisent, pour chaque fragment, le pixel correspondant dans le ''framebuffer'', font le mélange ''alpha'', et enregistrent le résultat dans le ''framebuffer''.
Mais les ROPs ont d'autres fonctions, plus méconnues, qu'on n'a pas abordé dans les chapitres précédents.
===Le tampon de ''stencil''===
Le '''''stencil''''' est une fonctionnalité des API graphiques qui existe depuis très longtemps. Il sert pour générer des effets graphiques très variés, qu'il serait vain de lister ici. Il a notamment été utilisé pour calculer des ombres volumétriques (le moteur de DOOM 3 en faisait grand usage à la base), des réflexions simples, des ''shadowmaps'', et bien d'autres.
Pour le résumer, on peut le voir comme une sorte de tampon de profondeur où la coordonnée z est remplacée par u octet dont le programmeur peut faire ce qu'il veut. L'idée est que chaque pixel/fragment se voit attribuer une valeur entière, généralement codée sur un octet, que les programmeurs peuvent faire varier à loisir. L'octet ajouté est appelé l''''octet de ''stencil'''''. Il a une certaine valeur, qui est calculée par la carte graphique, généralement par les ''shaders''. Il ne remplace pas la coordonnée de profondeur, mais s'ajoute à celle-ci.
Les octets de ''stencil'' sont placés dans le tampon de profondeur. L'ensemble forme un tableau qui associe 32 bits à chaque" pixel : 24 bits pour une coordonnée z, 8 pour l'octet de ''stencil''. Lors du passage d'un fragment les ROPs, la carte graphique lit le pixel correspondant, dans le tampon de profondeur. Il récupère la coordonnée z, mais aussi l'octet de ''stencil''. Puis il compare l'octet de ''stencil'' avec celui du fragment traité. Si le test échoue, le fragment ne passe pas à l'étape de test de profondeur et est abandonné. S'il passe, le tampon de ''stencil'' est mis à jour. Par mis à jour, on veut dire que le ROP peut faire diverses manipulations dessus : l'incrémenter, le décrémenter, le mettre à 0, inverser ses bits, remplacer par l'octet de ''stencil'' du fragment, etc. Les opérations possibles sont bien plus nombreuses qu'avec le tampon de profondeur, qui se contente de remplacer la coordonnée z par celle du fragment.
===Les effets de brouillard===
Les '''effets de brouillard''' sont des effets graphiques assez intéressants. Ils sont nécessaires dans certains jeux vidéo pour l'ambiance (pensez à des jeux d'horreur comme Silent Hill), mais ils ont surtout été utilisés pour économiser des calculs. L'idée est de ne pas calculer les graphismes au-delà d'une certaine distance, sans que cela se voie.
L'idée est d'avoir un ''view frustum'' limité : le plan limite au-delà duquel on ne voit pas les objets est assez proche de la caméra. Mais si le plan limite est trop proche, cela donnera une cassure inesthétique dans le rendu. Pour masquer cette cassure, les programmeurs ajoutaient un effet de brouillard. Les objets au-delà du plan limite étaient totalement dans le brouillard, puis ce brouillard se réduisait progressivement en se rapprochant de la caméra, avant de s'annuler à partir d'une certaine distance.
Pour calculer le brouillard, on mélange la couleur finale du pixel avec une ''couleur de brouillard'', la couleur de brouillard étant pondérée par la profondeur. Au-delà d'une certaine distance, l'objet est intégralement dans le brouillard : le brouillard domine totalement la couleur du pixel. En dessous d'une certaine distance, le brouillard est à zéro. Entre les deux, la couleur du brouillard et de l'objet devront toutes les deux être prises en compte dans les calculs. La formule de calcul exacte varie beaucoup, elle est souvent linéaire ou exponentielle.
Notons que ce calcul implique à la fois du mélange ''alpha'' mais aussi la coordonnée de profondeur, ce qui en fait que son implémentation dans les ROPs est l'idéal. Aussi, les premières cartes graphiques calculaient le brouillard dans les ROP, en fonction de la coordonnée de profondeur du fragment. De nos jours, il est calculé par les ''pixel shaders'' et les ROP n'incorporent plus de technique de brouillard spécialisée. Vu que les pixels shaders peuvent s'en charger, cela fait moins de circuits dans les ROPs pour un cout en performance mineur. Et ce d'autant plus que les effets de brouillard sont devenus assez rares de nos jours. Autant les émuler dans les pixels shaders que d'utiliser des circuits pour une fonction devenue anecdotique.
===Les autres fonctions des ROPs===
Les ROPS implémentent aussi des techniques utilisées sur les ''blitters'' des anciennes cartes d'affichage 2D, comme l'application d''''opérations logiques''' sur chaque pixel enregistré dans le ''framebuffer''. Les opérations logiques en question peuvent prendre une à deux opérandes. Les opérandes sont soit un pixel lu dans le ''framebuffer'', soit un fragment envoyé au ROP. Les opérations logiques à un opérande peuvent inverser, mettre à 0 ou à 1 le pixel dans le ''framebuffer'', ou faire la même chose sur le fragment envoyé en opérande. Les opérations à deux opérandes lisent un pixel dans le framebuffer, et font un ET/OU/XOR avec le fragment opérande (un opérande peut être inversé). Elles sont utilisées pour faire du traitement d'image ou du rendu 2D, rarement pour du rendu 3D.
Les ROPs gèrent aussi des '''masques d'écritures''', qui permettent de décider si un pixel doit être écrit ou non en mémoire. Il est possible d'inhiber certaines écritures dans le tampon de profondeur ou le tampon de couleur, éventuellement le tampon de stencil. Inhiber la mise à jour d'un pixel dans le tampon de profondeur est utile pour gérer la transparence. Si un pixel est transparent, même partiellement, il ne faut pas mettre à jour le tampon de profondeur, et cela peut être géré par ce système de masquage. Les masquages des couleurs permettent de ne modifier qu'une seule composante R/G/B au lieu de modifier les trois en même temps, pour faire certains effets visuels.
==L'architecture matérielle d'un ROP==
Les ROP contiennent des circuits pour gérer la profondeur des fragments. Ils effectuent un test de profondeur, à savoir que les fragments correspondant à un même pixel sont comparés pour savoir lequel est devant l'autre. Ils contiennent aussi des circuits pour gérer la transparence des fragments. Le ROP gère aussi l'antialiasing, de concert avec l'unité de rastérisation. D'autres fonctionnalités annexes sont parfois implémentées dans les ROP. Par exemple, les vielles cartes graphiques implémentaient les effets de brouillards dans les ROPs. Le tout est suivi d'une unité qui enregistre le résultat final en mémoire, où masques et opérations logiques sont appliqués.
Les différentes opérations du ROP doivent se faire dans un certain ordre. Par exemple, gérer la transparence demande que les calculs de profondeur se fassent globalement après ou pendant le mélange ''alpha''. Ou encore, les masques et opérations logiques se font à la toute fin du rendu. L'ordre des opérations est censé être le suivant : test ''alpha'', test du ''stencil'', test de profondeur, mélange ''alpha''. Du moins, la carte graphique doit donner l'impression que c'est le cas. Elle peut optimiser le tout en traitant le tampon de profondeur, de couleur et de ''stencil'' en même temps, mais donner les résultats adéquats.
Un ROP est typiquement organisé comme illustré ci-dessous. Notons que les circuits de gestion de la profondeur et de la transparence sont séparés dans les schémas, mais il s'agit là d'une commodité qui ne reflète pas forcément l'implémentation matérielle. Et si ces deux circuits sont séparés, ils communiquent entre eux, notamment pour gérer la profondeur des fragments transparents.
[[File:Render Output Pipeline-processor.png|centre|vignette|upright=2|Render Output Pipeline-processor]]
Les ROPs récupèrent les fragments calculés par les pixels shaders et/ou les unités de texture, via un circuit d'interconnexion spécialisé. Chaque ROP est connecté à toutes les unités de ''shader'', même si la connexion n'est pas forcément directe. Toute unité de ''shader'' peut envoyer des pixels à n'importe quel ROP. Les circuits d'interconnexion sont généralement des réseaux d'interconnexion de type ''crossbar'', comme illustré ci-contre (le premier rectangle rouge).
Le ROP effectue beaucoup de lectures et écritures en mémoire vidéo. Or, la bande passante mémoire est limitée, ce qui fait que le ROP est un goulot d'étranglement assez important pour le rendu 3D. Heureusement, de nombreuses optimisations permettent d'optimiser le tout. Elles agissent sur la lecture du tampon de profondeur, mais aussi sur le ''framebuffer''.
===Le ''fast clear'' du ''framebuffer''===
Une première optimisation porte sur le ''framebuffer''. Le ''framebuffer''est souvent réutilisé d'une image sur l'autre. Quand une image a été envoyée à l'écran, le ''framebuffer'' est remis à zéro pour accueillir une nouvelle image. Et ce avec ou sans ''double buffering''. La mise à zéro est censée se faire en remettant réellement le ''framebuffer'' à zéro, en écrivant des 0 pour chaque pixel du ''framebuffer''. Mais il y a moyen de s'en passer.
Pour cela, l'idée est que le ''framebuffer'' est découpé en ''tiles'', des carrés de 4, 8, 16 pixels de côté. Les ''tiles'' ont généralement la même taille que les ''tiles'' utilisées pour la rastérisation, mais passons sur ce détail. L'idée est de mémoriser, pour chaque ''tile'', si elle est mise à 0 ou non. Il suffit de cela d'un seul bit par ''tile'', appelé le bit RESET. L'ensemble des bits RESET est mémorisé dans une petite mémoire SRAM, intégrée aux ROPs.
Lorsqu'on souhaite remettre à zéro le ''framebuffer'', il suffit de mettre à 0 tous les bits RESET dans cette SRAM, pas besoin d’accéder à la mémoire vidéo. Avant toute lecture dans le ''framebuffer'', le ROP lit cette SRAM pour vérifier si la ''tile'' en question a été remise à 0. Si ce n'est pas le cas, il lit le pixel voulu depuis le ''framebuffer''. Mais si c'est le cas, alors le ROP ne fait pas la lecture et fournit un pixel à zéro à la place, qui est utilisé pour le mélange ''alpha'' ou autre. La moindre écriture dans une ''tile'' met le bit RESET à 0 : la ''tile'' entière est considérée comme non-remise à zéro, même si un seul pixel a été modifié dedans.
Notons que l'usage d'une granularité par ''tile'' est un compromis. On peut ne peut pas utiliser un bit par pixel, car cela demanderait d'utiliser une SRAM énorme. De même, utiliser un seul bit pour tout le ''framebuffer'' ruinerait totalement l'optimisation : le ''framebuffer'' entier serait considéré comme non-RESET dès la première écriture d'un pixel dedans, on ne sauverait qu'un nombre trop limité d'accès mémoire.
===La z-compression===
La technique de '''z-compression''' compresse le tampon de profondeur. Plus précisément, elle découpe le tampon de profondeur en ''tiles'', en blocs carrés, qui sont compressés séparément les uns des autres. La taille des ''tiles'' est souvent la même que celle utilisée par le rastériseur pour la rastérisation grossière. Par exemple, la ''z-compression'' des cartes graphiques ATI radeon 9800, découpait le tampon de profondeur en ''tiles'' de 8 * 8 fragments, et les encodait avec un algorithme nommé DDPCM (''Differential differential pulse code modulation'').
Précisons que cette compression ne change pas la taille occupée par le tampon de profondeur, mais seulement la quantité de données lue/écrite. La raison est que les ''tiles'' doivent avoir une place fixe en mémoire. Par exemple, si une ''tile'' non-compressée prend 64 octets, on trouvera une ''tile'' tous les 64 octets en mémoire vidéo, afin de simplifier les calculs d'adresse, afin que le ROP sache facilement où se trouve la ''tile'' à lire/écrire. Avec une vraie compression, les ''tiles'' se trouveraient à des endroits très variables d'une image à l'autre.
Par contre, la z-compression réduit la quantité de données écrite dans le tampon de profondeur. Par exemple, au lieu d'écrire une ''tile'' non-compressée de 64 octets, on écrira une ''tile'' de seulement 6 octets, les 58 octets restants étant pas lus ou écrits. On obtient un gain en performance, pas en mémoire.
[[File:AMD HyperZ.svg|centre|vignette|upright=2|AMD HyperZ]]
Le format de compression ajoute un bit par ''tile'', qui indique si elle est compressée ou non. Le bit qui indique si la ''tile'' est compressée permet de laisser certaines ''tiles'' non-compressés, dans le cas où la compression ne permet pas de gagner de la place. La compression ajoute souvent un second bit, qui indique si la ''tile'' est à zéro ou non, sur le même modèle que pour le ''framebuffer''. Il accélère la remise à zéro du tampon de profondeur. Au lieu de réellement remettre tout le tampon de profondeur à 0, il suffit de réécrire un bit par ''tile''. Le gain en nombre d'accès mémoire peut se révéler assez impressionnant.
Les deux bits en question peuvent être placés à deux endroits différents. La première solution serait d'utiliser une portion de la mémoire vidéo, mais cela demanderait de faire deux lectures par accès au tampon de profondeur. La vraie solution est d'utiliser une SRAM reliée aux ROPs, qui est assez grande pour mémoriser tout le tampon de profondeur, du moins avec deux bits par ''tile''.
===Le cache de profondeur===
Une optimisation complémentaire ajoute une ou plusieurs mémoires caches dans le ROP, dans le circuit de profondeur. Ce '''cache de profondeur''' stocke des portions du tampon de profondeur qui ont été lues ou écrite récemment. Comme cela, pas besoin de les recharger plusieurs fois : on charge un bloc une fois pour toutes, et on le conserve pour gérer les fragments qui suivent.
Sur certaines cartes graphiques, les données dans le cache de profondeur sont stockées sous forme compressées dans le cache de profondeur, là encore pour augmenter la taille effective du cache. D'autres cartes graphiques ont un cache qui stocke des données décompressées dans le cache de profondeur. Tout est question de compromis entre accès rapide au cache et augmentation de la taille du cache.
Il faut savoir que les autres unités de la carte graphique peuvent lire le tampon de profondeur, en théorie. Cela peut servir pour certaines techniques de rendu, comme pour le ''shadowmapping''. De ce fait, il arrive que le cache de profondeur contienne des données qui sont copiées dans d'autres caches, comme les caches des processeurs de shaders. Le cache de profondeur n'est pas gardé cohérent avec les autres caches du GPU, ce qui signifie que les écritures dans le cache de profondeur ne sont pas propagées dans les autres caches du GPU. Si on modifie des données dans ce cache, les autres caches qui ont une copie de ces données auront une version périmée de la donnée. C'est souvent un problème, sauf dans le cas du cache de profondeur, pour lequel ce n'est pas nécessaire. Cela évite d'implémenter des techniques de cohérence des caches couteuses en circuits et en performance, alors qu'elles n'auraient pas d'intérêt dans ce cas précis.
===Le ''z-fast pass''===
Le ''z-fast pass'' améliore la performance des '''prépasses z''', une technique utilisée par de nombreux moteurs de jeux vidéo. L'idée est que le moteur de jeu effectue plusieurs passes, chacune faisant un truc précis, la prépasse z étant l'une de ces passes. Lors d'une prépasse z, le moteur de jeu calcule la scène 3D, rastérise l'image, et remplit le tampon de profondeur uniquement. Il ne place pas de textures, ne calcule pas de pixels shaders, il se préoccupe uniquement des coordonnées de profondeur des pixels. Au final, le rendu ne donne que le tampon de profondeur, qui est utilisé par les passes suivantes.
L'utilité est très variable, mais il y a deux raisons pour effectuer une prépasse z : la performance, mais aussi certains effets graphiques. Par exemple, les effets d'occlusion ambiante "''screen space''" utilisent le tampon de profondeur pour faire leur travail. Il en est de même pour les ''shadowmaps'', qui effectuent une prépasse z par ombre à afficher. Une autre utilisation est que cela permet d'utiliser élimination des pixels cachés très performante. On effectue une prépasse z pour calculer le tampon de profondeur final, qui est ensuite utilisé par les passes suivantes pour éliminer les pixels cachés. Ainsi, les pixels cachés ne sont pas texturés et pixel shadés, avec certitude.
Toujours est-il qu'une prépasse z utilise les ROP "à moitié", dans le sens où seul le tampon de profondeur est utilisé, par la gestion des couleurs. Mais il se trouve que les circuits qui servent pour le mélange ''alpha'' peuvent être réutilisés pour faire les comparaisons de profondeur ! Le résultat est que les ROP peuvent fonctionner à double vitesse lors d'une prépasse z ! Cela demande cependant de concevoir les circuits du ROP pour en profiter. L'optimisation est parfois appelée le '''''z-fast pass'''''.
Tous les GPU depuis la Geforce FX en sont capables. Il y a cependant quelques contraintes. Premièrement, le ROP doit être configuré de manière à n’accéder qu'au tampon de profondeur, ils ne doivent pas dessiner dans le ''framebuffer''. Le mélange '''alpha'' doit être désactivé, de même que l'alpha-test. D'autres contraintes supplémentaires sont parfois présentes, surtout sur les vieux GPUs. Par exemple, l'antialiasing doit être désactivé lors de la prépasse z. Et mine de rien, cela ne marche que pour les prépasses z pures. Par exemple, certaines techniques de rendu différé augmentent la prépasse z pour que celle-ci ne calcule pas que le tampon de profondeur, mais aussi d'autres informations comme les normales : elles ne profitent pas de cette optimisation.
{{NavChapitre | book=Les cartes graphiques
| prev=Les unités de texture
| prevText=Les unités de texture
| next=Le support matériel du lancer de rayons
| nextText=Le support matériel du lancer de rayons
}}{{autocat}}
aqjsm6kiwvjtkttdqxown9znig920jv
Fonctionnement d'un ordinateur/Les registres du processeur
0
69030
763580
762937
2026-04-13T14:12:56Z
Mewtow
31375
/* La taille des registres flottants et les doubles arrondis */ Déplacement autre chapitre
763580
wikitext
text/x-wiki
Le processeur incorpore un ou plusieurs registres, des mémoires de petite taille, capables de mémoriser un nombre entier/flottant. Naïvement, les registres sont utilisés pour stocker les opérandes des instructions et leur résultat. Un programmeur (ou un compilateur) qui programme en langage machine manipule ces registres intégrés dans le processeur. Cependant, tous les registres d'un processeur ne sont pas forcément manipulables par le programmeur. Il faut distinguer les '''registres architecturaux''', manipulables par des instructions, des '''registres internes''' aux processeurs.
==Les différents types de registres architecturaux==
Dans ce qui suit, nous allons parler uniquement des registres architecturaux. Les registres internes seront vu dans les chapitre sur la microarchitecture d'un processeur. Ils servent à simplifier la conception du processeur, à mettre en œuvre des optimisations de performance. Les registres architecturaux, eux, font partie de l'interface que le processeur fournit aux programmeurs. Ils font partie du jeu d'instruction, qui liste les registres, les instructions supportées, comment instructions et registres interagissent, etc. Il existe plusieurs types de registres architecturaux, qui sont assez difficiles à classer, que nous allons décrire ci-dessous.
===Le registre d'état (entier)===
Le '''registre d'état''' est un registre aux fonctions assez variées, qui varient selon le processeur. Au minimum, il contient des bits qui indiquent le résultat d'une instruction de test. Il contient aussi d'autres bits, mais dont l'interprétation dépend du jeu d'instruction. En général, le registre d'état contient les bits suivants :
* le bit d'''overflow'', qui est mis à 1 lors d'un débordement d'entiers ;
* le bit de retenue, qui indique si une addition/soustraction a donné une retenue ;
* le bit ''null'' précise que le résultat d'une instruction est nul (vaut zéro) ;
* le bit de signe, qui permet de dire si le résultat d'une instruction est un nombre négatif ou positif.
Le registre d'état est mis à jour par les instructions de test, mais aussi par les instructions arithmétiques entières (sur des opérandes entiers). Par exemple, si une opération arithmétique entraine un débordement d'entier, le registre d'état mémorisera ce débordement. Dans le chapitre précédent, nous avions vu que les débordements sont mémorisés par le processeur dans un bit dédié, appelé le bit de débordement. Et bien ce dernier est un bit du registre d'état. Il en est de même pour le bit de retenue vu dans le chapitre précédent, qui mémorise la retenue effectuée par une opération arithmétique comme une addition, une soustraction ou un décalage.
Le bit de débordement est parfois présent en double : un bit pour les débordements pour les nombres non-signés, et un autre pour les nombres signés (en complément à deux). En effet, la manière de détecter les débordements n'est pas la même pour des nombres strictement positifs et pour des nombres en complément à deux. Certains processeurs s'en sortent avec un seul bit de débordement, en utilisant deux instructions d'addition : une pour les nombres signés, une autre pour les nombres non-signés. Mais d'autres processeurs utilisent une seule instruction d'addition pour les deux, qui met à jour deux bits de débordements : l'un qui détecte les débordements au cas où les deux opérandes sont signés, l'autre si les opérandes sont non-signées. Sur les processeurs ARM, c'est la seconde solution qui a été choisie.
N'oublions pas les bits de débordement pour les entiers BCD, à savoir le bit de retenue et le bit ''half-carry'', dont nous avions parlé au chapitre précédent.
Sur certains processeurs, comme l'ARM1, chaque instruction arithmétique existe en deux versions : une qui met à jour le registre d'état, une autre qui ne le fait pas. L'utilité de cet arrangement n'est pas évident, mais il permet à certaines instructions arithmétiques de ne pas altérer le registre d'état, ce qui permet de conserver son contenu pendant un certain temps.
Le fait que le registre d'état est mis à jour par les instructions arithmétiques permet d'éviter de faire certains tests gratuitement. Par exemple, imaginons un morceau de code qui doit vérifier si deux entiers A et B sont égaux, avant de faire plusieurs opérations sur la différence entre les deux (A-B). Le code le plus basique pour cela fait la comparaison entre les deux entiers avec une instruction de test, effectue un branchement, puis fait la soustraction pour obtenir la différence, puis les calculs adéquats. Mais si la soustraction met à jour le registre d'état, on peut simplement faire la soustraction, faire un branchement qui teste le bit ''null'' du registre d'état, puis faire les calculs. Une petite économie toujours bonne à prendre.
Il faut noter que certaines instructions sont spécifiquement conçues pour altérer uniquement le registre d'état. Par exemple, sur les processeurs x86, certaines instructions ont pour but de mettre le bit de retenue à 0 ou à 1. Il existe en tout trois instructions capables de manipuler le bit de retenue : l'instruction CLC (CLear Carry) le met à 0, l'instruction STC (SeT Carry) le met à 1, l'instruction CMC (CompleMent Carry) l'inverse (passe de 0 à 1 ou de 1 à 0). Ces instructions sont utilisées de concert avec les instructions d'addition ADDC (ADD with Carry) et SUBC (SUB with Carry), qui effectuent le calcul A + B + Retenue et A - B - Retenue, et qui sont utilisées pour additionner/soustraire des opérandes plus grandes que les registres. Nous avions vu ces instructions dans le chapitre sur les instructions machines, aussi je ne reviens pas dessus.
Le registre d'état n'est pas présent sur toutes les architectures, notamment sur les jeux d'instruction modernes, mais beaucoup d'architectures anciennes en ont un.
===Le ''program counter''===
Le '''''Program Counter''''' mémorise l'adresse de l’instruction en cours ou de la prochaine instruction (le choix entre les deux dépend du processeur). C'est bel et bien un registre architectural, car ils sont manipulés par les instructions de branchement, bien qu'implicitement. Ce n'est pas un registre utilisé à des fins d'optimisation ou de simplicité d'implémentation.
Lors du démarrage ou redémarrage, il faut initialiser le ''program counter'' avec l'adresse de la première instruction. Le processeur est câblé pour lire une adresse bien précise lors du ''boot'', qui porte le nom d''''adresse de démarrage''', '''CPU Reset vector''' en anglais. L'adresse de démarrage n'est pas toujours l'adresse 0 : les premières adresses peuvent être réservées pour la pile ou le vecteur d'interruptions. Pour déterminer l'adresse de démarrage, il y a deux solutions : soit on utilise une adresse fixée une fois pour toutes, soit l'adresse est configurable.
* Avec la première solution, la plus simple, le processeur démarre l’exécution du code à une adresse bien précise, toujours la même, câblée dans ses circuits. Il s'agit typiquement de l'adresse 0 : le processeur lit le contenu de la toute première adresse et démarre l’exécution du programme à cet endroit.
* Avec la seconde solution, on ajoute un niveau de redirection. Le processeur accède toujours à une adresse fixe au démarrage, mais l'adresse en question ne contient pas la première instruction à exécuter. À la place, elle contient l'adresse de la première instruction. Le processeur lit donc cette adresse, puis la copie dans son ''program counter''. En clair, il effectue un branchement.
Il existe des processeurs où le ''Program Counter'' est adressable, via un nom de registre. Sur ces processeurs, on peut parfaitement lire ou écrire dans le ''Program Counter'' sans trop de problèmes. Ainsi, au lieu d'effectuer des branchements sur le ''Program Counter'', on peut simplement utiliser une instruction qui ira écrire l'adresse à laquelle brancher dans le registre. On peut même faire des calculs sur le contenu du ''Program Counter'' : cela n'a pas toujours de sens, mais cela permet parfois d'implémenter facilement certains types de branchements avec des instructions arithmétiques usuelles.
Le ''program counter'' et le registre d'état sont parfois fusionnés en un seul registre appelé le '''''Program status word''''', abrévié en PSW. L'avantage est que le ''Program status word'' regroupe tout ce qui est utile pour les branchements et test. Les branchements écrivent dans le ''program counter'' pour brancher à l'adresse finale, lire l'adresse dans le ''program counter'' pour certains branchements dits relatifs, les tests/branchements peuvent lire le registre d'état. Avec un PSW, tout cela est regroupé dans le PSW, les tests et branchements altérent tous deux le PSW. L'avantage est mineur et pose des problèmes niveau implémentation matérielle.
Il peut y avoir un avantage en terme de taille des registres. Par exemple, l'ARM1 fusionne le registre d'état et le ''program counter'' en un seul registre de 32 bits. La raison à cela est que ses registres font 32 bits, que le ''program counter'' n'a besoin que de 24 bits pour fonctionner ce qui laisse 8 bits pour le registre d'état. Précisément, le ''program counter'' est censé gérer des adresses de 26 bits, mais les instructions de ce processeur font exactement 32 bits et elles sont alignées en mémoire, ce qui fait que les 2 bits de poids faibles du ''program counter'' sont inutilisés. Au total, cela fait 8 bits inutilisés. Et ils ont été réutilisés pour mémoriser les bits du registre d'état.
===Les registres de contrôle===
Les '''registres de contrôle''' permettent de configurer le processeur pour qu'il fonctionne comme souhaité. Ils sont très variables et dépendent fortement du jeu d'instruction, mais aussi du modèle de processeur considéré. Quelques fonctionnalités importantes sont gérées par ce registre, même si on ne peut pas encore en parler. Des fonctionnalités comme la désactivation des interruptions ou la gestion du mode noyau/hyperviseur, par exemple.
Des bits de contrôle sont dédiés à la gestion du cache. Il est ainsi possible de configurer le cache, voire de le désactiver. Nous ne pouvons pas en parler en détail ici, car nous ne savons pas comment fonctionne une mémoire cache pour le moment. Mais nous détaillerons les bits de contrôle du cache dans le chapitre sur la mémoire cache. Pour le moment, nous ne pouvons parler que d'un seul bit de contrôle du cache :; celui qui l'active ou le désactive.
===Les registres généraux : entiers et adresses===
Les registres de données mémorisent des informations comme des entiers, des adresses, des flottants, manipulés par un programme. Ils sont classés en deux grand types, appelés registres entiers et flottants, dont les noms sont assez transparents. Les registres entiers sont spécialement conçus pour stocker des nombres entiers. Les registres entiers sont aussi appelés des registres généraux, car ils servent non seulement pour les entiers, mais aussi les adresses et d'autres informations codées en binaire.
Les registres entiers ne font pas que mémoriser les opérandes/résultats, et peuvent contenir n'importe quelle information codée par des nombres entiers. Notamment, ils peuvent mémoriser des adresses mémoire. L'avantage est que cela permet de faire des calculs sur des adresses mémoire, chose très importante pour supporter des structures de données comme les tableaux. Nous en reparlerons plus en détail dans le chapitre sur les modes d'adressage.
Pour le moment, vous avez juste à savoir que les registres entiers sont en réalité des '''registres généraux''' utilisables pour tout et n'importe quoi, qui peuvent stocker toute sorte d’information codée en binaire. Par exemple, un processeur avec 8 registres généraux pourra les utiliser sans vraiment de restrictions. On pourra s'en servir pour stocker 8 entiers, 6 entiers et 2 adresses, 1 adresse et 5 entiers, etc. Ce qui sera plus flexible et utilisera les registres disponibles au maximum.
De nombreux processeurs incorporent des registres entiers ou flottants en lecture seule, qui contiennent des constantes assez souvent utilisées. Par exemple, certains processeurs possèdent des registres initialisés à zéro pour accélérer la comparaison avec zéro ou l'initialisation d'une variable à zéro. On peut aussi citer certains registres flottants qui stockent des nombres comme pi, ou e pour faciliter l'implémentation des calculs trigonométriques. Ils sont appelés des '''registres de constante''', leur nom étant assez clair.
===Les registres flottants===
Les '''registres flottants''' sont spécialement conçus pour stocker des nombres flottants. Ils ne sont présents que sur les processeurs qui supportent les nombres flottants. Tous les processeurs modernes séparent les registres flottants et entiers, pour de bonnes raisons. Une des raisons est que les flottants et entiers n'ont pas le même encodage et n'ont pas forcément la même taille. Les flottants font 32 et 64 bits, ce qui posait problème sur les architectures 32 bits. Mais surtout, les flottants et entiers sont vraiment traités séparément dans le processeur : ils ont des circuits de calcul distincts, ils sont traités par des instructions séparées. Les mettre dans des registres séparés aide beaucoup pour la conception du processeur, comme on le verra dans quelques chapitres. Et cela n'entraine pas de problèmes de performances.
Les processeurs qui gèrent les nombres flottants incorporent aussi un '''registre d'état flottant''', qui s'occupe des nombres flottants. Sur les CPU x86, qui utilisaient l'extension x87, il était appelé le ''Status Word''. Celui-ci fait lui aussi 16 bits et contient tout ce qu'il faut pour qu'un programme puisse comprendre la cause d'une exception. Voici son contenu, à peu de chose près.
{|class="wikitable"
|-
! Bit
! Utilité
|-
| U
| Mis à 1 lorsqu'un débordement a lieu.
|-
| O
| Pareil que U, mais pour les overflow
|-
| Z
| Bit mis à 1 lors d'une division par zéro
|-
| D
| Bit mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
| I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Les '''registres de contrôle flottant''' configurent les opérations flottantes. Ils configurent quel mode d'arrondi utiliser, comment traiter les infinis, si les flottants utilisés sont simple (32 bits) ou double précision (64 bits). Pour donner un exemple, voici le registre ''control word'' utilisé sur les anciens CPU x86, pour l'extension x87. L'extension x87 ajoutait le support des nombres flottants aux CPU x86, mais ceux-ci n'étaient pas tout à fait compatibles avec la norme IEEE 754. Une différence notable est que les flottants étaient codés sur 80 bits maximum.
{|class="wikitable"
|-
! Bit
! Utilité
|-
| Infinity Control
| Mode de gestion des infinis, codé sur 2 bits :
* 0 : Les infinis sont tous traités comme s'ils valaient <math>+ \infty</math>.
* 1 : Les infinis sont traités normalement.
|-
| Rouding Control
| Mode d'arrondi codé sur 2 bits :
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
| Precision Control
| Taille de la mantisse, configurée via deux bits. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
===Les registres d'adresse et d'indice===
Quelques processeurs incorporent des registres spécialisés dans les adresses et leur calcul. Les '''registres d'adresse''' contiennent des adresses. Ils étaient surtout présents sur les architectures 16 bits, plus rarement sur les architectures 32 bits. L'usage de registres d'adresse s'explique par le fait que sur les anciennes architectures, les adresses n'ont pas la même taille que les données.
Un exemple est celui des processeurs Motorola 68000, sur lequel les entiers faisaient 32 bits et les adresses faisaient 24 bits. Le packaging du processeur ne permettait pas de mettre trop de broches, ce qui fait que les broches d'adresse étaient limitée à 24 bits, ce qui était suffisant pour l'époque. L'usage de registres d'adresse séparés des registres entiers permettait de gérer au mieux cette différence de taille. Ce problème a été corrigé à l'arrivée du 68020, qui avait des adresses sur 32 bits et 32 broches d'adresse, mais a conservé la séparation entre registres d'adresse et entiers pour des raisons de compatibilité.
Un autre exemple est celui du processeur du CDC 6600, qui avait 8 registres d'adresse couplés à 8 registres d'entrée. Les registres d'adresse fonctionnaient d'une manière totalement inédite, qu'on ne retrouve pas sur d'autres processeurs avec registre d'adresse. Tout registre d'adresse était associé à un registre entier. Concrètement, les 8 registres d'adresse étaient numérotés de 0 à 7, idem pour les registres entier. Le CDC 6600 n'avait pas d'instruction LOAD ou STORE, tout passait par des écritures dans ces registres. Le comportement dépendait du registre concerné.
* Une écriture dans le registre A0 ne faisait rien, il sert d'exception. Le registre D0 n'est pas altéré lors d'une écriture dans le registre A0.
* Les registres A1 à A5 servaient pour les lectures. L'écriture d'une adresse dans un de ces registres entrainait une lecture de cette adresse. La donnée lue était copiée automatiquement dans le registre entier associé, le registre entier de même numéro.
* Les registres A5 à A7 servaient pour les écriture. L'écriture d'une adresse dans un de ces registres entrainait une écriture à cette adresse. La donnée à écrire était prise dans dans le registre entier associé, le registre entier de même numéro.
L'usage de registres d'adresse dédiés est très rare, les processeurs préfèrent utiliser des registres généraux qui servent à la fois de registres entier et de registres d'adresse. La raison est que les adresses sont encodés avec des entiers en binaire. Les opérations effectuées sur les adresses sont des opérations entières basiques : additions/soustractions, parfois multiplications entières, opérations de masquage, bit à bit, etc. Aussi, séparer adresses et entiers dans des registres séparés n'est pas très pertinent.
Prenons un exemple : j'ai un processeur disposant d'un Program Counter, de 4 registres entiers et de 4 registres d'adresse. Si j’exécute un morceau de programme qui ne manipule presque pas d'adresses, mais fait beaucoup de calcul, les 4 registres d'adresse seront sous-utilisés alors que je manquerais de registres entiers. Utiliser 8 registres généraux permet de contourner le problème. On peut se servir de ces 8 registres généraux pour stocker 8 entiers, 6 entiers et 2 adresses, 1 adresse et 5 entiers, etc. Ce qui sera plus flexible et utilisera les registres disponibles au maximum.
Les '''registres d'indice''' servent à calculer des adresses, afin de manipuler rapidement des données complexes comme les tableaux. Ils étaient présents sur les premiers ordinateurs et ont perduré jusqu’aux architectures 16 bits inclues. Dans les faits, ils étaient présent sur une classe particulière de processeurs, appelés les architectures à accumulateur, qui aura droit à son chapitre dédié. Nous parlerons en détail des registres d'indice dans ce chapitre dédié aux architectures à accumulateur.
===Les registres de débogage===
Les '''registres de débogage''' sont des cousins des registres d'état, et sont utilisés par des logiciels appelés des débogueurs. les débogueurs sont des logiciels qui permettent de trouver des ''bugs'' dans un programme. Ils permettent d'analyser l'exécution d'un programme dans le détail. Par exemple, ils peuvent exécuter le programme instruction par instruction et mémoriser l'état du processeur après chaque instruction. Mais surtout : ils peuvent stopper l'exécution du programme à un point bien précis, appelé un '''''breakpoint'''''. Lorsque le programme est interrompu, l'état du processeur est mémorisé, que ce soit les registres, la pile d'appel, ou bien d'autres informations.
Pour faciliter le travail des débogueurs, certains processeurs intègrent des registres de débogages. Par exemple, les processeurs x86 intégrent 6 registres de débogages, nommés DR0, DR1, DR2, DR3, DR6 et DR7. Les registres DR4 et DR5 n'existent pas, la raison n'est pas connue.
Les registres DR0 à DR3 facilitent l'interruption du programme à un point précis. Le ''breakpoint'' en question correspond à une instruction précise, qui est choisie par un humain, là où il suppose que le bug est déclenché. Le débogueur doit stopper le programme quand il atteint cette instruction. Pour cela, lorsque le ''program counter'' atteint une adresse de ''breakpoint'', il stoppe son exécution. Pour cela, les registres DR0, DR1, DR2 et DR3 contiennent chacun l'adresse d'un ''breakpoint'', ce qui permet de programmer 4 ''breakpoint'' différents.
Les registres DR6 et DR7 permettent de programmer sous quelles conditions l'interruption a lieu. Par exemple il est possible de stopper le programme : soit si l'instruction s'exécute, soit si elle écrit quelque chose dans la mémoire RAM, soit en cas de lecture/écriture en RAM. Il est aussi possible d'activer ou de désactiver chaque registre de débogage. Par exemple, si l'utilisateur ne veut utiliser deux ''breakpoints'', il peut désactiver les registres DR2 et DR3. Cela se fait en configurant quelques bits dans le registre DR7.
==L'adressage des registres architecturaux==
Outre leur taille, les registres du processeur se distinguent aussi par la manière dont on peut les adresser, les sélectionner. Les registres du processeur peuvent être adressés par trois méthodes différentes. À chaque méthode correspond un mode d'adressage différent. Les modes d'adressage des registres sont les modes d'adressages absolu (par adresse), inhérent (à nom de registre) et/ou implicite.
===Les registres nommés===
Dans le premier cas, chaque registre se voit attribuer une référence, une sorte d'identifiant qui permettra de le sélectionner parmi tous les autres. C'est un peu la même chose que pour la mémoire RAM : chaque byte de la mémoire RAM se voit attribuer une adresse. Pour les registres, c'est un peu la même chose : ils se voient attribuer quelque chose d'équivalent à une adresse, une sorte d'identifiant qui permettra de sélectionner un registre pour y accéder.
L'identifiant en question est ce qu'on appelle un '''nom de registre''' ou encore un '''numéro de registre'''. Ce nom n'est rien d'autre qu'une suite de bits attribuée à chaque registre, chaque registre se voyant attribuer une suite de bits différente. Celle-ci sera intégrée à toutes les instructions devant manipuler ce registre, afin de sélectionner celui-ci. Le numéro/nom de registre permet d'identifier le registre que l'on veut, mais ne sort jamais du processeur, il ne se retrouve jamais sur le bus d'adresse. Les registres ne sont donc pas identifiés par une adresse mémoire.
[[File:Adressage des registres via des noms de registre.png|centre|vignette|upright=2|Adressage des registres via des noms de registre.]]
===Les registres adressés===
Mais il existe une autre solution, utilisée sur de très vieux ordinateurs des années 50 à 70, ou quelques microcontrôleurs. C'est le cas du PDP-10.. L'idée est d'adresser les registres via une adresse mémoire. Les registres se voient attribuer les adresses mémoires les plus basses, à partir de l'adresse 0. Par exemple, un processeur avec 16 registres utilisait les 16 adresses basses, une par registre.
[[File:Adressage des registres via des adresses mémoires.png|centre|vignette|upright=2|Adressage des registres via des adresses mémoires.]]
===Les registres adressés implicitement===
Certains registres n'ont pas forcément besoin d'avoir un nom. Par exemple, c'est le cas du ''Program Counter'' : à part sur certains processeurs vraiment très rares, on ne peut modifier son contenu qu'en utilisant des instructions de branchements. Idem pour le registre d'état, manipulé obligatoirement par les instructions de comparaisons et de test, et certaines opérations arithmétiques.
Dans ces cas bien précis, on n'a pas besoin de préciser le ou les registres à manipuler : le processeur sait déjà quels registres manipuler et comment, de façon implicite. Le seul moyen de manipuler ces registres est de passer par une instruction appropriée, qui fera ce qu'il faut. Mais précisons encore une fois que sur certains processeurs, le registre d'état et/ou le ''Program Counter'' sont adressables.
==La taille des registres architecturaux==
Vous avez certainement déjà entendu parler de processeurs 32 ou 64 bits, voire de processeurs 8 ou 16 bits pour les ordinateurs anciens. Derrière cette appellation qu'on retrouve souvent dans la presse ou comme argument commercial se cache un concept simple, appelé la '''taille des registres'''. Il s'agit de la quantité de bits qui peuvent être stockés dans les registres principaux. Le même terme était autrefois utilisé pour les consoles de jeu, où il était censé désigner la même chose, à savoir la taille des registres du processeur. Mais l'utilisation sur les consoles de jeu était moins stricte, les fabricants de consoles n'hésitait pas à faire gonfler les chiffres par intérêt marketing.
Les registres principaux en question dépendent de l'architecture. Sur les architectures avec des registres généraux, la taille des registres est celle des registres généraux. Sur les autres architectures, la taille mentionnée est généralement celle des nombres entiers, les autres registres peuvent avoir une taille totalement différente. Notamment, sur les processeurs 8 bits, il y a souvent une différence entre la taille des entiers (codés sur 8 bits) et les adresses (codées sur 16 à 24 bits). Dans ce cas, un processeur 8 bits peut parfaitement gérer des adresses 16 ou 24 bits, mais reste un processeur 9 bits par ses entiers sont codés sur 8 bits.
Aujourd'hui, les processeurs utilisent presque tous des registres dont la taille est une puissance de 2 : 8, 16, 32, 64, 128, 256, voire 512 bits. L'usage de registres qui ne sont pas des puissances de 2 posent quelques problèmes techniques en termes d’adressage, comme on le verra dans le chapitre sur l'alignement et le boutisme. Mais ca n'a pas toujours été le cas. Par exemple, les processeurs dédiés au traitement de signal audio, que l'on trouve dans les chaînes HIFI, les décodeurs TNT, les lecteurs DVD, etc. Ceux-ci utilisent des registres de 24 bits, car l'information audio est souvent codée par des nombres de 24 bits.
Aux tout début de l'informatique, les processeurs utilisaient tous l'encodage BCD et codaient leurs chiffres sur 4/5/6/7 bits. La taille des registres était donc un multiple de 4/5/6/7 bits. Les registres de 36 bits et de 48 bits étaient la norme sur les gros ordinateurs de type ''mainframe'', qu'ils soient commerciaux ou destinés au calcul scientifique. Certaines machines utilisaient des registres de 3, 7, 13, 17, 23, 36 et 48 bits ; mais elles sont aujourd'hui tombées en désuétude.
===La taille d'un registre n'est pas toujours égale à celle du bus de données===
La taille d'un registre est souvent comparée à la largeur du bus de données (c'est à dire du nombre de bits qui peuvent transiter en même temps sur le bus de données). Intuitivement, on s'attend à ce que le bus mémoire ait la même taille que les registres, ce qui permet de lire un registre en une fois, en un seul accès mémoire. Mais il existe des processeurs où le bus mémoire est plus petit ou plus grand que les registres.
Le cas le plus fréquent est celui où le bus est plus petit que les registres. Les données doivent alors être lues ou écrites en plusieurs passes. Un exemple est celui du processeur MOS Technology 65C816, utilisé dans la console de jeu SNES. C'était un processeur 16 bits, avec des registres entiers de 16 bits. Cependant, le bus de données faisait lui 8 bits. Les adresses faisaient elles 24 bits, mais cela impacte le bus d'adresse, pas le bus de données. L'inconvénient est que les instructions LOAD et STORE demandaient deux accès mémoire : un pour les 8 bits de poids faible, un autre pour les 8 bits de poids fort.
Un autre exemple est celui des vieux coprocesseurs x87 d'Intel et d'autres entreprises. Ils utilisaient des nombres flottants codés sur 32, 64 et 80 bits. Les toutes premiers étaient connectés à un bus 16 bits, ce qui fait qu'une instruction LOAD/STORE demandait 2, 4 ou 5 accès consécutifs. Les suivants étaient connectés à un bus de 32 bits, ce qui demandait 1, 2 ou 2.5 accès consécutifs (3 en pratique, pas 2,5).
Un autre exemple est celui des anciens processeurs x86 32 bits, sur lequels un registre entier fait 32 bits alors que le bus de données fait 64 bits. La raison à cela est la présence d'un cache entre la mémoire et le CPU. Le bus de données est utilisée pour échanger des données entre RAM et cache, pas directement entre registres et RAM. Pas étonnant donc que les deux n'aient pas la même taille. Cependant, le bus entre cache et registres fait lui bel et bien 32 bits, en théorie.
===Le système d'''aliasing'' de registres sur les processeurs x86===
Les processeurs 8086 et 8088 étaient des processeurs 16 bits, qu'on peut considérer comme des versions améliorées et grandement remaniées du 8008 8 bit. En théorie, la rétrocompatibilité n'était pas garantie, car les jeux d'instruction étaient différents entre le 8086 et le 8008. Mais Intel avait prévu quelques améliorations pour rendre la transition plus facile. Et l'une d'entre elle concerne directement le passage des registres de 8 à 16 bits.
Les CPU Intel 16 bits avaient 4 registres de données, nommés AX, BX, CX et DX. Il faisaient 16 bits, soit deux octets. Et chaque octet était adressable comme des registres à part entière. On pouvait adresser un registre de 16, ou alors adresser seulement l'octet de poids fort ou l'octet de poids faible. Le registre AX fait 16 bits, l'octet de poids fort est un registre à part entière nommé AH, l'octet de poids faible est lui le registre nommé AL (H pour ''High'' et L pour ''Low''). Idem avec les registres BX, BH et BL, les registres CX, CH et CL, ou encore les registres DX, DH, DL. Les autres registres ne sont pas concernés par ce découpage.
Tout cela décrit un système d''''''alias'' de registres''', qui permet d'adresser certaines portions d'un registre comme un registre à part entière. Les registres AH, AL, BH, BL, ..., ont tous un nom de registre et peuvent être utilisés dans des opérations arithmétiques, logiques ou autres. Une même opération peut donc agir sur 16 ou 8 bits suivant le registre sélectionné.
[[File:Register 8086.PNG|centre|vignette|upright=2|Registres du 8086, processeur x86 16 bits. Certains registres sont liés à la segmentation ou à d'autres fonctions que nous n'avons pas encore expliqué à ce point du cours, aussi je vais vous demander de les ignorer.]]
Par la suite, le jeu d'instruction x86 a étendu ses registres à 32 et enfin 64 bits. Et les CPU 32 bits ont utilisé le même système d'''alias'' que les CPU 16 bits, mais légèrement modifié. Sur un registre 32 bits, les 16 bits de poids faible sont adressables séparément, mais pas les 16 bits de poids fort. Les registres 8 et 16 bits ont le même nom de registre que sur les CPU 16 bits, le registre étendu a un nouveau nom de registre.
Pour rendre tout cela plus clair, voyons l'exemple du registre EAX des processeurs 64 bits. C'est un registre 32 bits, les 16 bits de poids faible sont tout simplement le registre AX vu plus haut, qui lui-même est subdivisé en AH et AL. La même chose a lieu pour les registres EBX, ECX et EDX. Et cette fois-ci, presque tous les registres ont étés étendus ainsi, même le ''program counter'', les registres liés à la pile et quelques autres, notamment pour adresser plus de mémoire.
[[File:Rejestry IA32.svg|centre|vignette|upright=2|Registres des processeurs x86 32 bits. Certains registres sont liés à la segmentation ou à d'autres fonctions que nous n'avons pas encore expliqué à ce poitn du cours, aussi je vais vous demander de les ignorer.]]
Lors du passage au 64 bits, les registres 32 bits ont étés étendus de la même manière, et les registres étendus à 64 bits ont reçu un nom de registre supplémentaire, RAX, RBX, RCX ou RDX. Le passage à 64 bits s'est accompagné de l'ajout de 4 nouveaux registres.
Un point intéressant est qu'Intel a beaucoup utilisé ce système d'''alias'' pour éviter d'avoir à réellement ajouter certains registres. Pour le moment, bornons-nous à citer les exemples les plus frappants et parlons du MMX, du SSE et de l'AVX.
Le MMX est une extension du x86, qui ajoute des instructions au jeu d'instruction x86 de base. Elle ajoutait 8 registres entiers appelés MM0, MM1, MM2, MM3, MM4, MM5, MM6 et MM7, d'une taille de 64 bits. En théorie, ces registres devraient être des registres séparés des autres, mais Intel utilisa le système d'''alias'' pour éviter d'avoir à rajouter des registres. Il réutilisa les 8 registres flottants de 80 bits déjà existants. Chaque registre MMX correspondait aux 64 bits de poids faible d'un des 8 registres flottants de la x87 ! Cela posa pas mal de problèmes pour les programmeurs qui voulaient utiliser l'extension MMX. Il était impossible d'utiliser à la fois le MMX et les flottants x87...
[[File:MMX-FPU-Register.JPG|centre|vignette|upright=2|Registres MMX et FPU x87.]]
[[File:AVX registers.svg|vignette|Registres AVX.]]
Par la suite, l'extension SSE ajouta plusieurs registres de 128 bits, les ''XMM registers'' illustrés ci-contre. Le SSE fût décliné en plusieurs versions, appelées SSE1, SSE2, SSE3, SS4 et ainsi de suite, chacune rajoutant de nouvelles instructions. Les registres SSE sont bien séparés des autres, Intel n'utilisa pas le système d'alias.
Puis, l'arrivée de l'extension '''AVX''' changea la donne. L'AVX complète le SSE et ses extensions, en rajoutant quelques instructions et surtout en permettant de traiter des données de 256 bits. Et cette dernière ajoute 16 registres d'une taille de 256 bits, nommés de YMM0 à YMM15 et dédiés aux instructions AVX. Et c'est là que le système d''alias'' a encore frappé. Les registres AVX sont partagés avec les registres SSE : les 128 bits de poids faible des registres YMM ne sont autres que les registres XMM.
Puis, arriva l''''AVX-512''' qui ajouta 32 registres de 512 bits, et des instructions capables de les manipuler, d'où son nom. Là encore, les 256 bits de poids faible de ces registres correspondent aux registres de l'AVX précédent. Du moins, pour les premiers 16 registres, vu qu'il n'y a que 16 registres de l'AVX normal.
Pour résumer, ce système permet d'ajouter des registres de plus grande taille, en étendant des registres existants pour en augmenter la taille. La longévité des architectures x86 a fait que cette technique a beaucoup été utilisée. Mais les autres architectures n'implémentent pas vraiment ce système. De plus, ce système marche assez mal avec les processeurs modernes, dont la conception interne se marie mal avec l'''aliasing'' de registres, pour des raisons que nous verrons plus tard dans ce cours (cela rend plus difficile le renommage de registres et la détection des dépendances entre instructions).
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Langage machine et assembleur
| next=Le modèle mémoire : alignement et boutisme
}}
</noinclude>
2sldvzscbztzeb1vqhhpcg3lf0znsjm
Les cartes graphiques/Les processeurs de shaders
0
69558
763648
763526
2026-04-13T22:53:51Z
Mewtow
31375
/* Les registres des processeurs de shaders */
763648
wikitext
text/x-wiki
Les '''''shaders''''' sont des programmes informatiques exécutés par la carte graphique, et plus précisément par des processeurs de ''shaders''. Un point très important à comprendre est que chaque triangle ou pixel d'une scène 3D peut être traité indépendamment des autres. Le tout se résume comme suit :
: '''L’exécution d'un shader génère un grand nombre d'instances de ce shader, chacune traitant un paquet de pixels/sommets différent.'''
En conséquence, il est possible de traiter chaque instance d'un ''shader'' en parallèle des autres, en même temps, au lieu de traiter les instances l'une après l'autre.
La conséquence est que les cartes graphiques sont des architectures massivement parallèles, à savoir qu'elles sont capables d'effectuer un grand nombre de calculs indépendants en même temps. De plus, le parallélisme utilisé est du parallélisme de données, à savoir qu'on exécute le même programme sur des données différentes, chaque donnée étant traitée en parallèle des autres. Les cartes graphiques récentes incorporent toutes les techniques de parallélisme de donnée au niveau matériel, et nous allons toutes les détailler dans ce chapitre. S'il fallait résumer, elles ont plusieurs processeurs/cœurs, chaque cœur est capable d’exécuter des instructions SIMD (ils ne font que cela, à vrai dire), les cœurs sont fortement multithreadés, et j'en passe.
[[File:CPU and GPU.png|vignette|Comparaison du nombre de processeurs et de cœurs entre CPU et GPU.]]
Le premier point est qu'une carte graphique contient de nombreux processeurs, qui eux-mêmes contiennent plusieurs unités de calcul. Savoir combien de cœurs contient une carte graphique est cependant très compliqué, car la terminologie utilisée par les fabricants de carte graphique est particulièrement confuse. Il n'est pas rare que ceux-ci appellent cœurs ou processeurs, ce qui correspond en réalité à une unité de calcul d'un processeur normal, sans doute histoire de gonfler les chiffres. Et on peut généraliser à la majorité de la terminologie utilisée par les fabricants, que ce soit pour les termes ''warps processor'', ou autre, qui ne sont pas aisés à interpréter.
L'architecture d'une carte graphique récente est illustrée ci-dessous. Rien de bien déroutant pour qui a déjà étudié les architectures à parallélisme de données, mais quelques rappels ou explications ne peuvent pas faire de mal. Le premier point est la présence d'un grand nombre de processeurs/cœurs, les rectangles en bleu/rouge. Chacun d'entre eux contient un grand nombre de circuits de calculs, avec des circuits de calcul simples mais nombreux en rouge, et une unité pour les calculs complexes (trigonométriques, racines carrées, autres) en rouge. Le tout est relié à une hiérarchie mémoire indiquée en vert, comprenant des mémoires locales en complément de la mémoire vidéo principale. Le tout est alimenté par une unité de répartition, le '''''Thread Execution Control Unit''''' en jaune, qui répartit les différentes instances du ''shader'' sur les différents processeurs. Elle est aussi appelée le '''processeur de commandes''', comme nous le verrons dans quelques chapitres. Nous utiliserons le terme processeur de commande dans ce qui suit.
[[File:NVIDIA GPU Accelerator Block Diagram.png|centre|vignette|upright=2.5|Ce schéma illustre l'architecture d'un GPU en utilisant la terminologie NVIDIA. Comme on le voit, la carte graphique contient plusieurs cœurs de processeur distincts. Chacun d'entre eux contient plusieurs unités de calcul généralistes, appelées processeurs de threads, qui s'occupent de calculs simples (en bleu). D'autres calculs plus complexes sont pris en charge par une unité de calcul spécialisée (en rouge). Ces cœurs sont alimentés en instructions par le processeur de commandes, ici appelé ''Thread Execution Control Unit'', qui répartit les différents shaders sur chaque cœur. Enfin, on voit que chaque cœur a accès à une mémoire locale dédiée, en plus d'une mémoire vidéo partagée entre tous les cœurs.]]
Les portions bleu, jaune et verte du schéma précédent méritent chacune un chapitre séparé. La hiérarchie mémoire en vert fera l'objet d'un chapitre ultérieur. Quant au répartiteur en jaune, il sera détaillé en profondeur dans le prochain chapitre. Dans ce chapitre, nous allons voir comment fonctionnent les processeurs de ''shaders'', la partie bleue. Nous allons voir que ceux-ci ne sont pas très différents des processeurs que l'on trouve dans les ordinateurs normaux, du moins dans les grandes lignes. Ce sont des processeurs séquentiels, qui exécutent des instructions les unes après les autres. Ils ont des instructions machines, des modes d'adressage, un assembleur, des registres et tout ce qui fait qu'un processeur est un processeur. Néanmoins, il y a une différence de taille : ce sont des processeurs adaptés pour effectuer un grand nombre de calculs en parallèle.
==Les registres des processeurs de shaders==
Un processeur de shaders contient beaucoup de '''registres généraux''', qui servent un peu à tout. Le programmeur de shader peut les utiliser à loisir. Tout processeur digne de ce nom possède des registres généraux, mais un processeur de ''shader'' dispose aussi de registres spécialisés, qu'on ne trouve que sur les processeurs de ''shaders''. Ils servent à l'interfacer avec le reste du pipeline graphique. Par exemple, des registres pour les textures, d'autres pour recevoir des données du rastériseur, etc.
[[File:Architecture carte graphique vertex avec texture.PNG|centre|vignette|upright=2|Architecture carte graphique vertex avec texture]]
===Les registres d'interface avec le pipeline graphique===
Les processeurs de ''vertex shader'' reçoivent des sommets provenant de l'''input assembler'' et envoient leur résultat au rastériseur. Les processeurs de ''pixel shader'' reçoivent des données de l'unité de rastérisation, et envoient un pixel éclairé aux ROPs. Et pour cela, le processeur de shader a des registres dédiés, qui servent d'interface avec le reste du pipeline graphique.
Les '''registres de sortie''' sont là où le processeur stocke les résultats à envoyer, soit au rastériseur pour un ''vertex shader'', soit aux ROP pour un ''pixel shader''. Les registres de sorties sont en écriture seule. Pour donner un exemple, les ''vertex shaders'' ont au minimum un registre pour la position du sommet dans l'espace (trois coordonnées), un autre pour la couleur/luminosité du sommet, un autre pour la couleur du brouillard, un autre pour les coordonnées de texture.
{|class="wikitable"
|+ Registres de sortie des ''pixel/vertex shaders''
|-
! Vertex shader
! Pixel shader
|-
| Couleur du pixel
| Couleur du sommet
|-
| Profondeur du pixel
| Position du sommet
|-
| rowspan="2" |
| Coordonnées de texture du sommet
|-
| Couleur de brouillard.
|}
Les '''registres d'entrée''', aussi appelés '''registres d'attributs''', réceptionnent soit les sommets provenant de l'''input assembler'', soit les pixels provenant de l'unité de rastérisation. Les registres d'entrée sont en lecture seule, du point de vue du processeur de shader, ils sont initialisés avant l'exécution de l'instance du ''shader''.
Les '''registres de constantes''' mémorisent des constantes utiles pour le ''shader''. Par exemple, pour les ''vertex shaders'', ils stockent les matrices servant aux différentes étapes de transformation ou d'éclairage. La différence avec les registres d'attribut est qu'ils mémorisent des données constantes pour un objet/modèle 3D (pour un ''draw call'', pour être plus précis) : des matrices de transformation, les adresses de texture, et bien d'autres. A l'opposé, les registres d'attributs mémorisent des sommets/pixels qui varient d'une instance de shader à l'autre.
Les ''pixel/vertex shaders'' 1.0 ne géraient que des constantes flottantes pour les ''vertex shaders'', entières pour les ''pixel shaders''. Les ''pixel/vertex shaders'' 2.0 et 3.0 ont ajouté des registres de constantes pour les nombres entiers et des opérandes booléennes. Il y en avait 16, comparé aux centaines de registres de constantes flottants. Les constantes entières et booléennes étaient utilisées pour gérer les boucles, guère plus. Avec les ''pixel/vertex shaders'' 4.0 et plus, les registres de constante n'ont plus de type prédéterminé, le programmeur gère ces registres comme il l'entend.
L'adressage des registres de constante est quelque peu particulier. Il faut dire qu'il y en a plusieurs milliers sur les processeurs de ''shaders'' modernes, au point qu'il serait plus juste de parler de mémoire RAM des constantes. Les registres de constante sont en effet un ''local store'' un peu spécial, intégré directement dans le processeur. Et le processeur accède à ce ''local store'' en utilisant une mode d'adressage semblable à celui utilisé pour la mémoire, avec un mode d'adressage indirect. L'adresse à lire dans ce ''local store'' est dans un registre, séparé du reste, appelé le '''registre d'adresse de constante'''.
===Les registres spécialisés internes===
D'autres registres spécialisés ne font pas l'interface avec le reste du GPU. Ils servent à stocker des constantes ou des données importantes, qui n'ont pas vraiment leur place dans les registres généraux.
Depuis les ''pixel/vertex shaders'' 3.0, les ''shaders'' sont capables d'effectuer des boucles et d'autres structures de contrôle familières pour les programmeurs. Et deux registres ont été intégrés afin d'améliorer les performances des structures de contrôle. Le premier est un registre à prédicat, qui sera vu dans la section sur le SIMD avec prédication. Le second est un '''registre compteur de boucle''', qui mémorise l'indice d'une boucle. Il est initialisé à 0, et est incrémenté à chaque fois qu'une boucle s'exécute.
Certains processeurs de shader ont aussi des '''registres de texture''' , qui servent d'interface avec la mémoire pour la gestion des textures. Ils mémorisent les texels lus par l'unité de texture. L'unité de texture lit un texel, plusieurs avec ''multitexturing'', et les place dans ces registres de texture. Les registres de texture sont parfois initialisés avant l'exécution du ''shader'', mais la plupart sont initialisé quand le ''shader'' termine une instruction de lecture de texture. Ils sont généralement en lecture seule, mais il y a des exceptions.
==Les processeurs de shaders modernes : les processeurs SIMD==
Maintenant, voyons quelles sont les instructions supportées par les processeurs de shaders modernes. Et si je dis moderne, c'est car nous ne parlerons que des GPU de l'époque DirectX 10 et après, pas des GPU de l'époque DirectX 9 et antérieur. La raison est que le jeu d'instruction des shaders a franchement évolué, avec le passage d'architectures VLIW à des architectures SIMD. Et cela a eu des conséquences assez profondes sur le jeu d'instruction et leur microarchitecture. Nous n'allons parler des GPU de type SIMD dans ce chapitre. Un chapitre dédié sera consacré aux GPU de type VLIW.
Le jeu d'instruction des GPU NVIDIA n'est pas encore connu à l'heure où j'écris ces lignes, la documentation du constructeur n'est pas disponible. Quelques chercheurs ont tenté de faire de la rétro-ingénierie du code de divers shaders pour retrouver le jeu d'instruction des divers GPU NVIDIA, ce qui fait qu'on a cependant une idée de ce dernier. Mais rien d'officiel. Par contre, AMD fournit librement cette documentation sur le net. Ce qui fait qu'on peut trouver des documents de ce genre :
* [https://developer.amd.com/wordpress/media/2012/12/AMD_Southern_Islands_Instruction_Set_Architecture.pdf Graphics Core Next 1 instruction set] ;
* [https://developer.amd.com/wordpress/media/2013/07/AMD_Sea_Islands_Instruction_Set_Architecture.pdf Graphics Core Next 2 instruction set] ;
* [https://developer.amd.com/wordpress/media/2013/12/AMD_GCN3_Instruction_Set_Architecture_rev1.1.pdf Graphics Core Next 3 and 4 instruction sets] ;
* [https://developer.amd.com/wp-content/resources/Vega_Shader_ISA_28July2017.pdf Graphics Core Next 5 instruction set] ;
* [https://developer.amd.com/wp-content/resources/Vega_7nm_Shader_ISA.pdf "Vega" 7nm instruction set architecture] (also referred to as Graphics Core Next 5.1) ;
* [https://www.amd.com/content/dam/amd/en/documents/radeon-tech-docs/instruction-set-architectures/rdna3-shader-instruction-set-architecture-feb-2023_0.pdf Jeu d'instruction des GPU de type RDNA3 d'AMD].
===Les instructions SIMD===
Les '''instructions SIMD''' manipulent plusieurs nombres en même temps. Elles manipulent plus précisément des '''vecteurs''', des ensembles de plusieurs nombres entiers ou nombres flottants placés les uns à côté des autres, le tout ayant une taille fixe, qui sont stockés dans des registres spécialisés. En général, tous les vecteurs ont une taille fixe, peu importe leur contenu. Cela implique que suivant la taille des données à manipuler, on pourra en placer plus ou moins dans un vecteur. Par exemple, un vecteur de 128 bits pourra contenir 4 entiers de 32 bits, 4 flottants 32 bits, ou 8 entiers de 16 bits.
[[File:Vector register.png|centre|vignette|upright=2|Contenu d'un vecteur en fonction du type de données utilisé.]]
Les vecteurs sont stockés dans des '''registres vectoriels''', aussi appelés '''registres SIMD'''. Un registre vectoriel peut contenir un vecteur complet, pas plus. En conséquence, ils ont une taille assez importante : ils font généralement 128, 256, voire 512 bits, comparé aux 32/64 bits des registres des CPU. Les cartes graphiques modernes contiennent un très grand nombre de registres SIMD.
{|
|+ Comparaison entre un processeur sans registres vectoriels, et avec registres vectoriels.
|[[File:Non-SIMD cpu diagram1.svg|vignette|upright=1.5|CPU Non-SIMD]]
|[[File:SIMD cpu diagram1.svg|vignette|upright=1.5|CPU SIMD]]
|}
Une instruction SIMD traite chaque donnée du vecteur indépendamment des autres. Par exemple, une instruction d'addition vectorielle va additionner ensemble les données qui sont à la même place dans deux vecteurs, et placer le résultat dans un autre vecteur, à la même place.
[[File:Instructions SIMD.png|centre|vignette|upright=2.0|Instructions SIMD]]
Sur les cartes graphiques modernes, les vecteurs sont généralement des vecteurs qui regroupent plusieurs nombres flottants. De plus, les flottants en question sont des flottants dits simple précision, codés sur 32 bits. Mais il y a quelques exceptions, comme [https://www.realworldtech.com/apple-custom-gpu/ certains GPU d'Apple, qui ne gèrent majoritairement que des flottants codés sur 16 bits], avec des fonctionnalités pour la simple précision. Les anciennes cartes graphiques ne géraient pas du tout de vecteurs contenant des nombres entiers.
===Les instruction scalaires entières, typiques des CPU===
Un processeur SIMD gère donc des instructions SIMD, et les anciennes cartes graphiques ne disposaient que d'instructions de ce type. Mais depuis au moins une décennie, les processeurs de shaders gèrent des instructions normales, non-SIMD. De telles instructions sont appelées des '''instruction scalaires'''. En clair, il s'agit des instructions qu'on retrouve normalement tous les processeurs principaux (les CPU).
Il s'agit généralement d''''instructions entières''', agissent sur des registres entiers non-SIMD. Elles ne traitent pas de vecteur, mais de simples nombres entiers indépendants, sans regroupement d'aucune sorte. Typiquement, il s'agit d'opérations d'addition, de soustraction, des opérations logiques, des comparaisons, guère plus. On trouve aussi des opérations un peu originales, comme des calculs de valeur absolue, du minimum/maximum de deux opérandes, des opérations à prédicat comme une instruction CMOV, etc. Les cartes graphiques supportent rarement la multiplication, mais les plus récentes supportent des multiplications sur des opérandes de 16/32 bits. Par contre, aucune ne gère de division entière.
Les GPU modernes gèrent aussi des instructions de test et de branchement, là encore sur des nombres entiers. Les instructions de test et branchement sont généralement considérées comme à part des instructions de calcul, mais ce sont des opérations scalaires. Les comparaisons se font entre deux entiers scalaires, pas entre deux vecteurs. Retenez bien ce détail, car il sera très important pour la suite.
Les GPU modernes gèrent aussi des '''instructions flottantes scalaires''', à savoir que des instructions qui ont pour opérandes des nombres flottants isolés, qui ne sont pas dans un vecteur. Les processeurs principaux (CPU) d'un ordinateur sont capables de faire beaucoup de calculs arithmétiques simples sur des nombres flottants, comme des additions, des multiplications, des opérations bit-à-bit, éventuellement des divisions, etc. Il en est de même sur les GPUS. Mais ces derniers gèrent aussi de nombreuses instructions flottantes que les CPU n'incorporent presque pas.
Il est rare que les CPU soient capables de faire des opérations flottantes complexes, comme des calculs trigonométriques, des exponentielles, des logarithmes, des racines carrées ou racines carrées inverse, etc. De tels calculs sont rares dans les programmes exécutables, alors que les calculs arithmétiques simples y sont légion. Mais le rendu 3D demande pas mal de calculs trigonométriques, de produits scalaires ou d'autres opérations. Par exemple, dans les chapitres précédents, nous avions abordé les calculs d'éclairage et avions vu qu'ils font beaucoup de calculs vectoriels avec des vecteurs comme la normale d'un sommet. Et ces calculs demandent de calculer des produits scalaires et vectoriels, qui eux-mêmes demandent des calculs trigonométriques comme le cosinus ou le sinus.
Aussi, les processeurs de ''shaders'' disposent souvent d'instructions flottantes spécialisées dans les calculs complexes : exponentielle/logarithme, racine carrée, racine carrée inverse, autres. Nous appellerons ces instructions des '''instructions transcendantales''', car elles effectuent des calculs de ce type.
Il faut noter que le processeur incorpore des registres dédiés aux scalaires, séparés des registres SIMD. Par séparés, on veut dire que ce sont des registres différents, adressés différemment, mais qu'ils sont aussi physiquement séparés dans le processeur, ils sont des bancs de registres différents.
===Les instructions en ''co-issue''===
Beaucoup de cartes graphiques récentes comme anciennes incorporent des '''instructions de ''co-issue''''' qui ne se trouvent que sur les cartes graphiques et n'ont aucun équivalent sur les CPUs. Les instructions de ''co-issue'' regroupent plusieurs opérations par instruction. Par exemple, elles peuvent combiner une opération vectorielle avec une opération scalaire. Ou encore, elles peuvent regrouper une opération scalaire, une opération vectorielle et un branchement. Il s'agit d'instructions qui ressemblent grandement à ce qu'on trouve sur les processeurs VLIW.
Un point important est que les cartes graphiques modernes disposent d'instructions à ''co-issue'' en plus des instructions normales. Les instructions à ''co-issue'' sont complémentaire des instructions normales, elles ne les remplacent pas. Les deux peuvent s'utiliser en même temps, dans un même shader. Il a cependant existé des cartes graphiques assez anciennes sur lesquelles toutes les instructions étaient des instructions à ''co-issue'' : certains processeurs de shaders VLIW anciens sont de ce type.
Il y a de nombreuses contraintes quant au regroupement des deux opérations. On ne peut pas regrouper n'importe quelle opération avec n'importe quelle autre. L'exemple type de ''co-issue'' est la ''co-issue'' entre opérations scalaires et vectorielles : il n'est pas possible de regrouper deux instructions scalaires ou deux instructions vectorielles. La seule possibilité est de regrouper une opération scalaire et une opération vectorielle. La raison à cela est qu'opérations scalaires et vectorielles sont calculées dans des circuits séparés : le processeur incorpore une unité de calcul scalaire et une unité de calcul SIMD, et peut utiliser les deux en parallèle, en même temps. Mais nous verrons cela dans quelques chapitres.
Pour simplifier, cette technique permettait d’exécuter deux opérations arithmétiques en même temps, en parallèle : une opération vectorielle appliquée aux couleurs R, G, et B, et une opération scalaire appliquée à la couleur de transparence. Si cela semble intéressant sur le papier, cela complexifie fortement le processeur de shader, ainsi que la traduction à la volée des shaders en instructions machine.
===Un exemple : le jeu d’instruction du GPU de la Geforce 3===
La première carte graphique commerciale grand public à disposer d'une unité de vertex programmable est la Geforce 3. Celui-ci respectait le format de vertex shader 1.1. L'ensemble des informations à savoir sur cette unité est disponible dans l'article [https://cseweb.ucsd.edu/~ravir/6160-fall04/papers/p149-lindholm.pdf "A user programmable vertex engine"], disponible sur le net. . Le processeur de cette carte était capable de gérer un seul type de données : les nombres flottants de norme IEEE754. Toutes les informations concernant la coordonnée d'une vertice, voire ses différentes couleurs, doivent être encodées en utilisant ces flottants.
Les processeurs de vertices de la Geforce 3 disposent de registres registres SIMD qui font 128 bits, soit 4 flottants de 32 bits. Elle contient 16 registres d'entrée, 16 registres de sortie, 32 registres généraux. La mémoire des constantes contient 512 "registres".
Le processeur de la Geforce 3 est capable d’exécuter 17 instructions différentes, dont voici les principales :
{|class="wikitable"
|-
!OpCode!!Nom!!Description
|-
! colspan="3" | Opérations mémoire
|-
|MOV||Move||vector -> vector
|-
|ARL||Address register load||miscellaneous
|-
! colspan="3" | Opérations arithmétiques
|-
|ADD||Add||vector -> vector
|-
|MUL||Multiply||vector -> vector
|-
|MAD||Multiply and add||vector -> vector
|-
|MIN||Minimum||vector -> vector
|-
|MAX||Maximum||vector -> vector
|-
|SLT||Set on less than||vector -> vector
|-
|SGE||Set on greater or equal||vector -> vector
|-
|LOG||Log base 2||miscellaneous
|-
|EXP||Exp base 2||miscellaneous
|-
|RCP||Reciprocal||scalar-> replicated scalar
|-
|RSQ||Reciprocal square root||scalar-> replicated scalar
|-
! colspan="3" | Opérations trigonométriques
|-
|DP3||3 term dot product||vector-> replicated scalar
|-
|DP4||4 term dot product||vector-> replicated scalar
|-
|DST||Distance||vector -> vector
|-
! colspan="3" | Opérations d'éclairage géométrique
|-
|LIT||Phong lighting||Calcule l'éclairage de Gouraud
|}
L'instruction la plus intéressante est clairement la dernière : elle éclaire un sommet, en utilisant un éclairage de Phong. Les autres instructions permettent d'implémenter un autre algorithme si besoin, mais cette forme d'éclairage est déjà là à la base.
Les autres instructions sont surtout des instructions arithmétiques : multiplications, additions, exponentielles, logarithmes, racines carrées, etc. Pour les instructions d'accès à la mémoire, on trouve une instruction MOV qui déplace le contenu d'un registre dans un autre et une instruction de calcul d'adresse, mais aucune instruction d'accès à la mémoire sur le processeur de la Geforce 3. Plus tard, les unités de ''vertex shader'' ont acquis la possibilité de lire des données dans une texture.
On remarque que la division est absente. Il faut dire que la contrainte qui veut que toutes ces instructions s’exécutent en un cycle d'horloge pose quelques problèmes avec la division, qui est une opération plutôt lourde en hardware. À la place, on trouve l'instruction RCP, capable de calculer 1/x, avec x un flottant. Cela permet ainsi de simuler une division : pour obtenir Y/X, il suffit de calculer 1/X avec RCP, et de multiplier le résultat par Y.
==La prédication et le SIMT==
Les cartes graphiques récentes peuvent effectuer des branchements, mais ceux-ci sont tout sauf performants. Dès qu'un branchement survient, le processeur est obligé de traiter chaque élément du vecteur un par un, au lieu de tous les traiter en même temps en parallèle. Les performances s'en ressentent, ce qui fait que les branchements sont à éviter le plus possible. Pour améliorer la gestion des conditions, les cartes graphiques modernes incorporent des instructions spécialisées qui permettent de remplacer des codes remplis de branchements par des codes plus simples, compatibles avec l'organisation des données en vecteurs.
Si on met de côté le support de certaines instructions courantes, comme la valeur absolue, ou le calcul du minimum/maximum, la technique la plus importante est la technique dite de '''prédication'''. L'idée est que quand une instruction effectue un calcul sur un ou deux vecteurs, certains éléments du vecteur sont ignorés. Les éléments à ignorer sont choisis suivant le résultat d'une instruction de comparaison, qui effectue un test : les éléments pour lesquels ce test est respecté sont pris en compte, ceux qui ne passent pas le test sont ignorés.
Pour donner un exemple d'utilisation, imaginons que l'on ait un vecteur dans lequel on veut remplacer toutes les valeurs négatives par des 0. Dans ce cas, on utilise :
* une instruction de comparaison, qui compare chaque élément du vecteur avec 0 et génère plusieurs bits de résultat ;
* suivi d'une instruction à prédicat qui met à zéro les éléments pour lesquels les bits de résultat précédents sont à 1.
Elle est implémentée grâce à un registre appelé le '''''Vector Mask Register'''''. Celui-ci permet de stocker des informations qui permettront de sélectionner certaines données et pas d'autres pour faire notre calcul. Il est mis à jour par des instructions de comparaison. le ''Vector Mask Register'' stocke un bit pour chaque flottant présent dans le vecteur à traiter, bit qui indique s'il faut appliquer l'instruction sur ce flottant. Si ce bit est à 1, notre instruction doit s’exécuter sur la donnée associée à ce bit. Sinon, notre instruction ne doit pas la modifier. On peut ainsi traiter seulement une partie des registres stockant des vecteurs SIMD.
[[File:Vector mask register.png|centre|vignette|upright=2.0|''Vector mask register'']]
===La prédication avec une pile SIMT===
Au niveau du jeu d’instruction, les architectures SIMT implémentent de la prédication, sous une forme améliorée. Les processeurs SIMT actuels sont surtout utilisées sur les processeurs intégrés aux cartes graphiques. Et ces derniers gèrent très mal les branchements, et encore : beaucoup de cartes graphiques, même récentes, ne gèrent tout simplement pas les branchements. Elles doivent donc se débrouiller avec uniquement la prédication, là où les processeurs SIMD utilisent des branchements normaux en complément de la prédication. Insistons sur le fait que cet usage exclusif de la prédication n'est présent que sur une sous-partie des architectures SIMT, le seul exemple que l'auteur de ce wikilivre connait étant celui des cartes graphiques.
Les architectures SIMT sans branchements doivent donc trouver des solutions pour gérer les structures de contrôle imbriquées, à savoir une boucle placée à l'intérieur d'une autre boucle, un IF...ELSE dans un autre IF...ELSE, etc. Elles utilisent pour cela la prédication, combinée avec des mécanismes annexes. Le premier d'entre eux est l'usage de plusieurs registres de masques organisés d'une manière bien précise, l'autre est l'usage de compteurs d'activité. Voyons ces deux techniques.
La '''pile de masques''' remplace le ou les registres de masque. Sans elle, le processeur SIMD incorpore un registre de masque qui est adressé implicitement ou explicitement. Éventuellement, le processeur peut contenir plusieurs registres de masque séparés adressables via un nom de registre. Avec elle, le processeur SIMD incorpore plusieurs registres de masque organisé en pile. Le registre de masque est donc remplacé par une mémoire LIFO, une pile, dans laquelle plusieurs masques sont empilés.
Le tout forme une pile, similaire à la pile d'appel, sauf qu'elle est utilisée pour empiler des masques. Un masque est calculé et empilé à chaque entrée dans une structure de contrôle, puis dépilé une fois la structure de contrôle exécutée. L'empilement et le dépilement des masques est effectué par des instructions PUSH et POP, présentes dans le jeu d'instruction du processeur SIMD.
Le calcul des masques doit répondre à plusieurs impératifs.
* Premièrement, chaque masque se calcule en faisant un ET entre le masque précédent et le masque calculé par l'instruction de test. Cela permet de ne pas réveiller d’élément au beau milieu d'une structure imbriquée. Si in IF désactive certains éléments du vecteur, une condition imbriquée dans ce IF ne doit pas réveiller cet élément. Le fait de faire un ET entre les masques garantit cela.
* Deuxièmement, les masques doivent être empilés et dépilés correctement. Au moment de rentrer dans une structure de contrôle, on effectue une instruction de test associée à la structure de contrôle, qui calcule un masque, et on empile le masque calculé. Au moment de sortir de la structure de contrôle, on dépile le masque en question.
L'implémentation demande d'utiliser une mémoire LIFO pour stocker la pile de masques, et quelques circuits annexes. Il faut notamment un circuit relié à l'ALU qui récupère les conditions, les résultats des comparaisons, et qui effectue le ET pour combiner les masques.
Pour donner un exemple, prenons le code suivant, qui est volontairement simpliste et ne sert qu'à des fins d'explication :
<syntaxhighlight lang="c">
if ( condition 1 )
{
if ( condition 2 )
{
...
}
else
{
...
}
Autres instructions
}
Instructions après le IF...
</syntaxhighlight>
Imaginons que l'on traite des vecteurs de 8 éléments.
Pour le vecteur considéré, la première condition (a > 0) n'est respectée que par les 4 premiers éléments. L'instruction de condition calcule alors le masque correspondant : 1111 0000. Le masque est alors calculé, puis empilé au sommet de la pile.
La seconde instruction de test, qui teste la variable b, est maintenant valide pour les 4 bits du milieu du masque. Mais n'allez pas croire que le masque correspondant soit 0011 11100 : il faut tenir compte de la condition précédente, qui a éliminé les 4 derniers éléments. Pour cela, on fait un ET logique entre le masque précédent, et le masque calculé par la condition. Le masque au sommet de la pile est donc lu, combiné avec le masque calculé par l'instruction, ce qui donne le masque final. Le masque final est alors empilé au sommet de la pile.
On exécute alors l'instruction du IF, en tenant compte du masque qui est au sommet de la pile. Si le IF était plus compliqué, toutes les instructions suivantes tiendraient compte du masque. En fait, le masque est pris en compte tant qu'il n'est pas dépilé. Une fois que le IF est terminé, le masque est dépilé.
On passe alors au ELSE, et rebelotte. Le masque pour le ELSE est calculé en combinant le masque au sommet de la pile avec la condition du ELSE. Le masque au sommet de la pile est celui calculé à l'entrée du premier IF, pas le second qui a été dépilé. Les instructions du ELSE sont alors exécutées en tenant compte de ce masque. Une fois qu'elles sont toutes exécutées, le masque est dépilé.
Puis vient l'exécution des instructions après le ELSE. Elles utilisent le masque empilé au sommet de la pile, qui correspond à celui à l'entrée du IF.
Puis vient le moment d'exécuter les instructions après le IF : pas de masque, on exécute sur tout le vecteur.
===Les compteurs d'activité===
Une variante de la technique précédente remplace la pile de masques par des '''compteurs d'activité'''. La technique est similaire, si ce n'est qu'elle utilise moins de circuits. Avant , on avait une pile de masques de même taille, dont les bits sont à 0 ou 1 suivant que la condition est remplie. La pile de masque ressemble donc à ceci :
{|class="wikitable"
|-
! masque 1
| 1 || 1 || 1 || 1
|-
! masque 2
| 0 || 1 || 1 || 1
|-
! masque 3
| 0 || 1 || 1 || 1
|-
! masque 4
| 0 || 0 || 0 || 1
|-
! masque 1
| colspan="4" | vide
|}
Une manière équivalente de représenter cette pile de masque est de compter combien de bits sont à 0 dans chaque colonne. Attention : j'ai bien dit à 0 ! On obtient alors :
{|class="wikitable"
|-
! masque 1
| 3 || 1 || 1 || 0
|}
Et c'est le principe caché derrière la technique des compteurs d'activité. Chaque élément dans un vecteur, chaque place, se voit attribuer un compteur. Un compteur non-nul indique qu'il ne faut pas prendre en compte l’élément. Ce n'est qu'une fois que le compteur est nul que l'on effectue des opérations sur l’élément associé du vecteur.
À chaque fois qu'on entre dans une structure de contrôle, on teste une condition sur chaque élément. Si la condition est respectée pour un élément, alors le compteur ne change pas. Mais si la condition n'est pas respectée, alors on incrémente le compteur associé. En sortant de la structure de contrôle, on décrémente le compteur associé. Notons que les compteurs qui n'ont pas été incrémentés en entrant dans la structure de contrôle ne sont pas décrémentés en sortant. En clair, là où on empilait/dépilait un masque, on se contente d'incrémenter/décrémenter un compteur.
Utiliser un compteur en lieu et place d'une colonne entière dans la pile de masque utilise moins de bits. Et c'est sans doute pour cette raison que certaines cartes graphiques, comme les cartes graphiques intégrées d'Intel depuis 2004, utilisent cette technique.
{{NavChapitre | book=Les cartes graphiques
| prev=L'évolution vers la programmabilité : les GPUs
| prevText=L'évolution vers la programmabilité : les GPUs
| next=La microarchitecture des processeurs de shaders
| nextText=La microarchitecture des processeurs de shaders
}}
{{autocat}}
3arhkq2glk54klzdtla80zcehbwlm4f
Mathc initiation/Fichiers h : c24
0
76192
763659
725879
2026-04-14T08:07:40Z
Xhungab
23827
763659
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005v| Sommaire]]
:
{{Partie{{{type|}}}|La méthode de Newton (en x et en y)}}
:
En analyse numérique, la méthode de Newton ou méthode de Newton-Raphson1 est, dans son application la plus simple, un algorithme efficace pour trouver numériquement une approximation précise d'un zéro (ou racine) d'une fonction réelle d'une variable réelle. [https://fr.wikipedia.org/wiki/M%C3%A9thode_de_Newton wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c24a1|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c24a5|x_nwtn.h ........... La méthode de Newton]]
* [[Mathc initiation/Fichiers h : c47a4|kg_2d.h ............ Dessiner f et g]]
* [[Mathc initiation/Fichiers h : c47a5|kg_2dp.h .......... Dessiner f, g et le point d'intersection]]
:
.
:
Les fonctions pour les différents exemples :
* [[Mathc initiation/Fichiers h : c24ba|f.h ]]
:
.
:
Exemples numériques :
* [[Mathc initiation/Fichiers c : c24ca|c16a1.c ]]
* [[Mathc initiation/Fichiers c : c24cb|c16b1.c ]]
* [[Mathc initiation/Fichiers c : c24cc|c16c1.c ]]
:
.
:
Dessiner les fonctions f et g :
* [[Mathc initiation/Fichiers h : c47a1|c16a2.c ]]
* [[Mathc initiation/Fichiers h : c47a2|c16b2.c ]]
* [[Mathc initiation/Fichiers h : c47a3|c16c2.c ]]
:
.
:
Dessiner les fonctions f, g et le point d'intersection :
* [[Mathc initiation/Fichiers h : c47fa|c16a3.c ]]
* [[Mathc initiation/Fichiers h : c47fb|c16b3.c ]]
* [[Mathc initiation/Fichiers h : c47fc|c16c3.c ]]
:
.
:
{{AutoCat}}
kddxm93ufm8b3req0vwawph7ir1kii5
Mathc initiation/Fichiers h : c24a4
0
76196
763667
725888
2026-04-14T08:22:26Z
Xhungab
23827
763667
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005w| Sommaire]]
:
{{Partie{{{type|}}}|Dessiner le vecteur orthogonal au point P}}
:
Le gradient d'une fonction de plusieurs variables en un certain point est un vecteur qui caractérise la variabilité de cette fonction au voisinage de ce point. Défini en tout point où la fonction est différentiable, il définit un champ de vecteurs, également dénommé gradient. Le gradient est la généralisation à plusieurs variables de la dérivée d'une fonction d'une seule variable. [https://fr.wikipedia.org/wiki/Gradient wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c27a1|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers c : c47cb|x_strcg.h ........... Déclaration des structures pour gnuplot]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c26a4|x_fxyz.h ............ Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c27a7|x_grad.h ........... Calculer le gradient au point p]]
* [[Mathc initiation/Fichiers h : x_70a5|kg_3dv.h ........... Dessiner le vecteur]]
:
.
:
Les fonctions pour les différents exemples :
* [[Mathc initiation/Fichiers h : x_69a5|f.h]]
:
.
:
Dessiner le vecteur orthogonal au point P :
* [[Mathc initiation/c36a2|c00a.c ... "2*exp(-x) * cos(y)"]]
* [[Mathc initiation/Fichiers h : c29a4|c00b.c ... "cos(x)+cos(y)"]]
* [[Mathc initiation/Fichiers h : c27a5|c00c.c ... "cos(x*y)"]]
* [[Mathc initiation/Fichiers h : c27a6|c00d.c ... "x**2 - 4*x*y"]]
* [[Mathc initiation/a59|c00e.c ... "sqrt(x**2 + y**2)"]]
:
.
:
{{AutoCat}}
l0ejfqun5jthhpmonvr3108gozql63v
Mathc initiation/Fichiers h : c25
0
76206
763658
725878
2026-04-14T08:07:09Z
Xhungab
23827
763658
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005v| Sommaire]]
:
{{Partie{{{type|}}}|Calcul des dérivées partielles}}
:
En mathématiques, la dérivée partielle d'une fonction de plusieurs variables est sa dérivée par rapport à l'une de ses variables, les autres étant gardées constantes. [https://en.wikipedia.org/wiki/Partial_derivative wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c25a1|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
:
.
:
Les fonctions et leurs dérivés pour les différents exemples :
* [[Mathc initiation/Fichiers h : c25ba|f.h ]]
:
.
:
Calculer les dérivées partielles du premier ordre :
* [[Mathc initiation/Fichiers c : c25ca|c16a.c ................. f(x,y) = y**2 * sin(x*y) ]]
* [[Mathc initiation/Fichiers c : c25cb|c16b.c ................. f(x,y) = x**3 * y**2 - 2*x**2 * y + 3*x ]]
* [[Mathc initiation/Fichiers c : c25cc|c16c.c ................. f(x,y) = exp(2*x+3*y)]]
:
.
:
Calculer les dérivées partielles du second ordre :
* [[Mathc initiation/Fichiers c : c25cd|c16d.c ................. f(x,y) = y**2 * sin(x*y) ]]
* [[Mathc initiation/Fichiers c : c25ce|c16e.c ................. f(x,y) = x**3 * y**2 - 2*x**2 * y + 3*x ]]
* [[Mathc initiation/Fichiers c : c25cf|c16f.c .................. f(x,y) = exp(2*x+3*y)]]
:
.
:
{{AutoCat}}
qe3mtidzhj6q08vig24ninq1uh9rn8v
Mathc initiation/Fichiers h : c26
0
76225
763660
725880
2026-04-14T08:07:57Z
Xhungab
23827
763660
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005v| Sommaire]]
:
{{Partie{{{type|}}}|Calcul des dérivées partielles}}
:
En mathématiques, la dérivée partielle d'une fonction de plusieurs variables est sa dérivée par rapport à l'une de ses variables, les autres étant gardées constantes. [https://en.wikipedia.org/wiki/Partial_derivative wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c26a1|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers h : c26a4|x_fxyz.h ............. Calculer les dérivées partielles]]
:
.
:
Les fonctions et leurs dérivés pour les différents exemples :
* [[Mathc initiation/Fichiers h : c26ba|f.h ]]
:
.
:
Calculer les dérivées partielles du premier ordre :
* [[Mathc initiation/Fichiers c : c26ca|c16a.c ................. f(x,y,z) = y*sin(x*y*z)*z ]]
* [[Mathc initiation/Fichiers c : c26cb|c16b.c ................. f(x,y,z) = x**3*y**2*z-x*y+z ]]
:
.
:
Calculer les dérivées partielles du second ordre :
* [[Mathc initiation/Fichiers c : c26cd|c16d.c ................. f(x,y,z) = y*sin(x*y*z)*z ]]
* [[Mathc initiation/Fichiers c : c26ce|c16e.c ................. f(x,y,z) = x**3*y**2*z-x*y+z ]]
:
.
:
{{AutoCat}}
1a8krnxxgl91zo37q20picwd4ty5k7l
Mathc initiation/Fichiers h : c27
0
76236
763664
725885
2026-04-14T08:21:31Z
Xhungab
23827
763664
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005w| Sommaire]]
:
{{Partie{{{type|}}}|Calcul du gradient au point p}}
:
Le gradient d'une fonction de plusieurs variables en un certain point est un vecteur qui caractérise la variabilité de cette fonction au voisinage de ce point. Défini en tout point où la fonction est différentiable, il définit un champ de vecteurs, également dénommé gradient. Le gradient est la généralisation à plusieurs variables de la dérivée d'une fonction d'une seule variable. [https://fr.wikipedia.org/wiki/Gradient wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c43a4|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers c : c47cb|x_strcg.h ........... Déclaration des structures pour gnuplot]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c26a4|x_fxyz.h ............ Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c27a7|x_grad.h ........... Calculer le gradient au point p]]
* [[Mathc initiation/Fichiers h : 812|kg_3d.h ............ Dessiner la fonction]]
* [[Mathc initiation/a91|kg_grad.h ......... Dessiner le gradient]]
:
.
:
Les fonctions pour les différents exemples :
* [[Mathc initiation/Fichiers h : c27ba|f.h]]
:
.
:
Calculer grad f(x,y)]p :
* [[Mathc initiation/Fichiers c : c27ca|c0a1.c ]] ................. * [[Mathc initiation/Fichiers h : c72a2|c0a2.c ................. x**2 - 4*x*y]]
* [[Mathc initiation/Fichiers c : c27cb|c0b1.c ]] ................. * [[Mathc initiation/Fichiers h : c74a2|c0b2.c ................. sqrt(x**2 + y**2)]]
* [[Mathc initiation/Fichiers h : z_d|c0c1.c ]] ................. * [[Mathc initiation/Fichiers h : c49a2|c0c2.c ................. 2*x**2-3*x*y+8*y**2+2*x-4*y+4]]
* [[Mathc initiation/Fichiers h : c14z_d|c0d1.c]] ................. * [[Mathc initiation/Fichiers h : c22a2|c0d2.c ................. cos(x*y)]]
* [[Mathc initiation/Fichiers h : c23a2|c0e1.c ]] ................. * [[Mathc initiation/Fichiers h : c76a2|c0e2.c ................. cos(x)+sin(y)]]
:
.
:
Calculer grad f(x,y,z)]p :
* [[Mathc initiation/Fichiers c : c27cd|c02f.c ................. f(x,y,z) = y*z**3 - 2*x**2 ]]
:
.
:
{{AutoCat}}
sad7dczsl9l6p7qhaatnyteeif4xv1x
Mathc initiation/Fichiers h : c28
0
76250
763665
725886
2026-04-14T08:21:48Z
Xhungab
23827
763665
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005w| Sommaire]]
:
{{Partie{{{type|}}}|Calcul la dérivée directionnelle}}
:
:
Dérivée directionnelle (Directional derivative)
En analyse mathématique, la notion de dérivée directionnelle permet de quantifier la variation locale d'une fonction dépendant de plusieurs variables, en un point donné et le long d'une direction donnée dans l'espace de ces variables.
[https://fr.wikipedia.org/wiki/D%C3%A9riv%C3%A9e_directionnelle Wikipédia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c28a1|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c26a4|x_fxyz.h ............ Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c28a6|x_v2d.h ............ Les fonctions sur les vecteurs 2d]]
* [[Mathc initiation/Fichiers h : c28a7|x_v3d.h ............ Les fonctions sur les vecteurs 3d]]
* [[Mathc initiation/Fichiers h : c27a7|x_grad.h ........... Calculer le gradient au point p]]
* [[Mathc initiation/Fichiers h : c28a9|x_d_u.h ............ Calculer la dérivée directionnelle]]
<br>
Les fonctions pour les différents exemples :
* [[Mathc initiation/Fichiers h : c28ba|f.h ]]
:
.
:
Calculer la dérivée directionnelle au point p par rapport à un vecteur (2d) donné :
* [[Mathc initiation/Fichiers c : c28ca|c16a.c ................. f(x,y) = x**2 - 5*x*y + 3*y**2 ]]
* [[Mathc initiation/Fichiers c : c28cb|c16b.c ................. f(x,y) = sqrt(9*x**2 - 4*y**2 -1 ]]
:
.
:
Calculer la dérivée directionnelle au point p par rapport à un vecteur (3d) donné :
* [[Mathc initiation/Fichiers c : c28cc|c16c.c ................. f(x,y,z) = x* y**3 *z**2 ]]
* [[Mathc initiation/Fichiers c : c28cd|c16d.c ................. f(x,y,z) = (x+y)*(y+z) ]]
:
.
:
Calculer les coordonnées du vecteur qui donne le plus fort acroissement au point p :
* [[Mathc initiation/Fichiers c : c28ce|c16e.c ................. f(x,y) = x**2 * exp(-2*y) ]]
* [[Mathc initiation/Fichiers c : c28cf|c16f.c ................. f(x,y,z) = sqrt(x*x + y*y + z*z)]]
:
.
:
Calculer les coordonnées du vecteur qui donne le plus fort décroissement au point p :
* [[Mathc initiation/Fichiers c : c28cg|c16g.c ................. f(x,y) = x**2 * exp(-2*y) ]]
* [[Mathc initiation/Fichiers c : c28ch|c16h.c ................. f(x,y,z) = sqrt(x*x + y*y + z*z)]]
:
.
:
Calculer la dérivée directionnelle au point p par rapport à un angle donné :
* [[Mathc initiation/Fichiers c : c28ci|c16i.c ................. f(x,y) = x**2 + 2*x*y - y**2 ]]
* [[Mathc initiation/Fichiers c : c28cj|c16j.c ................. f(x,y) = (x*y - y**2)**4]]
:
.
:
{{AutoCat}}
81mm972tgh8tsg13yzmp8atmijaskfe
Mathc initiation/Fichiers h : c29
0
76284
763666
725887
2026-04-14T08:22:06Z
Xhungab
23827
763666
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005w| Sommaire]]
:
{{Partie{{{type|}}}|L'équation du plan tangent au point P0 pour une fonction f(x,y,z)}}
:
Le gradient d'une fonction de plusieurs variables en un certain point est un vecteur qui caractérise la variabilité de cette fonction au voisinage de ce point. Défini en tout point où la fonction est différentiable, il définit un champ de vecteurs, également dénommé gradient. Le gradient est la généralisation à plusieurs variables de la dérivée d'une fonction d'une seule variable. [https://fr.wikipedia.org/wiki/Gradient wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c29a1|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c26a4|x_fxyz.h ............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c27a7|x_grad.h ........... Calculer le gradient au point p]]
:
.
:
Les fonctions pour les différents exemples :
* [[Mathc initiation/Fichiers h : c29ba|f.h ]]
:
.
:
Calculer l'équation du plan tangent au point P0 pour la fonction f(x,y,z) :
* [[Mathc initiation/Fichiers c : c29ca|c16a.c ................. f(x,y,z) = 4*x**2 - y**2 + 3*z**2 - 10]]
* [[Mathc initiation/Fichiers c : c29cb|c16b.c ................. f(x,y,z) = 2*exp(-x) * cos(y) - z ]]
* [[Mathc initiation/Fichiers c : c29cd|c16c.c ................. f(x,y,z) = x*y+2*y*z-x*z**2+10]]
:
.
:
{{AutoCat}}
d0228mm2685vkzjbyyghbnbz6i5umvu
Mathc initiation/Fichiers h : c37a2
0
76435
763674
725884
2026-04-14T08:28:21Z
Xhungab
23827
763674
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005v| Sommaire]]
:
{{Partie{{{type|}}}|Dessiner une tangente sur une fonction f(x,y) }}
:
En mathématiques et plus spécialement en analyse vectorielle, une fonction numérique à plusieurs variables réelles est une fonction dont l'ensemble de départ E est une partie du produit cartésien Rn. L'ensemble d'arrivée F peut être R ou Rp. [https://fr.wikipedia.org/wiki/Fonction_de_plusieurs_variables wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c26a2|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h ............... Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers c : c47cb|x_strcg.h ........... Déclaration des structures pour gnuplot]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c47fd|kg_3dp.h ........... Dessiner la fonction avec un point]]
* [[Mathc initiation/Fichiers h : c44a2|kg_3dtx.h .......... Dessiner une tangente x variable]]
* [[Mathc initiation/Fichiers h : c43a2|kg_3dty.h .......... Dessiner une tangente y variable]]
:
.
:
Les fonctions :
* [[Mathc initiation/Fichiers h : c40a2|fa.h .......... (x*y**2)/(x**2+y**4)]]
:
.
:
Dessiner quelques tangentes sur la fonction f : (x,y) -> (x*y**2)/(x**2+y**4).
* [[Mathc initiation/Fichiers h : c39a2|Dessinons la fonction et un point]]
* [[Mathc initiation/a24|L'équation d'une tangente]]
* [[Mathc initiation/a93|Dessiner la tangente y variable]]
* [[Mathc initiation/Fichiers h : c41a2|Dessiner la tangente x variable]]
* [[Mathc initiation/Fichiers h : c42a2|Animer la tangente y variable]]
* [[Mathc initiation/Fichiers h : c38a2|Animer la tangente x variable]]
:
.
:
{{AutoCat}}
t90iygt19ydsav7xsg8kzz8xd2fim27
Mathc initiation/Fichiers h : c44a4
0
76524
763592
763575
2026-04-13T15:33:13Z
Xhungab
23827
763592
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
Je vous propose comme cours de référence les trois livres de '''openstax''' en accès libre. Vous pouvez les lire en ligne ou télécharger les PDF.
* [https://openstax.org/details/books/calculus-volume-1 Calculus 1],
* [https://openstax.org/details/books/calculus-volume-2 Calculus 2],
* [https://openstax.org/details/books/calculus-volume-3 Calculus 3].
:
{{Partie{{{type|}}}|[[Mathc initiation/a08| Analyse I ; Les fonctions]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c60a3| Analyse I : Les courbes paramétriques]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/c58a3| Analyse I : Les fonctions vectorielles]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a334| Analyse I : Les suites et les séries]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a16| Analyse II : Dérivées partielles et applications]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005u| Analyse II : Dérivée d'une fonction implicite]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005t| Analyse II : Dérivées des fonctions composées]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a09| Analyse III : Intégrale doubles et applications]]}}
{{Partie{{{type|}}}|[[Mathc initiation/0044| Analyse III : Intégrale triples et applications]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005j| Analyse III : Petite introduction sur les intégrales curvilignes]]}}
{{Partie{{{type|}}}|[[Mathc initiation/0045| Analyse III : Intégrale curvilignes et applications.]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005k| Analyse III : Petite introduction sur les champs de vecteurs]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005l| Analyse III : Intégrale curvilignes dans un champ de vecteurs.]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005h| Analyse III : L'intégrale de surface]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005s| Analyse III : L'intégrale de flux de surface]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/004w| Analyse III : Théorème de Gauss (Théorème de la divergence)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005i| Analyse III : Théorème de Green, de Stoke]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a10| Analyse III : Les équations différentielles]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a512| Analyse IV : Se familiariser avec la Transformée de Laplace]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a584| Analyse IV : Se familiariser avec la transformée en Z]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a592| Analyse IV : Se familiariser avec la transformée de Fourier discrète]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a594| Analyse IV : Se familiariser avec les series de Fourier]]}}
:
.
:
{{Partie{{{type|}}}|fond={{{fond|}}}|prefixTable=I - |prefix1=Troisième Partie : | '''Bibliothèques'''}}
:
Pour éviter de devoir télécharger les bibliothèques à chaque sections, je vous propose ici de télécharger les fichiers le plus souvent utilisés.
{{Partie{{{type|}}}|[[Mathc initiation/a406| La bibliothèque d'analyse I, II, III :]]}}
:
{{AutoCat}}
773bzi2y6kyiar5n7th3gfxdryhcuz7
763656
763592
2026-04-14T08:05:28Z
Xhungab
23827
763656
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
Je vous propose comme cours de référence les trois livres de '''openstax''' en accès libre. Vous pouvez les lire en ligne ou télécharger les PDF.
* [https://openstax.org/details/books/calculus-volume-1 Calculus 1],
* [https://openstax.org/details/books/calculus-volume-2 Calculus 2],
* [https://openstax.org/details/books/calculus-volume-3 Calculus 3].
:
{{Partie{{{type|}}}|[[Mathc initiation/a08| Analyse I ; Les fonctions]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c60a3| Analyse I : Les courbes paramétriques]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/c58a3| Analyse I : Les fonctions vectorielles]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a334| Analyse I : Les suites et les séries]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a16| Analyse II : Fonction de plusieurs variables]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005v| Analyse II : Dérivées partielles. Méthode de Newton (en xy)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005u| Analyse II : Dérivée d'une fonction implicite]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005t| Analyse II : Dérivées des fonctions composées]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a09| Analyse III : Intégrale doubles et applications]]}}
{{Partie{{{type|}}}|[[Mathc initiation/0044| Analyse III : Intégrale triples et applications]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005j| Analyse III : Petite introduction sur les intégrales curvilignes]]}}
{{Partie{{{type|}}}|[[Mathc initiation/0045| Analyse III : Intégrale curvilignes et applications.]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005k| Analyse III : Petite introduction sur les champs de vecteurs]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005l| Analyse III : Intégrale curvilignes dans un champ de vecteurs.]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005h| Analyse III : L'intégrale de surface]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005s| Analyse III : L'intégrale de flux de surface]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/004w| Analyse III : Théorème de Gauss (Théorème de la divergence)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005i| Analyse III : Théorème de Green, de Stoke]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a10| Analyse III : Les équations différentielles]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a512| Analyse IV : Se familiariser avec la Transformée de Laplace]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a584| Analyse IV : Se familiariser avec la transformée en Z]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a592| Analyse IV : Se familiariser avec la transformée de Fourier discrète]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a594| Analyse IV : Se familiariser avec les series de Fourier]]}}
:
.
:
{{Partie{{{type|}}}|fond={{{fond|}}}|prefixTable=I - |prefix1=Troisième Partie : | '''Bibliothèques'''}}
:
Pour éviter de devoir télécharger les bibliothèques à chaque sections, je vous propose ici de télécharger les fichiers le plus souvent utilisés.
{{Partie{{{type|}}}|[[Mathc initiation/a406| La bibliothèque d'analyse I, II, III :]]}}
:
{{AutoCat}}
3e7ff4vzkd6tbqljrz9pa82tz6j038k
763662
763656
2026-04-14T08:20:44Z
Xhungab
23827
763662
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
Je vous propose comme cours de référence les trois livres de '''openstax''' en accès libre. Vous pouvez les lire en ligne ou télécharger les PDF.
* [https://openstax.org/details/books/calculus-volume-1 Calculus 1],
* [https://openstax.org/details/books/calculus-volume-2 Calculus 2],
* [https://openstax.org/details/books/calculus-volume-3 Calculus 3].
:
{{Partie{{{type|}}}|[[Mathc initiation/a08| Analyse I ; Les fonctions]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c60a3| Analyse I : Les courbes paramétriques]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/c58a3| Analyse I : Les fonctions vectorielles]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a334| Analyse I : Les suites et les séries]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a16| Analyse II : Fonction de plusieurs variables]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005v| Analyse II : Dérivées partielles. Méthode de Newton (en xy)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005u| Analyse II : Dérivée d'une fonction implicite]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005t| Analyse II : Dérivées des fonctions composées]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005w| Analyse II : Calculer le gradient et quelques applications]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a09| Analyse III : Intégrale doubles et applications]]}}
{{Partie{{{type|}}}|[[Mathc initiation/0044| Analyse III : Intégrale triples et applications]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005j| Analyse III : Petite introduction sur les intégrales curvilignes]]}}
{{Partie{{{type|}}}|[[Mathc initiation/0045| Analyse III : Intégrale curvilignes et applications.]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005k| Analyse III : Petite introduction sur les champs de vecteurs]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005l| Analyse III : Intégrale curvilignes dans un champ de vecteurs.]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005h| Analyse III : L'intégrale de surface]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005s| Analyse III : L'intégrale de flux de surface]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/004w| Analyse III : Théorème de Gauss (Théorème de la divergence)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005i| Analyse III : Théorème de Green, de Stoke]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a10| Analyse III : Les équations différentielles]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a512| Analyse IV : Se familiariser avec la Transformée de Laplace]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a584| Analyse IV : Se familiariser avec la transformée en Z]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a592| Analyse IV : Se familiariser avec la transformée de Fourier discrète]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a594| Analyse IV : Se familiariser avec les series de Fourier]]}}
:
.
:
{{Partie{{{type|}}}|fond={{{fond|}}}|prefixTable=I - |prefix1=Troisième Partie : | '''Bibliothèques'''}}
:
Pour éviter de devoir télécharger les bibliothèques à chaque sections, je vous propose ici de télécharger les fichiers le plus souvent utilisés.
{{Partie{{{type|}}}|[[Mathc initiation/a406| La bibliothèque d'analyse I, II, III :]]}}
:
{{AutoCat}}
hoq512ftphy6ossgwqys24swuaq7vgw
763675
763662
2026-04-14T08:29:05Z
Xhungab
23827
763675
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
Je vous propose comme cours de référence les trois livres de '''openstax''' en accès libre. Vous pouvez les lire en ligne ou télécharger les PDF.
* [https://openstax.org/details/books/calculus-volume-1 Calculus 1],
* [https://openstax.org/details/books/calculus-volume-2 Calculus 2],
* [https://openstax.org/details/books/calculus-volume-3 Calculus 3].
:
{{Partie{{{type|}}}|[[Mathc initiation/a08| Analyse I ; Les fonctions]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c60a3| Analyse I : Les courbes paramétriques]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/c58a3| Analyse I : Les fonctions vectorielles]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a334| Analyse I : Les suites et les séries]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a16| Analyse II : Fonction de plusieurs variables]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005v| Analyse II : Dérivées partielles. Méthode de Newton (en xy). Applications]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005u| Analyse II : Dérivée d'une fonction implicite]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005t| Analyse II : Dérivées des fonctions composées]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005w| Analyse II : Calculer le gradient et quelques applications]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a09| Analyse III : Intégrale doubles et applications]]}}
{{Partie{{{type|}}}|[[Mathc initiation/0044| Analyse III : Intégrale triples et applications]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005j| Analyse III : Petite introduction sur les intégrales curvilignes]]}}
{{Partie{{{type|}}}|[[Mathc initiation/0045| Analyse III : Intégrale curvilignes et applications.]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005k| Analyse III : Petite introduction sur les champs de vecteurs]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005l| Analyse III : Intégrale curvilignes dans un champ de vecteurs.]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/005h| Analyse III : L'intégrale de surface]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005s| Analyse III : L'intégrale de flux de surface]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/004w| Analyse III : Théorème de Gauss (Théorème de la divergence)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/005i| Analyse III : Théorème de Green, de Stoke]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a10| Analyse III : Les équations différentielles]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a512| Analyse IV : Se familiariser avec la Transformée de Laplace]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a584| Analyse IV : Se familiariser avec la transformée en Z]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a592| Analyse IV : Se familiariser avec la transformée de Fourier discrète]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a594| Analyse IV : Se familiariser avec les series de Fourier]]}}
:
.
:
{{Partie{{{type|}}}|fond={{{fond|}}}|prefixTable=I - |prefix1=Troisième Partie : | '''Bibliothèques'''}}
:
Pour éviter de devoir télécharger les bibliothèques à chaque sections, je vous propose ici de télécharger les fichiers le plus souvent utilisés.
{{Partie{{{type|}}}|[[Mathc initiation/a406| La bibliothèque d'analyse I, II, III :]]}}
:
{{AutoCat}}
j5j8weilj8w5ibq3buv0z0jiexwt6t1
Mathc initiation/Fichiers h : c66a3
0
76832
763673
725883
2026-04-14T08:28:03Z
Xhungab
23827
763673
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005v| Sommaire]]
:
{{Partie{{{type|}}}| Calculer les local minimum , local maximum , point-selle}}
:
Un extremum (pluriel extrema ou extremums), ou extrémum (pluriel extrémums), est une valeur extrême, soit maximum, soit minimum. [https://en.wikipedia.org/wiki/Maximum_and_minimum wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c59a3|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers c : c47cb|x_strcg.h ........... Déclaration des structures pour gnuplot]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c39a3|x_Hess.h .......... Calculer le déterminant]]
* [[Mathc initiation/Fichiers h : c59a5|x_nwtn.h ........... Méthode de Newton]]
* [[Mathc initiation/Fichiers h : c59a6|kg_3dpxy.h ........ Dessiner l'extrema]]
:
.
:
Les fonctions pour les différents exemples :
* [[Mathc initiation/Fichiers h : c54a3|fa.h ... "cos(x)+cos(y)"]]
:
.
:
Local maximum :
* [[Mathc initiation/Fichiers h : c54a4|c00a.c]]
* [[Mathc initiation/Fichiers h : c65a3|c00b.c]]
:
.
:
Local minimun :
* [[Mathc initiation/Fichiers h : c53a3|c01a.c]]
* [[Mathc initiation/Fichiers h : c52a3|c01b.c]]
:
.
:
Point-selle :
* [[Mathc initiation/Fichiers h : c51a3|c02a.c]]
* [[Mathc initiation/Fichiers h : c50a3|c02b.c]]
:
.
:
{{AutoCat}}
s96efjgzkc6pym6c6mfp9zukjyhg47r
Mathc initiation/a16
0
76849
763591
763574
2026-04-13T15:32:08Z
Xhungab
23827
763591
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
.
:
[[Mathc initiation/Fichiers h : c44a4| Sommaire]]
:
.
:
'''L'étude de ce chapitre peut ce faire à l'aide de cette [[https://youtube.com/playlist?list=PLi6peGpf8EPNB2TeHJVw0XVZeIJLqoQCV&feature=shared Playlist]].
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a56|* Dessiner f(x,y) et les lignes de niveaux]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a57|* Dessiner et animer un point sur la fonction f(x,y)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/c58a2|* Dessiner le chemin d'un point sur la fonction f(x,y)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c27a2|* Limite f(x,y) : Formes indéterminées]]}}
:
{{Partie{{{type|}}}|.}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c25|* Dérivées partielles (en xy)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c24|* La méthode de Newton (en xy)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c26|* Dérivées partielles (en xyz)]]}}
:
{{Partie{{{type|}}}|.}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a511|* Matrice hessienne]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c74a3|* Local minimum , local maximum , point-selle]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c66a3|* Calculer les local minimum , local maximum , point-selle]]}}
:
{{Partie{{{type|}}}|.}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c37a2|* Dessiner une tangente sur une fonction f(x,y)]]}}
:
{{Partie{{{type|}}}|.}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c27|* Le gradient au point p (en xy et xyz)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c28|* La dérivée directionnelle (en xy et xyz)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c29|* Plan tangent en P0 pour une fonction f(x,y,z)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c24a4|* Dessiner le vecteur orthogonal au point P]]}}
{{Partie{{{type|}}}|[[Mathc initiation/a60|* Dessiner le plan orthogonal au point P]]}}
{{AutoCat}}
gfvi8c1jerrrd3j6xi9zgq71hqv4vuu
763655
763591
2026-04-14T08:03:36Z
Xhungab
23827
763655
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
.
:
[[Mathc initiation/Fichiers h : c44a4| Sommaire]]
:
.
:
'''L'étude de ce chapitre peut ce faire à l'aide de cette [[https://youtube.com/playlist?list=PLi6peGpf8EPNB2TeHJVw0XVZeIJLqoQCV&feature=shared Playlist]].
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a56|* Dessiner f(x,y) et les lignes de niveaux]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a57|* Dessiner et animer un point sur la fonction f(x,y)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/c58a2|* Dessiner le chemin d'un point sur la fonction f(x,y)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c27a2|* Limite f(x,y) : Formes indéterminées]]}}
:
{{Partie{{{type|}}}|.}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a511|* Matrice hessienne]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c74a3|* Local minimum , local maximum , point-selle]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c66a3|* Calculer les local minimum , local maximum , point-selle]]}}
:
{{Partie{{{type|}}}|.}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c37a2|* Dessiner une tangente sur une fonction f(x,y)]]}}
:
{{Partie{{{type|}}}|.}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c27|* Le gradient au point p (en xy et xyz)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c28|* La dérivée directionnelle (en xy et xyz)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c29|* Plan tangent en P0 pour une fonction f(x,y,z)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c24a4|* Dessiner le vecteur orthogonal au point P]]}}
{{Partie{{{type|}}}|[[Mathc initiation/a60|* Dessiner le plan orthogonal au point P]]}}
{{AutoCat}}
rxztzysl2jxcyzzpuwykbwb0562qnzm
763661
763655
2026-04-14T08:18:45Z
Xhungab
23827
763661
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
.
:
[[Mathc initiation/Fichiers h : c44a4| Sommaire]]
:
.
:
'''L'étude de ce chapitre peut ce faire à l'aide de cette [[https://youtube.com/playlist?list=PLi6peGpf8EPNB2TeHJVw0XVZeIJLqoQCV&feature=shared Playlist]].
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a56|* Dessiner f(x,y) et les lignes de niveaux]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a57|* Dessiner et animer un point sur la fonction f(x,y)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/c58a2|* Dessiner le chemin d'un point sur la fonction f(x,y)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c27a2|* Limite f(x,y) : Formes indéterminées]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a511|* Matrice hessienne]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c74a3|* Local minimum , local maximum , point-selle]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c66a3|* Calculer les local minimum , local maximum , point-selle]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c37a2|* Dessiner une tangente sur une fonction f(x,y)]]}}
{{AutoCat}}
clqqs3ayy55abxtkuicm0p3w78ueko2
763669
763661
2026-04-14T08:25:57Z
Xhungab
23827
763669
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/Fichiers h : c44a4| Sommaire]]
'''L'étude de ce chapitre peut ce faire à l'aide de cette [[https://youtube.com/playlist?list=PLi6peGpf8EPNB2TeHJVw0XVZeIJLqoQCV&feature=shared Playlist]].
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/a56|* Dessiner f(x,y) et les lignes de niveaux]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a57|* Dessiner et animer un point sur la fonction f(x,y)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/c58a2|* Dessiner le chemin d'un point sur la fonction f(x,y)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c27a2|* Limite f(x,y) : Formes indéterminées]]}}
{{AutoCat}}
j4fwagndhw34r2s9hdzoniasizbny8i
Mathc initiation/Fichiers h : c67
0
76850
763594
725890
2026-04-13T15:34:57Z
Xhungab
23827
763594
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005u| Sommaire]]
:
{{Partie{{{type|}}}|Dérivée d'une fonction implicite}}
:
En mathématiques, une équation entre différentes variables où une variable n'est pas explicitée en fonction des autres est appelée une équation implicite. Une fonction implicite est une fonction qui se déduit implicitement d'une telle équation. [[https://fr.wikipedia.org/wiki/Fonction_implicite wikipedia]]
:
.
:
Copier ces fichiers dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : x_66a1|x_hfile.h ................. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .................. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ............... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .................. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : x_66a8|x_dydx.h ............... La fonction pour dériver implicitement]]
:
.
:
Déclaration des fonctions :
* [[Mathc initiation/Fichiers h : x_66a4|f.h ]]
:
.
:
Quelques exemples :
* [[Mathc initiation/Fichiers h : x_66ca| c16a.c ................. f = "y**4 + 3*y - 4*x**3 - 5*x - 1"]]
* [[Mathc initiation/Fichiers h : x_66cb| c16b.c ................. f = "4*y**2 + 9*x**2 - 40"]]
* [[Mathc initiation/Fichiers h : x_66cc| c16c.c ................. f = "x**2 * y + sin(y) - 2*PI"]]
:
.
:
{{AutoCat}}
4r8shyzoxjr2g9gsxhlsrx1e5o47pqr
Mathc initiation/Fichiers h : c68
0
76865
763595
725891
2026-04-13T15:35:14Z
Xhungab
23827
763595
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005u| Sommaire]]
:
{{Partie{{{type|}}}|Dérivée d'une fonction implicite (xyz)}}
:
En mathématiques, une équation entre différentes variables où une variable n'est pas explicitée en fonction des autres est appelée une équation implicite. Une fonction implicite est une fonction qui se déduit implicitement d'une telle équation. [[https://fr.wikipedia.org/wiki/Fonction_implicite wikipedia]]
:
.
:
Copier ces fichiers dans votre répertoire de travail :
:
* [[Mathc initiation/Fichiers h : x_68a1|x_hfile.h ................. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h ................... Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ................ Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers h : c26a4|x_fxyz.h ................. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : x_68a5|x_dz.h .................... Les fonctions de dérivation implicite]]
:
.
:
* [[Mathc initiation/Fichiers h : x_68fa|fa.h ]]
:
.
:
Quelques exemples :
:
* [[Mathc initiation/Fichiers h : x_68ca| c16a.c]]
* [[Mathc initiation/Fichiers h : x_68cb| c16b.c]]
:
.
:
{{AutoCat}}
00hwpe1jchehtaumn5djpvwf83lik78
Mathc initiation/Fichiers h : c74a3
0
77897
763672
725882
2026-04-14T08:27:45Z
Xhungab
23827
763672
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005v| Sommaire]]
:
{{Partie{{{type|}}}|Minimum local, maximum local ... }}
:
Un extremum (pluriel extrema ou extremums), ou extrémum (pluriel extrémums), est une valeur extrême, soit maximum, soit minimum. [https://en.wikipedia.org/wiki/Maximum_and_minimum wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c40a3|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers c : c47cb|x_strcg.h ........... Déclaration des structures pour gnuplot]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c39a3|x_Hess.h ........... Calculer le déterminant]]
* [[Mathc initiation/a25|kg_3dp.h .......... Dessiner l'extrema]]
:
.
:
Les fonctions pour les différents exemples :
* [[Mathc initiation/Fichiers h : c41a3|f.h]]
:
.
:
Méthode 1 local minimun, local maximun, ... :
* [[Mathc initiation/Fichiers h : c37a3|c00a.c ... "-x**2 - 4*x - y**2 + 2*y - 1"]]
* [[Mathc initiation/Fichiers h : 813|c00b.c ... "x**2 + 4*y**2 - x + 2*y"]]
* [[Mathc initiation/Fichiers h : c79a3|c00c.c ... "x**3 + 3*x*y - y**3"]]
:
.
:
Méthode 2 local minimun, local maximun, ... :
* [[Mathc initiation/Fichiers h : c71a3|c01a.c]]
* [[Mathc initiation/Fichiers h : c73a3|c01b.c]]
* [[Mathc initiation/Fichiers h : c72a3|c01c.c]]
:
.
:
{{AutoCat}}
gmy2zsedtkq4e6wo8kwxs0aockr9cqf
Les cartes graphiques/Le rendu d'une scène 3D : concepts de base
0
79234
763601
763567
2026-04-13T16:47:36Z
Mewtow
31375
/* La lumière incidente : le terme géométrique */
763601
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Mettons qu'on regarde un objet semi-transparent, qui est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent.
Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, on ne voit pas l'objet semi-transparent et seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
===Les fragments et les ROPs===
Maintenant, qu'en est-il pour ce qui est du rendu 3D ? Le mélange est réalisé pixel par pixel. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent.
Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel. Si les objets sont opaques et le fragment le plus proche est sélectionné. Mais avec des fragments transparents, les choses sont plus compliquées, car la couleur final est un mélange de plusieurs fragments non-cachés. Vu que plusieurs fragments sont censés être visibles, on ne sait pas quelle coordonnée z stocker. Il y a donc une interaction entre tampon de profondeur et mélange ''alpha''. Le tampon de profondeur est comme désactivé pour les fragments transparents, il n'est appliqué que sur les objets opaques.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha'' : une technique obsolète===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous d'un seuil, le fragment est simplement abandonné. Il s'agit d'une technique binaire de gestion de la transparence, qui est aujourd'hui obsolète. Elle optimisait le rendu de textures où les pixels sont soit totalement opaques, soit totalement transparents.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''. La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
iltz7q4mrdqd97bxkei4fcwj1i7hm3b
763602
763601
2026-04-13T16:52:46Z
Mewtow
31375
/* La lumière incidente : le terme géométrique */
763602
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Mettons qu'on regarde un objet semi-transparent, qui est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent.
Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, on ne voit pas l'objet semi-transparent et seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
===Les fragments et les ROPs===
Maintenant, qu'en est-il pour ce qui est du rendu 3D ? Le mélange est réalisé pixel par pixel. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent.
Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel. Si les objets sont opaques et le fragment le plus proche est sélectionné. Mais avec des fragments transparents, les choses sont plus compliquées, car la couleur final est un mélange de plusieurs fragments non-cachés. Vu que plusieurs fragments sont censés être visibles, on ne sait pas quelle coordonnée z stocker. Il y a donc une interaction entre tampon de profondeur et mélange ''alpha''. Le tampon de profondeur est comme désactivé pour les fragments transparents, il n'est appliqué que sur les objets opaques.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha'' : une technique obsolète===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous d'un seuil, le fragment est simplement abandonné. Il s'agit d'une technique binaire de gestion de la transparence, qui est aujourd'hui obsolète. Elle optimisait le rendu de textures où les pixels sont soit totalement opaques, soit totalement transparents.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
khv94jy1j5b5bag62re2snbgt03vg16
763628
763602
2026-04-13T19:37:18Z
Mewtow
31375
/* Le test alpha : une technique obsolète */
763628
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Mettons qu'on regarde un objet semi-transparent, qui est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent.
Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, on ne voit pas l'objet semi-transparent et seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
===Les fragments et les ROPs===
Maintenant, qu'en est-il pour ce qui est du rendu 3D ? Le mélange est réalisé pixel par pixel. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent.
Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel. Si les objets sont opaques et le fragment le plus proche est sélectionné. Mais avec des fragments transparents, les choses sont plus compliquées, car la couleur final est un mélange de plusieurs fragments non-cachés. Vu que plusieurs fragments sont censés être visibles, on ne sait pas quelle coordonnée z stocker. Il y a donc une interaction entre tampon de profondeur et mélange ''alpha''. Le tampon de profondeur est comme désactivé pour les fragments transparents, il n'est appliqué que sur les objets opaques.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous d'un seuil, le fragment est simplement abandonné. Il s'agit d'une technique binaire de gestion de la transparence, qui est utilisé dans certains scénarios spécifiques. Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
jiq8ggrapti6fnycvtxmjpiysorpc8k
763629
763628
2026-04-13T19:41:05Z
Mewtow
31375
/* Le test alpha */
763629
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Mettons qu'on regarde un objet semi-transparent, qui est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent.
Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, on ne voit pas l'objet semi-transparent et seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
===Les fragments et les ROPs===
Maintenant, qu'en est-il pour ce qui est du rendu 3D ? Le mélange est réalisé pixel par pixel. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent.
Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel. Si les objets sont opaques et le fragment le plus proche est sélectionné. Mais avec des fragments transparents, les choses sont plus compliquées, car la couleur final est un mélange de plusieurs fragments non-cachés. Vu que plusieurs fragments sont censés être visibles, on ne sait pas quelle coordonnée z stocker. Il y a donc une interaction entre tampon de profondeur et mélange ''alpha''. Le tampon de profondeur est comme désactivé pour les fragments transparents, il n'est appliqué que sur les objets opaques.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
21ml4opqd5i9d867od9y9k5yqiij5so
763630
763629
2026-04-13T19:42:13Z
Mewtow
31375
/* Le mélange alpha */
763630
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Mettons qu'on regarde un objet semi-transparent, qui est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent.
Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, on ne voit pas l'objet semi-transparent et seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Un défaut de cette méthode est qu'elle se marie mal avec le tampon de profondeur. Le mélange ''alpha'' ne fonctionne que si les objets sont rendus du plus lointain au plus proche.
===Les fragments et les ROPs===
Maintenant, qu'en est-il pour ce qui est du rendu 3D ? Le mélange est réalisé pixel par pixel. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent.
Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel. Si les objets sont opaques et le fragment le plus proche est sélectionné. Mais avec des fragments transparents, les choses sont plus compliquées, car la couleur final est un mélange de plusieurs fragments non-cachés. Vu que plusieurs fragments sont censés être visibles, on ne sait pas quelle coordonnée z stocker. Il y a donc une interaction entre tampon de profondeur et mélange ''alpha''. Le tampon de profondeur est comme désactivé pour les fragments transparents, il n'est appliqué que sur les objets opaques.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
i4t0gj54812ko3dzoj8vouqh2okig5x
763631
763630
2026-04-13T19:44:01Z
Mewtow
31375
/* Le mélange alpha */
763631
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Mettons qu'on regarde un objet semi-transparent, qui est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent.
Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, on ne voit pas l'objet semi-transparent et seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Un défaut de cette méthode est qu'elle se marie mal avec le tampon de profondeur. Le mélange ''alpha'' ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
===Les fragments et les ROPs===
Maintenant, qu'en est-il pour ce qui est du rendu 3D ? Le mélange est réalisé pixel par pixel. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent.
Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel. Si les objets sont opaques et le fragment le plus proche est sélectionné. Mais avec des fragments transparents, les choses sont plus compliquées, car la couleur final est un mélange de plusieurs fragments non-cachés. Vu que plusieurs fragments sont censés être visibles, on ne sait pas quelle coordonnée z stocker. Il y a donc une interaction entre tampon de profondeur et mélange ''alpha''. Le tampon de profondeur est comme désactivé pour les fragments transparents, il n'est appliqué que sur les objets opaques.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
f6zr1apg1twgqdid4bqlvi6x31ahyq1
763632
763631
2026-04-13T19:46:17Z
Mewtow
31375
/* Le tampon de profondeur */
763632
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Mettons qu'on regarde un objet semi-transparent, qui est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent.
Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, on ne voit pas l'objet semi-transparent et seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Un défaut de cette méthode est qu'elle se marie mal avec le tampon de profondeur. Le mélange ''alpha'' ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
===Les fragments et les ROPs===
Maintenant, qu'en est-il pour ce qui est du rendu 3D ? Le mélange est réalisé pixel par pixel. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent.
Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel. Si les objets sont opaques et le fragment le plus proche est sélectionné. Mais avec des fragments transparents, les choses sont plus compliquées, car la couleur final est un mélange de plusieurs fragments non-cachés. Vu que plusieurs fragments sont censés être visibles, on ne sait pas quelle coordonnée z stocker. Il y a donc une interaction entre tampon de profondeur et mélange ''alpha''. Le tampon de profondeur est comme désactivé pour les fragments transparents, il n'est appliqué que sur les objets opaques.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
0d5vstffgay5sdqj596yiriduf4o8s9
763633
763632
2026-04-13T19:47:14Z
Mewtow
31375
/* Les fragments et les ROPs */
763633
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Mettons qu'on regarde un objet semi-transparent, qui est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent.
Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, on ne voit pas l'objet semi-transparent et seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Un défaut de cette méthode est qu'elle se marie mal avec le tampon de profondeur. Le mélange ''alpha'' ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
===Les fragments et les ROPs===
Maintenant, qu'en est-il pour ce qui est du rendu 3D ? Le mélange est réalisé pixel par pixel. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
2fdylr1mt8i2p4n6aockb95gsb13ool
763634
763633
2026-04-13T19:47:32Z
Mewtow
31375
/* La transparence, les fragments et les ROPs */
763634
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Mettons qu'on regarde un objet semi-transparent, qui est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent.
Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, on ne voit pas l'objet semi-transparent et seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Maintenant, qu'en est-il pour ce qui est du rendu 3D ? Le mélange est réalisé pixel par pixel. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel.
Un défaut de cette méthode est qu'elle se marie mal avec le tampon de profondeur. Le mélange ''alpha'' ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
icf23746up7xtuhfdaiwbkdh4uewd6c
763635
763634
2026-04-13T19:49:44Z
Mewtow
31375
/* Le mélange alpha */
763635
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Histoire de simplifier les explications, nous allons d'abord voir le cas où un objet semi-transparent est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent. Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Maintenant, qu'en est-il du cas où plusieurs objets sont superposés ? Le mélange est réalisé pixel par pixel. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel.
Un défaut de cette méthode est qu'elle se marie mal avec le tampon de profondeur. Le mélange ''alpha'' ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
60stztlra21mog8l4npf7nn4msfykq5
763636
763635
2026-04-13T19:52:03Z
Mewtow
31375
/* Le mélange alpha */
763636
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
Cependant, faire cela pose un problème : les objets transparents ne marchent pas ! Et pour comprendre pourquoi, ainsi que comment corriger ce problème, nous allons devoir expliquer la notion de fragment.
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Histoire de simplifier les explications, nous allons d'abord voir le cas où un objet semi-transparent est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent. Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Maintenant, qu'en est-il du cas où plusieurs objets sont superposés ? Si vous tracez une demi-droite dont l'origine est la caméra et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points, un point par objet sur la ligne du regarde. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel.
Il est possible d'utiliser le mélange ''alpha'' pour cela. Il suffit de faire le mélange ''alpha'' entre le fragment qui vient d'être calculé, et le pixel dans le ''framebuffer''. le pixel déjà dans le ''framebuffer'' est un résultat temporaire, né du mélange ''alpha'' de tous les fragments précédents. Un défaut de cette méthode est qu'elle ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
Pour appliquer le mélange ''alpha'' correctement, la profondeur des fragments est gérée en même temps que la transparence, par un même circuit. Il est appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. En effet, il faut connaitre la couleur final d'un fragment, pour faire les calculs. Et celle-ci n'est connue qu'en sortie de l'unité de texture, au plus tôt.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
h95i31sup9ttck0rd2ik0gywp10o6jd
763637
763636
2026-04-13T19:54:11Z
Mewtow
31375
/* La transparence, les fragments et les ROPs */
763637
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
En réalité, la profondeur des fragments est gérée par un circuit appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. Et nous allons voir pourquoi la transparence est gérée à la fin du pipeline.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Histoire de simplifier les explications, nous allons d'abord voir le cas où un objet semi-transparent est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent. Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Maintenant, qu'en est-il du cas où plusieurs objets sont superposés ? Si vous tracez une demi-droite dont l'origine est la caméra et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points, un point par objet sur la ligne du regarde. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel.
Il est possible d'utiliser le mélange ''alpha'' pour cela. Il suffit de faire le mélange ''alpha'' entre le fragment qui vient d'être calculé, et le pixel dans le ''framebuffer''. le pixel déjà dans le ''framebuffer'' est un résultat temporaire, né du mélange ''alpha'' de tous les fragments précédents. Un défaut de cette méthode est qu'elle ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo.
Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
g96ie788wu9oc1uxw2no8as0dmkuchm
763638
763637
2026-04-13T20:15:37Z
Mewtow
31375
/* Le test alpha */
763638
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
En réalité, la profondeur des fragments est gérée par un circuit appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. Et nous allons voir pourquoi la transparence est gérée à la fin du pipeline.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Histoire de simplifier les explications, nous allons d'abord voir le cas où un objet semi-transparent est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent. Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Maintenant, qu'en est-il du cas où plusieurs objets sont superposés ? Si vous tracez une demi-droite dont l'origine est la caméra et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points, un point par objet sur la ligne du regarde. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel.
Il est possible d'utiliser le mélange ''alpha'' pour cela. Il suffit de faire le mélange ''alpha'' entre le fragment qui vient d'être calculé, et le pixel dans le ''framebuffer''. le pixel déjà dans le ''framebuffer'' est un résultat temporaire, né du mélange ''alpha'' de tous les fragments précédents. Un défaut de cette méthode est qu'elle ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
===Le test ''alpha''===
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo. L'''alpha test'' permet donc de gagner en performance au prix d'une baisse de la qualité d'image.
Il y a cependant des cas où l'usage de l'''alpha test'' est primordial, au-delà d'une question de performances. Un exemple classique est celui du rendu du feuillage dans un jeu 3D. Un feuillage est composé en assemblant plusieurs images de feuilles. Chaque feuille est un carré sur lequel on place une texture de feuille, qui est opaque pour la partie verte des feuilles, transparente pour le reste. Les carrés ne sont cependant pas superposés, mais s'intersectent fortement, ce qui fait que le mélange ''alpha'' ne donne pas de bons résultats. L'usage de l'''alpha test'' permet d'obtenir un rendu correct : il suffit de calculer la couleur de chaque fragment, et de l'abandonner si sa transparence est égale à 50%, le garder si elle est supérieure à 50%. C'est bizarre, mais ca marche très bien.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
g8usb9eytfx7tl0pdpc498s6pd1hzo5
763639
763638
2026-04-13T20:16:31Z
Mewtow
31375
/* Le test alpha */
763639
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
En réalité, la profondeur des fragments est gérée par un circuit appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. Et nous allons voir pourquoi la transparence est gérée à la fin du pipeline.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Histoire de simplifier les explications, nous allons d'abord voir le cas où un objet semi-transparent est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent. Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Maintenant, qu'en est-il du cas où plusieurs objets sont superposés ? Si vous tracez une demi-droite dont l'origine est la caméra et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points, un point par objet sur la ligne du regarde. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel.
Il est possible d'utiliser le mélange ''alpha'' pour cela. Il suffit de faire le mélange ''alpha'' entre le fragment qui vient d'être calculé, et le pixel dans le ''framebuffer''. le pixel déjà dans le ''framebuffer'' est un résultat temporaire, né du mélange ''alpha'' de tous les fragments précédents. Un défaut de cette méthode est qu'elle ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
===Le test ''alpha''===
Le test ''alpha'' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo. L'''alpha test'' permet donc de gagner en performance au prix d'une baisse de la qualité d'image.
Il y a cependant des cas où l'usage du test ''alpha'' est primordial, au-delà d'une question de performances. Un exemple classique est celui du rendu du feuillage dans un jeu 3D. Un feuillage est composé en assemblant plusieurs images de feuilles. Chaque feuille est un carré sur lequel on place une texture de feuille, qui est opaque pour la partie verte des feuilles, transparente pour le reste. Les carrés ne sont cependant pas superposés, mais s'intersectent fortement, ce qui fait que le mélange ''alpha'' ne donne pas de bons résultats. L'usage du test ''alpha'' permet d'obtenir un rendu correct : il suffit de calculer la couleur de chaque fragment, et de l'abandonner si sa transparence est égale à 50%, le garder si elle est supérieure à 50%. C'est bizarre, mais ça marche très bien.
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
tekk6sc5i4bg9i0chltvryxwfc0ko9v
763640
763639
2026-04-13T20:17:36Z
Mewtow
31375
/* Le test alpha */
763640
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
En réalité, la profondeur des fragments est gérée par un circuit appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. Et nous allons voir pourquoi la transparence est gérée à la fin du pipeline.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Histoire de simplifier les explications, nous allons d'abord voir le cas où un objet semi-transparent est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent. Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Elle agit comme un coefficient qui dit comment mélanger la couleur de l'objet transparent et celui de l'objet opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Pour résumer, le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Maintenant, qu'en est-il du cas où plusieurs objets sont superposés ? Si vous tracez une demi-droite dont l'origine est la caméra et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points, un point par objet sur la ligne du regarde. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel.
Il est possible d'utiliser le mélange ''alpha'' pour cela. Il suffit de faire le mélange ''alpha'' entre le fragment qui vient d'être calculé, et le pixel dans le ''framebuffer''. le pixel déjà dans le ''framebuffer'' est un résultat temporaire, né du mélange ''alpha'' de tous les fragments précédents. Un défaut de cette méthode est qu'elle ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
===Le test ''alpha''===
Le test ''alpha'' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo. L'''alpha test'' permet donc de gagner en performance au prix d'une baisse de la qualité d'image.
Il y a cependant des cas où l'usage du test ''alpha'' est primordial, au-delà d'une question de performances. Un exemple classique est celui du rendu du feuillage dans un jeu 3D. Un feuillage est composé en assemblant plusieurs images de feuilles. Chaque feuille est un carré sur lequel on place une texture de feuille, qui est opaque pour la partie verte des feuilles, transparente pour le reste. Les carrés ne sont cependant pas superposés, mais s'intersectent fortement, ce qui fait que le mélange ''alpha'' ne donne pas de bons résultats. L'usage du test ''alpha'' permet d'obtenir un rendu correct. Pour d'informations via ce lien :
* [https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f Anti-aliased Alpha Test: The Esoteric Alpha To Coverage].
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
kco4fzliuc0d02k0qrgdh66xk4ytz51
763649
763640
2026-04-13T23:35:08Z
Mewtow
31375
/* Le mélange alpha */
763649
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les triangles sont rastérisés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
En réalité, la profondeur des fragments est gérée par un circuit appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. Et nous allons voir pourquoi la transparence est gérée à la fin du pipeline.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Histoire de simplifier les explications, nous allons d'abord voir le cas où un objet semi-transparent est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent. Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Plus la composante alpha est élevée, plus le pixel est opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Elle est ajoutée aux composantes RGB, ce qui fait que tout fragment contient une "couleur de transparence" en plus des couleurs RGB. Elle agit comme un coefficient qui dit comment mélanger la couleur d'un objet transparent et d'un objet opaque. Le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Maintenant, qu'en est-il du cas où plusieurs objets sont superposés ? Si vous tracez une demi-droite dont l'origine est la caméra et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points, un point par objet sur la ligne du regarde. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel.
Il est possible d'utiliser le mélange ''alpha'' pour cela. Il suffit de faire le mélange ''alpha'' entre le fragment qui vient d'être calculé, et le pixel dans le ''framebuffer''. le pixel déjà dans le ''framebuffer'' est un résultat temporaire, né du mélange ''alpha'' de tous les fragments précédents. Un défaut de cette méthode est qu'elle ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
===Le test ''alpha''===
Le test ''alpha'' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo. L'''alpha test'' permet donc de gagner en performance au prix d'une baisse de la qualité d'image.
Il y a cependant des cas où l'usage du test ''alpha'' est primordial, au-delà d'une question de performances. Un exemple classique est celui du rendu du feuillage dans un jeu 3D. Un feuillage est composé en assemblant plusieurs images de feuilles. Chaque feuille est un carré sur lequel on place une texture de feuille, qui est opaque pour la partie verte des feuilles, transparente pour le reste. Les carrés ne sont cependant pas superposés, mais s'intersectent fortement, ce qui fait que le mélange ''alpha'' ne donne pas de bons résultats. L'usage du test ''alpha'' permet d'obtenir un rendu correct. Pour d'informations via ce lien :
* [https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f Anti-aliased Alpha Test: The Esoteric Alpha To Coverage].
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
i5ojh6syalskgwd2dcdbqc9cvhyqhzy
763650
763649
2026-04-13T23:38:41Z
Mewtow
31375
/* Le tampon de profondeur */
763650
wikitext
text/x-wiki
Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D.
==Les bases du rendu 3D==
Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme.
===Les objets 3D et leur géométrie===
[[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]]
Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues.
[[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]]
Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''.
La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie.
===La caméra : le point de vue depuis l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par :
* une position ;
* par la direction du regard (un vecteur).
A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''.
[[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]]
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
La majorité des jeux vidéos ajoutent deux plans :
* un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches.
* Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains.
Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels.
===Les textures===
Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief.
[[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]]
Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres.
Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique.
Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas.
===La différence entre rastérisation et lancer de rayons===
[[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]]
Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal.
La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''.
La rastérisation est structurée autour de trois étapes principales :
* une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D ;
* une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles ;
* une étape de '''rastérisation''' qui effectue beaucoup de traitements différents, mais dont le principal est d'attribuer chaque triangle à un pixel donné de l'écran.
[[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]]
L'étape de traitement de la géométrie et celle de rastérisation sont souvent réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Il existe plusieurs rendus différents et l'étape de rastérisation dépend fortement du rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée.
Mais quoiqu'il en soit, le rendu d'une image est décomposé en une succession d'étapes, chacune ayant un rôle bien précis. Le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''',qui sera détaillé dans ce qui suit.
==Le calcul de la géométrie==
Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective.
===Les trois étapes de transformation===
La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour.
De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets..
[[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]]
Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z).
[[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]]
Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces.
===Les changements de coordonnées se font via des multiplications de matrices===
Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail.
Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble.
Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc.
Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps.
==L'élimination des surfaces cachées==
Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''.
Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre.
===Les différentes formes de ''culling''/''clipping''===
La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable.
[[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]]
Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''.
[[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]]
Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger.
L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z.
===L'algorithme du peintre===
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''.
[[File:Polygons cross.svg|vignette|Polygons cross]]
[[File:Painters problem.svg|vignette|Painters problem]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
===Le tampon de profondeur===
[[File:Z-buffer no text.jpg|vignette|Z-buffer correspondant à un rendu]]
Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra.
Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective.
Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde.
==La rastérisation et les textures==
L'étape de rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres.
L'étape de rastérisation effectue aussi beaucoup de traitements différents, mais l'idée structurante est que rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures.
===Le rendu en fil de fer===
[[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]]
Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS.
{|
|[[File:Maze war.jpg|vignette|Maze war]]
|[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]]
|}
Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet.
L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer.
Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres.
===Le rendu à primitives colorées===
[[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]]
Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''.
[[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]]
La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique.
Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés.
Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet :
* [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.]
===Le placage de textures direct===
Les deux rendus précédents sont très simples et il n'existe pas de carte graphique qui les implémente. Cependant, une amélioration des rendus précédents a été implémentée sur des cartes graphiques assez anciennes. Le rendu en question est appelé le '''rendu par placage de texture direct''', que nous appellerons rendu direct dans ce qui suit. Le rendu direct a été utilisé sur les anciennes consoles de jeu et sur les anciennes bornes d'arcade, mais il est aujourd'hui abandonné. Il n'a servi que de transition entre rendu 2D et rendu 3D.
L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''.
L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''.
[[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]]
La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire.
: Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran.
Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche.
Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse.
L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes.
Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''.
{|class="wikitable"
|-
! Géométrie
| Processeurs dédiés programmé pour émuler le pipeline graphique
|-
! Tri des quads du plus lointain au plus proche
| Processeur principal (implémentation logicielle)
|-
! Application des textures
| ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''.
|}
L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque.
Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D.
[[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]]
Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn.
===Le placage de textures inverse===
Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet.
[[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]]
Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale.
[[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]]
Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés.
[[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]]
Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé.
Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités.
L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres.
==La transparence, les fragments et les ROPs==
Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées.
La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
|}
Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Tampon de profondeur
| Placage de textures
|}
En réalité, la profondeur des fragments est gérée par un circuit appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. Et nous allons voir pourquoi la transparence est gérée à la fin du pipeline.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
===Le mélange ''alpha''===
La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Histoire de simplifier les explications, nous allons d'abord voir le cas où un objet semi-transparent est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent. Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque.
La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Plus la composante alpha est élevée, plus le pixel est opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Elle est ajoutée aux composantes RGB, ce qui fait que tout fragment contient une "couleur de transparence" en plus des couleurs RGB. Elle agit comme un coefficient qui dit comment mélanger la couleur d'un objet transparent et d'un objet opaque. Le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''.
: <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math>
[[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]]
Maintenant, qu'en est-il du cas où plusieurs objets sont superposés ? Si vous tracez une demi-droite dont l'origine est la caméra et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points, un point par objet sur la ligne du regarde. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''.
Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Il connait notamment une composante ''alpha'', qui est ajouté aux trois couleurs RGB. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel.
Il est possible d'utiliser le mélange ''alpha'' pour cela. Il suffit de faire le mélange ''alpha'' entre le fragment qui vient d'être calculé, et le pixel dans le ''framebuffer''. le pixel déjà dans le ''framebuffer'' est un résultat temporaire, né du mélange ''alpha'' de tous les fragments précédents. Un défaut de cette méthode est qu'elle ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Une solution, utilisée par beaucoup de moteurs 3D, est de rendre séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, mais les objets transparents doivent être triés selon leur distance. Quelques optimisations permettent cependant de passer outre certaines de ces contraintes.
===Le test ''alpha''===
Le test ''alpha'' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc.
Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo. L'''alpha test'' permet donc de gagner en performance au prix d'une baisse de la qualité d'image.
Il y a cependant des cas où l'usage du test ''alpha'' est primordial, au-delà d'une question de performances. Un exemple classique est celui du rendu du feuillage dans un jeu 3D. Un feuillage est composé en assemblant plusieurs images de feuilles. Chaque feuille est un carré sur lequel on place une texture de feuille, qui est opaque pour la partie verte des feuilles, transparente pour le reste. Les carrés ne sont cependant pas superposés, mais s'intersectent fortement, ce qui fait que le mélange ''alpha'' ne donne pas de bons résultats. L'usage du test ''alpha'' permet d'obtenir un rendu correct. Pour d'informations via ce lien :
* [https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f Anti-aliased Alpha Test: The Esoteric Alpha To Coverage].
==L'éclairage d'une scène 3D==
L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation.
===Les sources de lumière et les couleurs associées===
L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous.
[[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]]
[[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]]
Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus.
Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles.
* Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus.
* Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous.
[[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]]
En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante.
===La lumière incidente : le terme géométrique===
Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''.
La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit.
: <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math>
La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond.
Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante.
La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée.
[[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]]
[[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]]
En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré.
[[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]]
Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet.
La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc :
: <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math>
Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture.
===Le produit scalaire de deux vecteurs===
Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante :
: <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B.
L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant :
: <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math>
En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué.
Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire.
===La réflexion de la lumière sur la surface===
[[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]]
Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''.
Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''.
{|
|-
|[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]]
|[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]]
|}
Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''.
Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants :
* L est le vecteur pour la lumière incidente ;
* N est la normale du sommet ;
* I est l'intensité de la source de lumière ;
* <math>C_d</math> est la couleur diffuse.
: <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math>
Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse.
: <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante.
En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses.
[[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]]
[[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]]
Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous :
: <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math>
La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire.
: <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math>
La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution.
===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel===
Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud.
[[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]]
L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations.
L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement.
L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''.
L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel.
La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec.
[[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]]
Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser.
[[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]]
L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable.
La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres.
{|
|-
|[[File:Per face lighting.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]]
|-
|[[File:Per face lighting example.png|vignette|upright=1|Flat shading]]
|[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]]
|[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]]
|}
===Les ''shaders'' : des programmes exécutés sur le GPU===
Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures.
Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| Unité de T&L : géométrie
| Rastérisation
| Placage de textures
| ''Raster Operations Pipeline''
|}
L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs.
Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage.
L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables.
[[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]]
Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux.
Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture.
{|class="wikitable"
|-
! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders''
|-
| rowspan="2" class="f_rouge" | ''Vertex shader''
| rowspan="2" | Rastérisation
| Placage de textures
| rowspan="2" |''Raster Operations Pipeline''
|-
| class="f_rouge" | ''Pixel shader''
|}
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage des anciens PC
| prevText=Les cartes d'affichage des anciens PC
| next=Avant les GPUs : les cartes accélératrices 3D
| nextText=Avant les GPUs : les cartes accélératrices 3D
}}{{autocat}}
00kwobid57eid9o4lx94dfcoipqy2go
Mathc initiation/a60
0
80360
763668
725889
2026-04-14T08:22:43Z
Xhungab
23827
763668
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005w| Sommaire]]
:
{{Partie{{{type|}}}|Dessiner le plan orthogonal au point P}}
:
Le gradient d'une fonction de plusieurs variables en un certain point est un vecteur qui caractérise la variabilité de cette fonction au voisinage de ce point. Défini en tout point où la fonction est différentiable, il définit un champ de vecteurs, également dénommé gradient. Le gradient est la généralisation à plusieurs variables de la dérivée d'une fonction d'une seule variable. [https://fr.wikipedia.org/wiki/Gradient wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/a61|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers c : c47cb|x_strcg.h ........... Déclaration des structures pour gnuplot]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c26a4|x_fxyz.h ............ Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c27a7|x_grad.h ........... Calculer le gradient au point p]]
* [[Mathc initiation/Fichiers h : x_70a3|kg_3dv1.h ........ Dessiner le plan sur le plan xy]]
* [[Mathc initiation/Fichiers h : x_69a3|kg_3dv2.h ........ Dessiner le plan tangent sur la fonction]]
:
.
:
Les fonctions pour les différents exemples :
* [[Mathc initiation/Fichiers h : x_68a3|f.h]]
:
.
:
Dessiner le plan sur le plan xy :
* [[Mathc initiation/Fichiers h : c27a4|c00a.c ... "2*exp(-x) * cos(y)"]]
* [[Mathc initiation/Fichiers h : c25a3|c00b.c ... "cos(x)+cos(y)"]]
* [[Mathc initiation/Fichiers h : c24a3|c00c.c ... "cos(x*y)"]]
* [[Mathc initiation/Fichiers h : c26a3|c00d.c ... "x**2 - 4*x*y"]]
* [[Mathc initiation/Fichiers h : c24a2|c00e.c ... "sqrt(x**2 + y**2)"]]
:
.
:
Dessiner le plan tangent sur la fonction :
* [[Mathc initiation/Fichiers h : c25a2|c01a.c]]
* [[Mathc initiation/c34a2|c01b.c]]
* [[Mathc initiation/Fichiers h : c77a2|c01c.c]]
* [[Mathc initiation/Fichiers h : c78a2|c01d.c]]
* [[Mathc initiation/Fichiers c : c47cc|c01e.c]]
:
.
:
{{AutoCat}}
fnnojluvl67e7i97n6tssl4xdukeyfn
Discussion Wikilivres:Le Bistro/2024
5
80494
763587
733289
2026-04-13T15:11:15Z
Uzume
11567
gitiles
763587
wikitext
text/x-wiki
Cette page est réservée aux '''informations techniques sur MediaWiki'''.
Pour discuter, ajouter un sujet ici : [[Wikilivres:Le Bistro/2024]].
----
== Do you use Wikidata in Wikimedia sibling projects? Tell us about your experiences ==
<div lang="en" dir="ltr" class="mw-content-ltr">
''Note: Apologies for cross-posting and sending in English.''
Hello, the '''[[m:WD4WMP|Wikidata for Wikimedia Projects]]''' team at Wikimedia Deutschland would like to hear about your experiences using Wikidata in the sibling projects. If you are interested in sharing your opinion and insights, please consider signing up for an interview with us in this '''[https://wikimedia.sslsurvey.de/Wikidata-for-Wikimedia-Interviews Registration form]'''.<br>
''Currently, we are only able to conduct interviews in English.''
The front page of the form has more details about what the conversation will be like, including how we would '''compensate''' you for your time.
For more information, visit our ''[[m:WD4WMP/AddIssue|project issue page]]'' where you can also share your experiences in written form, without an interview.<br>We look forward to speaking with you, [[m:User:Danny Benjafield (WMDE)|Danny Benjafield (WMDE)]] ([[m:User talk:Danny Benjafield (WMDE)|talk]]) 08:53, 5 January 2024 (UTC)
</div>
<!-- Message envoyé par User:Danny Benjafield (WMDE)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/WD4WMP/ScreenerInvite&oldid=26027495 -->
== Actualités techniques n° 2024-02 ==
<section begin="technews-2024-W02"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/02|D’autres traductions]] sont disponibles.
'''Changements récents'''
* [https://mediawiki2latex.wmflabs.org/ mediawiki2latex] est un outil qui convertit le contenu wiki en format LaTeX, PDF, ODT ou EPub. Le code s’exécute désormais bien plus rapidement grâce à de récentes améliorations. Un conteneur Docker est aussi [[b:de:Benutzer:Dirk_Hünniger/wb2pdf/install#Using_Docker|disponible à l’installation]] pour votre machine locale.
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] La façon dont les pages aléatoires sont sélectionnées a été mise à jour, réduisant un peu le problème qui réduit la probabilité de certaines pages d’apparaitre. [https://phabricator.wikimedia.org/T309477]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.13|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-01-09|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-01-10|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-01-11|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/02|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner votre avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’inscrire ou se désinscrire]].''
</div><section end="technews-2024-W02"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 9 janvier 2024 à 02:19 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26026251 -->
== Reusing references: Can we look over your shoulder? ==
''Apologies for writing in English.''
The Technical Wishes team at Wikimedia Deutschland is planning to [[m:WMDE Technical Wishes/Reusing references|make reusing references easier]]. For our research, we are looking for wiki contributors willing to show us how they are interacting with references.
* The format will be a 1-hour video call, where you would share your screen. [https://wikimedia.sslsurvey.de/User-research-into-Reusing-References-Sign-up-Form-2024/en/ More information here].
* Interviews can be conducted in English, German or Dutch.
* [[mw:WMDE_Engineering/Participate_in_UX_Activities#Compensation|Compensation is available]].
* Sessions will be held in January and February.
* [https://wikimedia.sslsurvey.de/User-research-into-Reusing-References-Sign-up-Form-2024/en/ Sign up here if you are interested.]
* Please note that we probably won’t be able to have sessions with everyone who is interested. Our UX researcher will try to create a good balance of wiki contributors, e.g. in terms of wiki experience, tech experience, editing preferences, gender, disability and more. If you’re a fit, she will reach out to you to schedule an appointment.
We’re looking forward to seeing you, [[m:User:Thereza Mengs (WMDE)| Thereza Mengs (WMDE)]]
<!-- Message envoyé par User:Thereza Mengs (WMDE)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=WMDE_Technical_Wishes/Technical_Wishes_News_list_all_village_pumps&oldid=25956752 -->
== Actualités techniques n° 2024-03 ==
<section begin="technews-2024-W03"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/03|D’autres traductions]] sont disponibles.
'''Changements récents'''
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Les pages qui utilisent le [[mw:Special:MyLanguage/Manual:ContentHandler|modèle de contenu]] JSON utiliseront désormais des tabulations au lieu d’espaces pour l’indentation automatique, ce qui réduira significativement la taille des pages. [https://phabricator.wikimedia.org/T326065]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Les [[mw:Special:MyLanguage/Extension:Gadgets|gadgets]] et scripts utilisateur personnels peuvent désormais utiliser la syntaxe JavaScript d’ES6 (aussi appelé « ES2015 ») et d’ES7 (« ES2016 »). MediaWiki doit valider le code source pour protéger les autres fonctionnalités du site contre les erreurs de syntaxe et vérifier que les scripts sont valides dans tous les [[mw:Special:MyLanguage/Compatibility#Browsers|navigateurs pris en charge]]. Auparavant, les gadgets pouvaient utiliser l’option <bdi lang="zxx" dir="ltr"><code><nowiki>requiresES6</nowiki></code></bdi>. Cette option n’est plus nécessaire et sera donc supprimée à l’avenir. [https://phabricator.wikimedia.org/T75714]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Les [[mw:Special:MyLanguage/Manual:Bot passwords|mots de passe des robots]] et les [[mw:Special:MyLanguage/OAuth/Owner-only consumers|utilisations d’OAuth pour propriétaire uniquement]] peuvent désormais être restreints pour n’autoriser la modification que de certaines pages. [https://phabricator.wikimedia.org/T349957]
* Vous pouvez désormais [[mw:Special:MyLanguage/Extension:Thanks|remercier]] les robots pour des contributions spécifiques. [https://phabricator.wikimedia.org/T341388]
* Une actualité concernant l’état de la consultation des souhaits de la communauté pour 2024 [[m:Special:MyLanguage/Community Wishlist Survey/Future Of The Wishlist/January 4, 2024 Update|a été publiée]]. Vous êtes invités à la lire et à y réagir.
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.14|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-01-16|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-01-17|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-01-18|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* À partir du 17 janvier, il ne sera plus possible de se connecter aux wikis Wikimedia depuis certaines versions spécifiques du navigateur Chrome (versions 51 à 66, sorties entre 2016 et 2018). De plus, les utilisateurs d’iOS 12 ou de Safari sur Mac OS 10.14 devront peut-être se connecter séparément à chaque wiki. [https://phabricator.wikimedia.org/T344791]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Le module <bdi lang="zxx" dir="ltr"><code>jquery.cookie</code></bdi> a été déprécié et remplacé par le module <bdi lang="zxx" dir="ltr"><code>mediawiki.cookie</code></bdi> l’an dernier. Un script a été lancé pour remplacer toutes les utilisations restantes. Les alias temporaires seront retirés cette semaine. [https://phabricator.wikimedia.org/T354966]
'''Futurs changements'''
* Wikimedia Allemagne travaille à [[m:WMDE Technical Wishes/Reusing references|rendre plus facile la réutilisation de références]]. Ils recherchent des personnes intéressées pour participer à des [https://wikimedia.sslsurvey.de/User-research-into-Reusing-References-Sign-up-Form-2024/en/ appels vidéos individuels pour une étude utilisateur en janvier et février].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/03|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner votre avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’inscrire ou se désinscrire]]''
</div><section end="technews-2024-W03"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 16 janvier 2024 à 01:12 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26074460 -->
== Vote sur la Charte du Comité de Coordination du Code de Conduite Universel ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/wiki/Universal Code of Conduct/Coordinating Committee/Charter/Announcement - voting opens|Vous trouverez ce message traduit dans d'autres langues sur Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:wiki/Universal Code of Conduct/Coordinating Committee/Charter/Announcement - voting opens}}&language=&action=page&filter= {{int:please-translate}}]''
Bonjour à toutes et à tous,
Je m'adresse à vous aujourd'hui pour vous annoncer que la période de vote pour la charte du [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Comité de Coordination du Code de Conduite Universel]] (U4C) est désormais ouverte. Les membres de la communauté peuvent [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter/Voter_information|voter et faire part de leurs commentaires sur la charte via SecurePoll]] jusqu'au '''2 février 2024'''. Celles et ceux d’entre vous qui ont exprimé leur opinion lors de l'élaboration des [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Lignes directrices pour l'application du CCU]] seront familiers avec ce processus.
La [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|version actuelle de la charte du Comité de Coordination du Code de Conduite Universel]] est disponible sur Meta-Wiki avec plusieurs traductions.
Veuillez lire la charte, procédez au vote et relayez cette annonce auprès des membres de votre communauté. Je vous affirme que le Comité de Construction de l'U4C se réjouit de votre participation.
Au nom de l’équipe du projet de CCU,<section end="announcement-content" />
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 19 janvier 2024 à 19:07 (CET)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=25853527 -->
== Actualités techniques n° 2024-04 ==
<section begin="technews-2024-W04"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/04|D’autres traductions]] sont disponibles.
'''Problèmes'''
* À cause d’un beugue dans l’assistant au téléversement, le nom utilisateur de la personne téléversant un fichier n’était plus un lien vers sa page utilisateur. Cela a été corrigé. [https://phabricator.wikimedia.org/T354529]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.15|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-01-23|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-01-24|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-01-25|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/04|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner votre avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’inscrire ou se désinscrire]].''
</div><section end="technews-2024-W04"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 23 janvier 2024 à 02:03 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26096197 -->
== Actualités techniques n° 2024-05 ==
<section begin="technews-2024-W05"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/05|D’autres traductions]] sont disponibles.
'''Changements récents'''
* À partir de lundi 29 janvier, tous les horodatages des messages dans les pages de discussion deviendront des liens. Chaque lien est un permalien vers le message. Il permet aux utilisateurs de retrouver le message qu’ils recherchent, même si celui-ci a été déplacé ailleurs. Cela va concerner tous les wikis, sauf la Wikipédia en anglais. Vous pouvez en savoir plus sur ce changement [https://diff.wikimedia.org/2024/01/29/talk-page-permalinks-dont-lose-your-threads/ sur Diff] ou [[mw:Special:MyLanguage/Help:DiscussionTools#Talk_pages_permalinking|sur MediaWiki.org]].<!-- The Diff post will be published on Monday morning UTC--> [https://phabricator.wikimedia.org/T302011]
* Quelques améliorations ont eu lieu sur le Captcha pour le rendre plus efficace contre les robots polluposteurs et les scripts. Vos commentaires peuvent être publiés sur [[phab:T141490|la tâche de développement]]. L’équipe va surveiller les statistiques liées au Captcha et celles secondaires telles que la création de comptes et le nombre de modifications.
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.16|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-01-30|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-01-31|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-02-01|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* [[File:Octicons-gift.svg|12px|link=|alt=|Concerne un souhait]] Le 1<sup>er</sup> février, un lien sera jouter au menu « Outils » pour télécharger un [[w:fr:code QR|code QR]] qui renverra vers la page que vous regardez. Une nouvelle page [[{{#special:QrCode}}]] permettra par ailleurs de créer des codes QR pour toute URL Wikimedia. Cela répond au [[m:Community Wishlist Survey 2023/Mobile and apps/Add ability to share QR code for a page in any Wikimedia project|19<sup>e</sup> souhait]] de la [[m:Community Wishlist Survey 2023/Results|consultation 2023 des souhaits de la communauté]]. [https://phabricator.wikimedia.org/T329973]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Les [[mw:Special:MyLanguage/Extension:Gadgets|gadgets]] qui ne fonctionnent que sur certains habillages utilisent parfois l’option <bdi lang="zxx" dir="ltr"><code>targets</code></bdi> pour limiter les pages où vous pouvez les utiliser. Cela va cesser de fonctionner cette semaine. Vous devez sans doute utiliser l’option <bdi lang="zxx" dir="ltr"><code>skins</code></bdi> à la place. [https://phabricator.wikimedia.org/T328497]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/05|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner votre avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’inscrire ou se désinscrire]].''
</div><section end="technews-2024-W05"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 29 janvier 2024 à 20:31 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26137870 -->
== Derniers jours pour voter concernant la Charte du Comité de Coordination du Code de Conduite Universel ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/wiki/Universal Code of Conduct/Coordinating Committee/Charter/Announcement - voting reminder|Vous trouverez ce message traduit dans d'autres langues sur Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:wiki/Universal Code of Conduct/Coordinating Committee/Charter/Announcement - voting reminder}}&language=&action=page&filter= {{int:please-translate}}]''
Bonjour à toutes et à tous,
Je m'adresse à vous aujourd'hui pour vous rappeler que la période de vote pour la charte du [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Comité de coordination du Code de conduite universel]] (U4C) se terminera le '''2 février 2024'''. Les membres de la communauté peuvent [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter/Voter_information|voter et faire part de leurs commentaires sur la charte via SecurePoll]].. Ceux d'entre vous qui ont exprimé leur opinion lors de l'élaboration des [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Lignes directrices pour l'application du CdCU]] seront familiers avec ce processus.
La [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|version actuelle de la charte de l’U4C]] est disponible sur Meta-Wiki avec plusieurs traductions.
Lisez la charte, votez et relayez cette annonce auprès des membres de votre communauté. Je vous affirme que le Comité de Construction de l'U4C se réjouit de votre participation.
Au nom de l’équipe du projet de CCU,<section end="announcement-content" />
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 31 janvier 2024 à 17:59 (CET)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=25853527 -->
== Actualités techniques n° 2024-06 ==
<section begin="technews-2024-W06"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|nouvelles techniques]]''' de la communauté technique de Wikimedia. Veuillez informer les autres utilisateurs de ces changements. Tous les changements ne vous concerneront pas. Des [[m:Special:MyLanguage/Tech/News/2024/06|traductions]] sont disponibles.
'''Changements récents'''
*Les pages d'historique du site mobile utilisent désormais le même HTML que les pages d'historique du bureau. Si vous rencontrez des problèmes liés à l'utilisation de l'historique sur mobile, veuillez les signaler sur [[phab:T353388|Phabricator]].
*Sur la plupart des wikis, les administrateurs peuvent désormais bloquer les utilisateurs pour des actions spécifiques. Ces actions sont : téléverser des fichiers, créer de nouvelles pages, déplacer (renommer) des pages et envoyer des remerciements. Le but de cette fonctionnalité est de permettre aux administrateurs d'appliquer des blocages adaptés à l'activité des utilisateurs bloqués. [[m:Special:MyLanguage/Community health initiative/Partial blocks#action-blocks|En savoir plus sur les "blocages d'actions"]] [https://phabricator.wikimedia.org/T242541][https://phabricator.wikimedia.org/T280531]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.17|nouvelle version]] de MediaWiki sera disponible sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-02-06|fr}}. Elle sera disponible sur les wikis non-Wikipédia et sur certaines Wikipédias à partir du {{#time:j xg|2024-02-07|fr}}. Elle sera disponible sur tous les wikis à partir du {{#time:j xg|2024-02-08|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]) [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* Les liens permanents des pages de discussion qui incluaient des diacritiques et des scripts non latins ne fonctionnaient pas correctement. Ce problème est corrigé. [https://phabricator.wikimedia.org/T356199]
'''Changements à venir'''
* [[m:WMDE Technical Wishes/ReferencePreviews#24WPs|24 Wikipédias]] avec [[mw:Special:MyLanguage/Reference_Tooltips|Info-bulles de référence]] en tant que gadget par défaut sont encouragées à supprimer ce marqueur par défaut. Cela ferait des [[mw:Special:MyLanguage/Help:Reference_Previews|aperçus de références]] le nouveau paramètre par défaut pour les popups de références, offrant ainsi une expérience plus cohérente à travers les wikis. Pour [[m:WMDE Technical Wishes/ReferencePreviews#46WPs|46 Wikipedias]] avec moins de 4 administrateurs d'interface, le changement est déjà prévu pour mi-février, [[m:Talk:WMDE Technical Wishes/ReferencePreviews#Reference Previews to become the default for previewing references on more wikis.|sauf en cas de soucis]]. L'ancien gadget Info-bulles de référence restera toujours utilisable et remplacera cette fonctionnalité, s'il est disponible sur votre wiki et que vous l'avez activé dans vos paramètres. [https://meta.wikimedia.org/wiki/WMDE_Technical_Wishes/ReferencePreviews#Reference_Previews_to_become_the_default_for_previewing_references_on_more_wikis][https://phabricator.wikimedia.org/T355312]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et publiées par des [[m:Special:MyLanguage/User:MediaWiki message delivery|robots]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/06|Traduire]] • [[m:Tech|Obtenir de l'aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S'abonner ou se désabonner]].''
</div><section end="technews-2024-W06"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 5 février 2024 à 20:22 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26180971 -->
== Annonce des résultats du vote de ratification de la charte du comité de coordination du CdCU ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/wiki/Universal Code of Conduct/Coordinating Committee/Charter/Announcement - results|Ce message est également traduit dans d'autres langues sur Méta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:wiki/Universal Code of Conduct/Coordinating Committee/Charter/Announcement - results}}&language=&action=page&filter= {{int:please-translate}}]''
Chers tous,
Merci à tous d'avoir suivi les progrès du Code de conduite universel. Je vous écris aujourd'hui pour vous annoncer le résultat du [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter/Voter_information|vote de ratification]] sur la [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|Charte du Comité de coordination du Code de conduite universel]]. 1746 personnes ont voté lors de ce vote de ratification, 1249 ayant soutenu la charte et 420 ne l'ayant pas soutenue. Le processus de vote de ratification a permis aux personnes ayant voté de fournir des commentaires sur la charte.
Un rapport sur les statistiques de vote et un résumé des commentaires des votants seront publiés sur Meta-wiki dans les semaines à venir.
Vous recevrez prochainement des informations sur les prochaines étapes.
Au nom de l’équipe du projet de CdCU,<section end="announcement-content" />
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 12 février 2024 à 19:23 (CET)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26160150 -->
== Actualités techniques n° 2024-07 ==
<section begin="technews-2024-W07"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/07|D’autres traductions]] sont disponibles.
'''Changements récents'''
* L’[[d:Wikidata:SPARQL query service/WDQS graph split|expérimentation de scission des graphes du service de requêtes de Wikidata (WDQS)]] fonctionne sur 3 serveurs de test. L’équipe teste l’impact de cette scission et a besoin des retours d’utilisateurs du WDQS à travers l’interface utilisateur ou par programme dans différents canaux<sup>[https://www.wikidata.org/wiki/Wikidata_talk:SPARQL_query_service/WDQS_graph_split][https://phabricator.wikimedia.org/T356773][https://www.wikidata.org/wiki/User:Sannita_(WMF)]</sup>. Les retours des utilisateurs valideront l’impact de divers cas d’usage et flux de travaux autour du WDQS. [https://www.wikidata.org/wiki/Wikidata:SPARQL_query_service/WDQS_backend_update/October_2023_scaling_update][https://www.mediawiki.org/wiki/Wikidata_Query_Service/User_Manual#Federation]
'''Problèmes'''
*[[phab:T356928|Un beugue]] a modifié l’apparence des liens visités sur mobile : ils apparaissaient en noir, mais cela est maintenant corrigé.
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.18|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-02-13|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-02-14|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-02-15|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Dans le cadre de l’extinction progressive du moteur de grille (Grid Engine)<sup>[https://wikitech.wikimedia.org/wiki/News/Toolforge_Grid_Engine_deprecation]</sup>, les outils sur la grille seront arrêtés à partir du 14 février 2024. Si vous avez des outils qui sont en train d’être migrés, vous pouvez demander un délai supplémentaire afin qu'ils ne soient pas arrêtés. [https://wikitech.wikimedia.org/wiki/Portal:Toolforge/About_Toolforge#Communication_and_support]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/07|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W07"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 13 février 2024 à 06:48 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26223994 -->
== Actualités techniques n° 2024-08 ==
<section begin="technews-2024-W08"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/08|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Si vous avez l’option « {{int:Tog-enotifwatchlistpages}} » activée, les modifications des robots ne déclencheront plus l’envoi de notifications par courriel. Auparavant, seules les modifications mineures ne déclenchaient pas l’envoi de courriel de notification. [https://phabricator.wikimedia.org/T356984]
* Des changements dans la manières dont les scripts des sites et ceux utilisateur se chargent dans [[mw:Special:MyLanguage/Skin:Vector/2022|Vector 2022]] sur certains wikis ont eu lieu. Ces changements concernent tous les wikis ayant l’[[mw:Special:MyLanguage/Skin:Vector|ancien Vector]] comme habillage par défaut, les Wikivoyage et les Wikibooks. Les autres wikis seront aussi concernés dans les trois prochains mois. Les gadgets ne sont pas impactés. Si cela vous impacte ou que vous souhaitez réduire les conséquences sur votre projet, voyez [[Phab:T357580|ce ticket]]. N’hésitez pas à vous coordonner et à prendre des initiatives.
*Les comptes nouvellement créés automatiquement (les comptes qui se créent lorsque vous visitez un wiki pour la première fois) ont désormais les mêmes préférences de notification que les utilisateurs qui viennent de s’inscrire sur ce wiki, pour quatre types de notification listés dans [[phab:T353225|la description de la tâche]].
*La taille maximum des fichiers lors de l’utilisation de [[c:Special:MyLanguage/Commons:Upload_Wizard|l’assistant de téléversement]] est désormais de 5 Gio. [https://phabricator.wikimedia.org/T191804]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.19|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-02-20|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-02-21|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-02-22|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Les outils actifs sur le moteur de grille ont été [[wikitech:News/Toolforge_Grid_Engine_deprecation|arrêtés]], puisque nous préparons l’arrêt de la grille le 14 mars 2024. Le code des outils et les données n’ont pas été supprimés. Si vous maintenez un outil et souhaitez réactiver celui-ci, contactez [[wikitech:Portal:Toolforge/About_Toolforge#Communication_and_support|l’équipe]]. Seuls les outils qui ont sollicité un délai supplémentaires s’exécutent encore sur la grille.
* La propriété CSS <bdi lang="zxx" dir="ltr"><code>[https://developer.mozilla.org/en-US/docs/Web/CSS/filter filter]</code></bdi> peut désormais être utilisée dans l’attribut HTML <bdi lang="zxx" dir="ltr"><code>style</code></bdi> en wikicode. [https://phabricator.wikimedia.org/T308160]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/08|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W08"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 19 février 2024 à 16:36 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26254282 -->
== Actualités techniques n° 2024-09 ==
<section begin="technews-2024-W09"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/09|D’autres traductions]] sont disponibles.
'''Changements récents'''
* L’[[mw:Special:MyLanguage/VisualEditor_on_mobile|éditeur visuel sur mobile]] est maintenant l’éditeur par défaut pour les utilisateurs qui n’ont jamais contribué auparavant, sur un petit groupe de wikis. [[mw:Special:MyLanguage/VisualEditor_on_mobile/VE_mobile_default#A/B_test_results|Des études]] montrent que les utilisateurs qui utilisent cet éditeur réussissent un peu mieux à publier les modifications qu’ils ont commencées, et un peu moins à publier des modifications qui ne sont pas annulées ensuite. Les utilisateurs qui ont défini l’éditeur de wikicode comme leur éditeur de wikicode par défaut sur ordinateur obtiendront également l’éditeur de wikicode sur mobile pour leur première modification sur mobile. [https://phabricator.wikimedia.org/T352127]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] La valeur <code>wgGlobalGroups</code> de [[mw:Special:MyLanguage/ResourceLoader/Core modules#mw.config|mw.config]] contient désormais uniquement des groupes actifs du wiki. Les scripts n’ont plus besoin de vérifier si le groupe est actif sur le wiki à l’aide d’une requête auprès de l’API. Voici un exemple de code illustratif : <bdi lang="zxx" dir="ltr"><code>if (/globalgroupname/.test(mw.config.get("wgGlobalGroups")))</code></bdi>. [https://phabricator.wikimedia.org/T356008]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.20|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-02-27|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-02-28|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-02-29|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
'''Futurs changements'''
* Le droit de modifier les [[mw:Special:MyLanguage/Manual:Tags|balises des révisions]] (<bdi lang="zxx" dir="ltr"><code>changetags</code></bdi>) sera retiré aux utilisateurs des sites Wikimedia, à l’exception des admins et robots. Votre communauté peut demander à conserver l’ancienne configuration sur votre wiki avant que ce changement ne se produise. Merci d’indiquer dans [[phab:T355639|ce ticket]] votre demande de conservation pour votre communauté avant la fin du mois de mars 2024.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/09|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W09"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 26 février 2024 à 20:23 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26294125 -->
== Actualités techniques n° 2024-10 ==
<section begin="technews-2024-W10"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/10|D’autres traductions]] sont disponibles.
'''Changements récents'''
* La page <bdi lang="zxx" dir="ltr"><code>Special:Book</code></bdi> (ainsi que la fonctionnalité « Créer un livre » associée) fournie par l’ancienne [[mw:Special:MyLanguage/Extension:Collection|extension Collection]] a été supprimée de tous les wikis Wikisource, car elle était cassée. Cela n’affecte pas la possibilité de télécharger des livres normaux, qui est fournie par l’[[mw:Special:MyLanguage/Extension:Wikisource|extension Wikisource]]. [https://phabricator.wikimedia.org/T358437]
* [[m:Wikitech|Wikitech]] utilise désormais par défaut l’analyseur syntaxique de wikicode de nouvelle génération [[mw:Special:MyLanguage/Parsoid|Parsoid]] pour générer toutes les pages de l’espace de noms des discussions (''Talk:''). Signalez tout problème sur la [[mw:Talk:Parsoid/Parser_Unification/Known_Issues|page de discussion des problèmes connus]]. Vous pouvez utiliser l’extension [[mw:Special:MyLanguage/Extension:ParserMigration|ParserMigration]] pour contrôler l’utilisation de Parsoid ; voir la [[mw:Special:MyLanguage/Help:Extension:ParserMigration|documentation d’aide de ParserMigration]] pour plus de détails.
* La maintenance d’[https://etherpad.wikimedia.org Etherpad] est terminée. Si vous rencontrez des problèmes, veuillez l’indiquer dans [[phab:T316421|ce billet]].
* [[File:Octicons-tools.svg|12px|link=|alt=| Sujet technique]] Les [[mw:Special:MyLanguage/Extension:Gadgets|gadgets]] permettent aux administrateurs d’interface de créer des fonctionnalités personnalisées avec CSS et JavaScript. Les espaces de noms <bdi lang="zxx" dir="ltr"><code>Gadget</code></bdi> et <bdi lang="zxx" dir="ltr"><code>Gadget_definition</code></bdi> et le droit utilisateur <bdi lang="zxx" dir="ltr"><code>gadgets-definition-edit</code></bdi> avaient été réservés pour expérimentation en 2015, mais n’ont jamais été utilisés. Ceux-ci étaient visibles sur Spécial:Rechercher et Spécial:Liste_des_droits_de_groupe. Ces espaces de noms et droits utilisateur non-utilisés sont désormais supprimés. Aucune page n’est déplacée et aucune modification n’est nécessaire. [https://phabricator.wikimedia.org/T31272]
* Une amélioration a été apportée pour faciliter l’usage de la fonctionnalité « Ajouter une citation » sur Wikipédia : le bouton d’insertion a été déplacé vers le haut de la boite surgissante. [https://phabricator.wikimedia.org/T354847]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.21|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-03-05|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-03-06|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-03-07|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
'''Futurs changements'''
* Tous les wikis seront uniquement lisibles pendant quelques minutes le 20 mars, 14 h UTC. Plus d'informations seront publiées dans les ''Actualités techniques'', mais aussi individuellement sur chaque wiki dans les prochaines semaines. [https://phabricator.wikimedia.org/T358233]
* Le code HTML des titres de sections et liens de modifications de celles-ci va changer cette année pour en améliorer l’accessibilité. Consultez les [[mw:Special:MyLanguage/Heading_HTML_changes|changements du HTML des titres]] pour plus de détails. Le nouveau balisage sera identique à celui produit par l’analyseur de wikicode Parsoid. Vous pouvez tester votre gadget ou votre feuille de style avec le nouveau balisage en ajoutant <bdi lang="zxx" dir="ltr"><code>?useparsoid=1</code></bdi> à l’URL ([[mw:Special:MyLanguage/Help:Extension:ParserMigration#Selecting_a_parser_using_a_URL_query_string|plus d’infos]]), ou activez Parsoid en lecture dans vos options utilisateur ([[mw:Special:MyLanguage/Help:Extension:ParserMigration#Enabling_via_user_preference|plus d’infos]]).
*
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/10|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W10"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 4 mars 2024 à 20:47 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26329807 -->
== Le rapport de la ratification de la Charte U4C et l'appel à candidatures U4C sont désormais disponibles ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024/Announcement – call for candidates| Ce message est également traduit dans d'autres langues sur Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Universal Code of Conduct/Coordinating Committee/Election/2024/Announcement – call for candidates}}&language=&action=page&filter= {{int:please-translate}}]''
Bonjour,
Je vous écris aujourd'hui pour vous communiquer deux informations importantes. Premièrement, le [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter/Vote results| rapport des commentaires du Comité de coordination du Code de conduite universel (U4C) sur la ratification de la Charte]] est maintenant disponible. Deuxièmement, l'appel à candidatures pour l'U4C est désormais ouvert jusqu'au 1er avril 2024.
Le [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Comité de coordination du Code de Conduite Universel]] (U4C) est un groupe mondial qui se consacre à la mise en œuvre équitable et cohérente du Code de Conduite Universel. Les membres de la communauté sont invités à soumettre leur candidature à l'U4C. Pour plus d'informations et pour connaître les responsabilités de l'U4C, veuillez [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|voir la charte de l'U4C]].
La charte prévoit 16 sièges pour l'U4C : huit sièges réservés à la communauté et huit autres aux régions pour garantir que l'U4C soit représentatif de la diversité du mouvement.
Pour en savoir plus et soumettre votre candidature, cliquez sur [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024|Meta-wiki]].
Au nom de l'équipe du projet UCoC,<section end="announcement-content" />
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 5 mars 2024 à 17:24 (CET)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26276337 -->
== <span lang="en" dir="ltr" class="mw-content-ltr">Tech News: 2024-11</span> ==
<div lang="en" dir="ltr" class="mw-content-ltr">
<section begin="technews-2024-W11"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2024/11|Translations]] are available.
'''Changes later this week'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Recurrent item]] The [[mw:MediaWiki 1.42/wmf.22|new version]] of MediaWiki will be on test wikis and MediaWiki.org from {{#time:j xg|2024-03-12|en}}. It will be on non-Wikipedia wikis and some Wikipedias from {{#time:j xg|2024-03-13|en}}. It will be on all wikis from {{#time:j xg|2024-03-14|en}} ([[mw:MediaWiki 1.42/Roadmap|calendar]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* After consulting with various communities, the line height of the text on the [[mw:Special:MyLanguage/Skin:Minerva Neue|Minerva skin]] will be increased to its previous value of 1.65. Different options for typography can also be set using the options in the menu, as needed. [https://phabricator.wikimedia.org/T358498]
*The active link color in [[mw:Special:MyLanguage/Skin:Minerva Neue|Minerva]] will be changed to provide more consistency with our other platforms and best practices. [https://phabricator.wikimedia.org/T358516]
* [[c:Special:MyLanguage/Commons:Structured data|Structured data on Commons]] will no longer ask whether you want to leave the page without saving. This will prevent the “information you’ve entered may not be saved” popups from appearing when no information have been entered. It will also make file pages on Commons load faster in certain cases. However, the popups will be hidden even if information has indeed been entered. If you accidentally close the page before saving the structured data you entered, that data will be lost. [https://phabricator.wikimedia.org/T312315]
'''Future changes'''
* All wikis will be read-only for a few minutes on March 20. This is planned at 14:00 UTC. More information will be published in Tech News and will also be posted on individual wikis in the coming weeks. [https://phabricator.wikimedia.org/T358233][https://meta.wikimedia.org/wiki/Special:MyLanguage/Tech/Server_switch]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2024/11|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2024-W11"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 12 mars 2024 à 00:04 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26374013 -->
== Sélection 2024 du conseil d'administration de la Fondation Wikimédia ==
<section begin="announcement-content" />
: ''[[m:Special:MyLanguage/Wikimedia Foundation elections/2024/Announcement/Selection announcement| Vous trouverez ce message traduit dans d'autres langues sur Meta-wiki.]]''
: ''<div class="plainlinks">[[m:Special:MyLanguage/Wikimedia Foundation elections/2024/Announcement/Selection announcement|{{int:interlanguage-link-mul}}]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Wikimedia Foundation elections/2024/Announcement/Selection announcement}}&language=&action=page&filter= {{int:please-translate}}]</div>''
Chers tous et toutes,
Cette année, le mandat de 4 (quatre) membres du conseil d'administration de la Fondation Wikimédia, sélectionnés par la communauté et les affiliés, arrivera à son terme [1]. Le conseil d'administration invite l'ensemble du mouvement à participer au processus de sélection de cette année et à voter pour pourvoir ces sièges.
Le [[m:Special:MyLanguage/Wikimedia Foundation elections committee|Comité des élections]] supervisera ce processus avec le soutien du personnel de la Fondation [2]. La Commission de gouvernance du Conseil a créé un groupe de travail sur la sélection des membres du Conseil, composé de Dariusz Jemielniak, Nataliia Tymkiv, Esra'a Al Shafei, Kathy Collins et Shani Evenstein Sigalov, qui ne peuvent candidater au processus de sélection des membres du Conseil sélectionnés par la communauté et les affiliés pour l'année 2024 [3]. Ce groupe est chargé de superviser le processus de sélection des membres du conseil d'administration pour 2024 et de tenir le conseil d'administration informé. Pour plus de détails sur les rôles du comité des élections, du conseil d'administration et du personnel, cliquez ici [4].
Voici les dates clés prévues :
* Mai 2024 : Appel à candidatures et appel à questions
* Juin 2024 : Les affiliés votent pour présélectionner 12 candidatures (pas de présélection si 15 candidatures ou moins se présentent) [5].
* Juin-août 2024 : Période de la campagne
* Fin août / début septembre 2024 : Période de vote communautaire de deux semaines
* Octobre-novembre 2024 : Examen des antécédents des candidates et candidats sélectionnés
* Réunion du conseil d'administration en décembre 2024 : L'entrée en fonction du nouveau conseil d'administration
Pour en savoir plus sur le processus de sélection de 2024 - y compris le calendrier détaillé, le processus de candidature, les règles de la campagne et les critères d'éligibilité des personnes qui votent -, consultez [[m:Special:MyLanguage/Wikimedia Foundation elections/2024|cette page Meta-wiki]], et préparez votre plan.
'''Bénévoles pour les élections'''
Une autre façon de participer au processus de sélection de 2024 est de devenir bénévole pour les élections. Les bénévoles pour les élections sont un pont entre le comité des élections et leur communauté respective. Ils veillent à ce que leur communauté soit représentée en la mobilisant pour qu'elle aille voter. Pour en savoir plus sur le programme et les modalités d'adhésion, consultez cette [[m:Special:MyLanguage/Wikimedia Foundation elections/2024/Election Volunteers|Meta-wiki page]].
Meilleures salutations,
[[m:Special:MyLanguage/User:Pundit|Dariusz Jemielniak]] (président du comité de gouvernance, groupe de travail sur la sélection du conseil d'administration)
[1] https://meta.wikimedia.org/wiki/Special:MyLanguage/Wikimedia_Foundation_elections/2021/Results#Elected
[2] https://foundation.wikimedia.org/wiki/Committee:Elections_Committee_Charter
[3] https://foundation.wikimedia.org/wiki/Minutes:2023-08-15#Governance_Committee
[4] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections_committee/Roles
[5] Même si le seuil idéal est de 12 candidates et candidats pour 4 sièges à pourvoir, la procédure de présélection sera déclenchée s'il y a plus de 15 candidatures, car les 1 à 3 personnes éliminées pourraient se sentir exclues et cela représenterait beaucoup de travail pour les affiliés de mener à bien la procédure de présélection pour n'éliminer que 1 à 3 personnes de la liste des candidatures.<section end="announcement-content" />
[[User:MPossoupe_(WMF)|MPossoupe_(WMF)]]12 mars 2024 à 20:56 (CET)
<!-- Message envoyé par User:MPossoupe (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26349432 -->
== Votre wiki sera bientôt en lecture seule ==
<section begin="server-switch"/><div class="plainlinks">
[[:m:Special:MyLanguage/Tech/Server switch|Lire ce message dans une autre langue]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-Tech%2FServer+switch&language=&action=page&filter= {{int:please-translate}}]
La [[foundation:|Fondation Wikimedia]] va basculer le trafic entre ses centres de données. Cela permettra de s’assurer que Wikipédia et les autres wikis de Wikimedia peuvent rester en ligne même après une catastrophe.
Le trafic sera basculé le '''{{#time:j xg|2024-03-20|fr}}'''. Le test débutera à '''[https://zonestamp.toolforge.org/{{#time:U|2024-03-20T14:00|en}} {{#time:H:i e|2024-03-20T14:00}}]''' (10:00 au Québec, 15:00 en heure d’Afrique de l'Ouest, 16:00 en heure d’Afrique centrale et 15:00 en Europe continentale).
Malheureusement, en raison de certaines limites de [[mw:Special:MyLanguage/Manual:What is MediaWiki?|MediaWiki]], toutes les modifications de pages devront être arrêtées durant le passage d’un centre de données à l’autre. Nous nous excusons pour ce dérangement et nous travaillons à le minimiser dans le futur.
'''Pendant une courte période, vous pourrez lire les wikis mais pas les modifier.'''
*Vous ne pourrez pas effectuer de modification pendant une durée pouvant aller jusqu’à une heure, le {{#time:l j xg Y|2024-03-20|fr}}.
*Si vous essayez de faire une modification ou de sauvegarder pendant cette période, vous verrez un message d’erreur. Nous espérons qu’aucune modification ne sera perdue durant ce temps, mais nous ne pouvons le garantir. Si vous voyez un message d’erreur, merci de patienter jusqu’au retour à la normale. Vous pourrez alors enregistrer votre modification. Mais nous vous conseillons de faire une copie de votre modification avant, au cas où.
''Autres conséquences :''
*Les tâches de fond seront ralenties et certaines pourraient être stoppées. Les liens rouges ne seront pas mis à jour aussi vite que d’habitude. Si vous créez un article qui est déjà lié depuis une autre page, le lien rouge pourrait rester rouge plus longtemps que d’habitude. Certains scripts ayant un long temps d’exécution devront être stoppés.
* Nous espérons que le déploiement de code se passe comme chaque semaine. Cependant, certains codes particuliers pourraient être gelés si l’opération le nécessitait.
* [[mw:Special:MyLanguage/GitLab|GitLab]] sera indisponible durant environ 90 minutes.
Ce projet pourra être reporté si nécessaire. Vous pouvez [[wikitech:Switch_Datacenter|consulter le calendrier sur wikitech.wikimedia.org]]. Tout changement sera annoncé dans le calendrier. Il y aura d’autres annonces à propos de cet événement. Une bannière sera affichée sur tous les wikis 30 minutes avant le début de l’opération. '''Merci de partager ces informations avec votre communauté.'''</div><section end="server-switch"/>
[[user:Trizek (WMF)|Trizek (WMF)]], 15 mars 2024 à 01:00 (CET)
<!-- Message envoyé par User:Trizek (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=25636619 -->
== Actualités techniques n° 2024-12 ==
<section begin="technews-2024-W12"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/12|D’autres traductions]] sont disponibles.
'''Changements récents'''
* L’indication « Les liens vers d’autres langues sont en haut de page » qui apparaissait dans le menu principal l’[[mw:Special:MyLanguage/Skin:Vector/2022|habillage Vector 2022]] a été retirée, maintenant que tous les visiteurs connaissent le nouvel emplacement du sélecteur de langue. [https://phabricator.wikimedia.org/T353619]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] La [[m:Special:MyLanguage/IP_Editing:_Privacy_Enhancement_and_Abuse_Mitigation/IP_Info_feature|fonctionnalité IP info]] affiche désormais des données de Spur, une base de données d’adresses IP. Auparavant, cette fonctionnalité ne se basait que sur la source MaxMind. IP info est donc désormais plus complet pour la patrouille. [https://phabricator.wikimedia.org/T341395]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Le service de moteur de grille de Toolforge a été éteint après les dernières migrations depuis le moteur de grille vers Kubernetes. [https://wikitech.wikimedia.org/wiki/Obsolete:Toolforge/Grid][https://wikitech.wikimedia.org/wiki/News/Toolforge_Grid_Engine_deprecation][https://techblog.wikimedia.org/2022/03/14/toolforge-and-grid-engine/]
* Les communautés peuvent désormais personnaliser les motifs par défaut pour restaurer une page, en créant [[MediaWiki:Undelete-comment-dropdown]]. [https://phabricator.wikimedia.org/T326746]
'''Problèmes'''
* [[m:Special:MyLanguage/WMDE_Technical_Wishes/RevisionSlider|RevisionSlider]] est une interface de navigation interactive dans l’historique d’une page. Des utilisateurs de langues [[mw:Special:MyLanguage/Extension:RevisionSlider/Developing_a_RTL-accessible_feature_in_MediaWiki_-_what_we%27ve_learned_while_creating_the_RevisionSlider|écrites de droite à gauche]] avaient signalé que la glissière réagissait mal aux clics de souris. Cela devrait être corrigé désormais. [https://phabricator.wikimedia.org/T352169]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.23|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-03-19|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-03-20|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-03-21|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* Tous les wikis seront en lecture seule pendant quelques minutes le 20 mars, à partir de [https://zonestamp.toolforge.org/1710943200 14 h UTC]. [https://phabricator.wikimedia.org/T358233][https://meta.wikimedia.org/wiki/Special:MyLanguage/Tech/Server_switch]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/12|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W12"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 18 mars 2024 à 18:39 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26410165 -->
== Actualités techniques n° 2024-13 ==
<section begin="technews-2024-W13"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|nouvelles techniques]]''' de la communauté technique de Wikimedia. Veuillez informer les autres utilisateurs de ces changements. Tous les changements ne vous affecteront pas. Des [[m:Special:MyLanguage/Tech/News/2024/13|traductions]] sont disponibles.
'''Modifications récentes'''
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Une mise à jour a été effectuée le 18 mars 2024 pour savoir comment différents projets chargent le site, les scripts JavaScript et le CSS utilisateur dans [[mw:Special:MyLanguage/Skin:Vector/2022|le thème Vector 2022]]. Une [[phab:T360384|liste de suivi]] est fournie pour que les administrateurs de site la suivent.
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.24|nouvelle version]] de MediaWiki sera sur les wikis de test et MediaWiki.org à partir du {{#time:j xg|2024-03-26|fr}}. Elle sera sur les wikis non-Wikipédia et sur certains Wikipédias à partir du {{#time:j xg|2024-03-27|fr}}. Elle sera sur tous les wikis à partir du {{#time:j xg|2024-03-28|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
'''''[[m:Special:MyLanguage/Tech/News|Actualités technologiques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|Rédacteurs des Actualités Technologiques]] et publiées par [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/13|Traduire]] • [[m:Tech|Obtenir de l'aide]] • [[m:Talk:Tech/News|Donner un retour]] • [[m:Global message delivery/Targets/Tech ambassadors|S'abonner ou se désabonner]].''
</div><section end="technews-2024-W13"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 25 mars 2024 à 19:56 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26446209 -->
== Actualités techniques n° 2024-14 ==
<section begin="technews-2024-W14"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/14|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Les utilisateurs de la fonctionnalité bêta [[mw:Special:MyLanguage/Reading/Web/Accessibility_for_reading|accessibilité de la lecture]] vont voir la taille de ligne par défaut changer pour les choix de textes normal ou large. [https://phabricator.wikimedia.org/T359030]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.25|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-04-02|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-04-03|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-04-04|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
'''Futurs changements'''
* Wikimedia Foundation s’organise autour d’un projet annuel qui détermine ce sur quoi l’organisation va travailler. Vous pouvez dès à présent lire [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2024-2025/Product & Technology OKRs#Draft Key Results|les premiers principaux résultats]] pour le département Produit et Technologie. Ce sont des suggestions de ce que l’organisation veut comme résultat pour les gros changements techniques entre juillet 2024 et juin 2025. Vous pouvez [[m:Talk:Wikimedia Foundation Annual Plan/2024-2025/Product & Technology OKRs|commenter ce sujet sur la page de discussion]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/14|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W14"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 2 avril 2024 à 05:35 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26462933 -->
== Actualités techniques n° 2024-15 ==
<section begin="technews-2024-W15"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique Wikimédia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/15|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Les navigateurs web peuvent utiliser des outils appelés « [[:w:fr:extension de navigateur|extensions]] ». Une nouvelle extension a été créé pour Chrome : [[m:Future Audiences/Experiment:Citation Needed|Citation Needed]] (« référence nécessaire ») ; elle permet de voir si une affirmation sur le web est traitée dans un article Wikipédia. Il s’agit d’une petite expérimentation pour voir si Wikipédia peut être utilisé de cette manière. En raison du caractère expérimental, l’outil n’est disponible que pour Chrome en anglais.
* [[File:Octicons-gift.svg|12px|link=|alt=|Concerne un souhait]] <span class="mw-translate-fuzzy">Une nouvelle fonctionnalité [[mw:Special:MyLanguage/Help:Edit Recovery|Récupération des modifications]] a été ajoutées sur tous les wikis, disponible dans les [[Special:Preferences#mw-prefsection-editing|préférences utilisateur]]. Une fois activée, vos modifications en cours sont enregistrées dans votre navigateur web : si vous fermez accidentellement un onglet de modification ou le navigateur, ou subissez un plantage de l’ordinateur, il vous sera proposé de récupéré le texte non publié. Vos commentaires sont les bienvenus sur la [[m:Special:MyLanguage/Talk:Community Wishlist Survey 2023/Edit-recovery feature|page de discussion du projet]]. Il s’agissait du 8<sup>e</sup> souhait de la consultation 2023 de la communauté.</span>
* Les premiers résultats de l’expérimentation de [[mw:Special:MyLanguage/Edit check|vérification des modifications]] ont [[mw:Special:MyLanguage/Edit_check#4_April_2024|été publiés]]. La vérification des modifications est désormais activée par défaut sur [[phab:T342930#9538364|les wikis qui l’ont testé]]. [[mw:Talk:Edit check|Faites-nous savoir]] si vous voulez que votre wiki fasse partie du prochain déploiement de la vérification des modifications. [https://phabricator.wikimedia.org/T342930][https://phabricator.wikimedia.org/T361727]
* Le lectorat qui utilise [[mw:Special:MyLanguage/Skin:Minerva Neue|l’habillage Minerva]] sur mobile remarqueront une amélioration de la hauteur de ligne pour les divers paramétrages typographiques. [https://phabricator.wikimedia.org/T359029]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.42/wmf.26|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-04-09|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédias le {{#time:j xg|2024-04-10|fr}} et enfin sur toutes les Wikipédias restantes le {{#time:j xg|2024-04-11|fr}} ([[mw:MediaWiki 1.42/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* Pour les nouveaux comptes et personnes non-connectées, [[mw:Special:MyLanguage/VisualEditor|l’éditeur visuel]] sera désormais activé par défaut sur mobile. Ce déploiement aura lieu pour tous les wikis, à l’exception de la Wikipédia en anglais. [https://phabricator.wikimedia.org/T361134]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/15|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W15"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 9 avril 2024 à 01:37 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26564838 -->
== Actualités techniques n° 2024-16 ==
<section begin="technews-2024-W16"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/16|D’autres traductions]] sont disponibles.
'''Problèmes'''
* Entre le 2 avril et le 8 avril, sur les wikis utilisant l’[[mw:Special:MyLanguage/Extension:FlaggedRevs|approbation des révisions]] (avec FlaggedRevs), la balise « {{Int:tag-mw-reverted}} » n’était plus appliquées aux modifications défaites. De plus, les renommages, protections et importations de pages n’étaient plus marquées automatiquement comme relues. Ce problèmes est désormais corrigé. [https://phabricator.wikimedia.org/T361918][https://phabricator.wikimedia.org/T361940]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.1|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-04-16|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-04-17|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-04-18|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* <span lang="en" dir="ltr" class="mw-content-ltr">[[mw:Special:MyLanguage/Help:Magic words#DEFAULTSORT|Default category sort keys]] will now affect categories added by templates placed in [[mw:Special:MyLanguage/Help:Cite|footnotes]]. Previously footnotes used the page title as the default sort key even if a different default sort key was specified (category-specific sort keys already worked).</span> [https://phabricator.wikimedia.org/T40435]
* <span lang="en" dir="ltr" class="mw-content-ltr">A new variable <bdi lang="zxx" dir="ltr"><code>page_last_edit_age</code></bdi> will be added to [[Special:AbuseFilter|abuse filters]]. It tells how many seconds ago the last edit to a page was made.</span> [https://phabricator.wikimedia.org/T269769]
'''Futurs changements'''
* Il est demandé aux développeurs bénévoles s’ils peuvent mettre à jour le code de leurs outils afin de prendre en charge les [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]]. [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/For developers/2024-04 CTA|En savoir plus]].
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] <span lang="en" dir="ltr" class="mw-content-ltr">Four database fields will be removed from database replicas (including [[quarry:|Quarry]]). This affects only the <bdi lang="zxx" dir="ltr"><code>abuse_filter</code></bdi> and <bdi lang="zxx" dir="ltr"><code>abuse_filter_history</code></bdi> tables. Some queries might need to be updated.</span> [https://phabricator.wikimedia.org/T361996]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/16|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W16"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 16 avril 2024 à 01:28 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26564838 -->
== Actualités techniques n° 2024-17 ==
<section begin="technews-2024-W17"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/17|D’autres traductions]] sont disponibles.
'''Changements récents'''
* À partir de cette semaine, les nouveaux qui contribuent à Wikipédia [[mw:Special:MyLanguage/Growth/Positive reinforcement#Leveling up 3|seront encouragés]] à essayer des tâches structurées. Les [[mw:Special:MyLanguage/Growth/Feature summary#Newcomer tasks|tâches structurées]] ont montré qu’elles [[mw:Special:MyLanguage/Growth/Personalized first day/Structured tasks/Add a link/Experiment analysis, December 2021|augmentent la probabilité que les nouveaux participent pour la première fois et dans la durée]]. [https://phabricator.wikimedia.org/T348086]
* Vous pouvez [[m:Special:MyLanguage/Coolest Tool Award|désigner vos outils favoris]] pour la cinquième édition de la Cérémonie des outils les plus cools. Les mises en candidatures sont ouvertes jusqu’au 10 mai.
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.2|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-04-23|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-04-24|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-04-25|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
'''Futurs changements'''
* Dernier avertissement : avant la fin du mois de mai 2024, l’habillage Vector 2022 ne partagera plus les scripts et styles avec l’ancien Vector. Pour les scripts utilisateur que vous voulez conserver avec Vector 2022, copiez le contenu depuis [[{{#special:MyPage}}/vector.js]] vers [[{{#special:MyPage}}/vector-2022.js]]. D’autres [[mw:Special:MyLanguage/Reading/Web/Desktop Improvements/Features/Loading Vector 2010 scripts|détails techniques]] sont disponibles. Les admins d’interface qui prévoient que cela va amener de nombreuses questions techniques voudront peut-être envoyer un message groupé à leur communauté, comme cela a été fait sur la Wikipédia en français. [https://phabricator.wikimedia.org/T362701]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/17|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W17"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 22 avril 2024 à 22:27 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26647188 -->
== Votez maintenant pour sélectionner les membres de la première U4C ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024/Announcement – vote opens|Ce message est également traduit dans d'autres langues sur Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Universal Code of Conduct/Coordinating Committee/Election/2024/Announcement – vote opens}}&language=&action=page&filter= {{int:please-translate}}]''
Salut à tous et à toutes,
Je vous écris pour vous informer que la période de vote pour le Comité de coordination du Code de conduite universel (U4C) est ouverte jusqu'au 9 mai 2024. Lisez les informations sur la page [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024| de vote sur Meta-wiki]] pour en savoir plus sur le vote et l'éligibilité des personnes votantes.
Le comité de coordination du code de conduite universel (U4C) est un groupe mondial qui se consacre à la mise en œuvre équitable et cohérente du code de conduite universel. Les membres de la communauté ont été invités à soumettre leur candidature à l'U4C. Pour plus d'informations et pour connaître les responsabilités de l'U4C, veuillez [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|consulter la charte U4C]].
Veuillez partager ce message avec les membres de votre communauté afin qu'ils puissent également participer.
Au nom de l’équipe du projet de CdCU,<section end="announcement-content" />
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 25 avril 2024 à 22:19 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26390244 -->
== Actualités techniques n° 2024-18 ==
<section begin="technews-2024-W18"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/18|D’autres traductions]] sont disponibles.
'''Changements récents'''
[[File:Talk_pages_default_look_(April_2023).jpg|thumb|alt=Capture d'écran des améliorations visuelles apportées aux pages de discussion|Exemple d'une page de discussion avec le nouveau ''design'', en français.]]
* L'apparence des pages de discussion a changé pour les wikis suivants : {{int:project-localized-name-azwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-bnwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-dewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-fawiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-hewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-hiwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-idwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-kowiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-nlwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-ptwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-rowiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-thwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-trwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-ukwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-viwiki/fr}}. Ces wikis ont participé à un test, où 50% des utilisateurs ont obtenu le nouveau design. Comme ce test [[Mw:Special:MyLanguage/Talk pages project/Usability/Analysis|a donné des résultats positifs]], le nouveau design sera déployé par défaut sur ces wikis. Il est possible de refuser ces modifications [[Special:Preferences#mw-prefsection-editing|dans les préférences utilisateur]] ("{{int:discussiontools-preference-visualenhancements}}"). Le déploiement aura lieu sur tous les wikis dans les semaines à venir. [https://phabricator.wikimedia.org/T341491]
* Sept nouveaux wikis ont été créés :
** une {{int:project-localized-name-group-wikipedia}} en [[d:Q33014|betawi]] ([[w:bew:|<code>w:bew:</code>]]) [https://phabricator.wikimedia.org/T357866]
** une {{int:project-localized-name-group-wikipedia}} en [[d:Q35708|kusaal]] ([[w:kus:|<code>w:kus:</code>]]) [https://phabricator.wikimedia.org/T359757]
** une {{int:project-localized-name-group-wikipedia}} en [[d:Q35513|igala]] ([[w:igl:|<code>w:igl:</code>]]) [https://phabricator.wikimedia.org/T361644]
** un {{int:project-localized-name-group-wiktionary}} dans [[d:Q33541|karakalpak]] ([[wikt:kaa:|<code>wikt:kaa:</code>]]) [https://phabricator.wikimedia.org/T362135]
** une {{int:project-localized-name-group-wikisource}} en [[d:Q9228|birman]] ([[s:my:|<code>s:my:</code>]]) [https://phabricator.wikimedia.org/T361085]
** une {{int:project-localized-name-group-wikisource}} en [[d:Q9237|malais]] ([[s:ms:|<code>s:ms:</code>]]) [https://phabricator.wikimedia.org/T363039]
** une {{int:project-localized-name-group-wikisource}} en [[d:Q8108|géorgien]] ([[s:ka:|<code>s:ka:</code>]]) [https://phabricator.wikimedia.org/T363085]
* Vous pouvez désormais [https://translatewiki.net/wiki/Support#Early_access:_Watch_Message_Groups_on_Translatewiki.net suivre des projets ou groupes de messages] sur [[m:Special:MyLanguage/translatewiki.net|Translatewiki.net]]. Dans un premier temps, cette fonctionnalité vous informera des messages ajoutés ou supprimés dans ces groupes. [https://phabricator.wikimedia.org/T348501]
* Le mode sombre est désormais disponible sur tous les wikis, sur le web mobile pour les utilisateurs connectés qui optent pour le [[Special:MobileOptions|mode avancé]]. Il s'agit de la première version de la fonctionnalité. Les contributeurs techniques sont invités à [https://night-mode-checker.wmcloud.org/ vérifier les problèmes d’accessibilité sur les wikis]. Voir [[mw:Special:MyLanguage/Reading/Web/Accessibility for reading/Updates/2024-04|les lignes directrices détaillées]].
'''Problèmes'''
* Les cartes [[mw:Special:MyLanguage/Help:Extension:Kartographer|Kartographer]] peuvent utiliser un style visuel alternatif sans la nomenclature, en utilisant <bdi lang="zxx" dir="ltr"><code><nowiki>mapstyle="osm"</nowiki></code></bdi>. Cela ne fonctionnait pas dans les aperçus, créant la fausse impression que ce n'était pas pris en charge. Ceci a maintenant été corrigé. [https://phabricator.wikimedia.org/T362531]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.3|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-04-30|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-05-01|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-05-02|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/18|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W18"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 30 avril 2024 à 05:33 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26689057 -->
== Actualités techniques n° 2024-19 ==
<section begin="technews-2024-W19"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/19|D’autres traductions]] sont disponibles.
'''Changements récents'''
[[File:Talk_pages_default_look_(April_2023).jpg|thumb|alt=Capture d'écran des améliorations visuelles apportées aux pages de discussion|Exemple d'une page de discussion avec le nouveau ''design'', en français.]]
* L’apparence des pages de discussion a changé pour tous les wikis, excepté pour Commons, Wikidata et la plupart des Wikipédia ([[m:Special:MyLanguage/Tech/News/2024/18|certaines]] sont déjà dotées du nouveau ''design''). Vous pouvez lire les détails de ce changement [[diffblog:2024/05/02/making-talk-pages-better-for-everyone/|sur ''Diff'']]. Il est possible de désactiver ces changements [[Special:Preferences#mw-prefsection-editing|dans les préférences personnelles]] (« {{int:discussiontools-preference-visualenhancements}} »). Le déploiement aura lieu sur les wikis restants dans les semaines à venir. [https://phabricator.wikimedia.org/T352087][https://phabricator.wikimedia.org/T319146]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Les administrateurs d'interface disposent désormais d'un plus grand contrôle sur le style des composants d'articles sur mobile grâce à l'introduction de l'extension <code>SiteAdminHelper</code>. De plus amples informations sur la façon dont les styles peuvent être désactivés sont disponibles [[mw:Special:MyLanguage/Extension:WikimediaMessages#Site_admin_helper|sur la page de l'extension]]. [https://phabricator.wikimedia.org/T363932]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] [[m:Special:MyLanguage/Wikimedia Enterprise|Wikimedia Enterprise]] propose maintenant les sections du corps des articles au format JSON, ainsi qu’un champ de description courte et précise en plus des champs extraits de la boite d’infos (infobox). Cette extension de l’API est aussi proposée sur les Services infonuagiques de Wikimedia. [https://enterprise.wikimedia.com/blog/article-sections-and-description/]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.4|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-05-07|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-05-08|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-05-09|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* Lorsque vous regardez la page Spécial:Journal, la vue par défaut est appelée « Tous les journaux publics », mais n’affiche que certains journaux. Cela va donc être renommé en « Principaux journaux publics ». [https://phabricator.wikimedia.org/T237729]
'''Futurs changements'''
* Un nouveau service sera construit pour remplacer l’extension ''[[mw:Special:MyLanguage/Extension:Graph|Graph]]''. Les détails sont disponibles [[mw:Special:MyLanguage/Extension:Graph/Plans|dans la dernière mise à jour]] concernant cette extension.
* À partir du 21 mai, la Wikipédia en anglais et la Wikipédia en allemand pourront activer l’outil « [[mw:Special:MyLanguage/Help:Growth/Tools/Add a link|Ajouter un lien]] ». Cela fait partie du [[phab:T304110|déploiement progressif de cet outil pour toutes les Wikipédia]]. Ces communautés pourront [[mw:Special:MyLanguage/Growth/Community configuration|activer et configurer la fonctionnalité localement]]. [https://phabricator.wikimedia.org/T308144]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/19|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W19"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 6 mai 2024 à 18:44 (CEST)
<!-- Message envoyé par User:Trizek (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26729363 -->
== Actualités techniques n° 2024-20 ==
<section begin="technews-2024-W20"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/20|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Sur Wikisource, il y a une page spéciale répertoriant les pages d'œuvres sans images de numérisation correspondantes. Maintenant, vous pouvez utiliser le nouveau mot magique <bdi lang="zxx" dir="ltr"><code>__EXPECTWITHOUTSCANS__</code></bdi> pour exclure certaines pages (liste des éditions ou traductions d'œuvres) de cette liste. [https://phabricator.wikimedia.org/T344214]
* Si vous utilisez la [[Special:Preferences#mw-prefsection-editing|préférence utilisateur]] « {{int:tog-uselivepreview}} », alors la fonctionnalité des pages de modèle « {{int:Templatesandbox-editform-legend}} » fonctionnera désormais également sans recharger la page. [https://phabricator.wikimedia.org/T136907]
* Les cartes [[mw:Special:Mylanguage/Extension:Kartographer|Kartographer]] peuvent maintenant spécifier un texte alternatif via l'attribut <bdi lang="zxx" dir="ltr"><code><nowiki>alt=</nowiki></code></bdi>. Ceci est identique à l'utilisation de l'attribut <bdi lang="zxx" dir="ltr"><code><nowiki>alt=</nowiki></code></bdi> dans la syntaxe des [[mw:Special:MyLanguage/Help:Images#Syntax|images et galeries]]. Une exception pour cette fonctionnalité concerne les wikis tels que Wikivoyage où les mini-cartes sont interactives. [https://phabricator.wikimedia.org/T328137]
* L'ancienne [[mw:Special:MyLanguage/Extension:GuidedTour|Visite guidée]] de la fonctionnalité « [[mw:Special:MyLanguage/Edit Review Improvements/New filters for edit review|Nouveaux filtres pour la révision des modifications]] » a été supprimée. Elle avait été créée en 2017 pour montrer aux personnes ayant des comptes plus anciens comment l'interface avait changé, et elle a maintenant été vue par la plupart des personnes concernées. [https://phabricator.wikimedia.org/T217451]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.5|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-05-14|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-05-15|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-05-16|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] La page des résultats de la recherche [[{{#special:search}}]] utilisera désormais les attributs CSS flex, pour une meilleure accessibilité, au lieu d'un tableau. Si vous avez un gadget ou un script qui ajuste les résultats de la recherche, vous devriez adapter votre script à la nouvelle structure HTML. [https://phabricator.wikimedia.org/T320295]
'''Futurs changements'''
* Avec l’habillage Vector 2022, les pages de l’espace principal seront affichées en pleine largeur (comme pages spéciales). L’objectif est de maintenir un nombre de caractères par ligne suffisamment important. Ceci est lié aux prochains changements de typographie dans Vector 2022. [[mw:Special:MyLanguage/Reading/Web/Accessibility for reading/Updates|En savoir plus]]. [https://phabricator.wikimedia.org/T357706]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Deux colonnes de la table de base de données <bdi lang="zxx" dir="ltr"><code>[[mw:Special:MyLanguage/Manual:pagelinks table|pagelinks]]</code></bdi> (<bdi lang="zxx" dir="ltr"><code>pl_namespace</code></bdi> et <bdi lang="zxx" dir="ltr"><code>pl_title</code></bdi>) seront bientôt supprimées. Les utilisateurs doivent utiliser deux colonnes de la nouvelle table <bdi lang="zxx" dir="ltr"><code>[[mw:special:MyLanguage/Manual:linktarget table|linktarget]]</code></bdi> (<bdi lang="zxx" dir="ltr"><code>lt_namespace</code></bdi> et <bdi lang="zxx" dir="ltr"><code>lt_title</code></bdi>) à la place. Dans vos requêtes SQL existantes :
*# remplacez <bdi lang="zxx" dir="ltr"><code>JOIN pagelinks</code></bdi> par <bdi lang="zxx" dir="ltr"><code>JOIN linktarget</code></bdi> et <bdi lang="zxx" dir="ltr"><code>pl_</code></bdi> par <bdi lang="zxx" dir="ltr"><code>lt_</code></bdi> dans la clause <bdi lang="zxx" dir="ltr"><code>ON</code></bdi> ;
*# puis ajoutez <bdi lang="zxx" dir="ltr"><code>JOIN pagelinks ON lt_id = pl_target_id</code></bdi>.
** Voir <bdi lang="en" dir="ltr">[[phab:T222224]]</bdi> pour les raisons techniques. [https://phabricator.wikimedia.org/T222224][https://phabricator.wikimedia.org/T299947]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/20|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W20"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 14 mai 2024 à 01:58 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26762074 -->
== Inscrivez-vous à la réunion communauté linguistique du 31 mai à 16 h UTC ==
<section begin="message"/>Bonjour à tous,
La prochaine réunion communauté linguistique est prévue dans quelques semaines, le 31 mai à 16 h UTC. Si vous cela vous intéresse, vous pouvez [https://www.mediawiki.org/w/index.php?title=Wikimedia_Language_engineering/Community_meetings#31_May_2024 vous inscrire sur cette page wiki].
Il s’agit d’une réunion orientée pour les participants, où nous faisons part des actualités de divers projets, discutons collectivement des questions techniques sur les wikis linguistiques et collaborons pour trouver des solutions possibles. Par exemple, lors de la dernière réunion, nous avons parlé du service de traduction automatique (MinT) et des langues et modèles qu’il prenait actuellement en charge, du travail de régionalisation de l’équipe de Kiwix et du défi technique de tri numérique des fichier utilisé sur la Wikisource en bengali.
Vous avez envie de faire part d’actualités techniques concernant votre projet ? Un problème que vous voudriez soumettre à discussion durant la réunion ? Vous avez besoin d’une interprétation de l’anglais dans votre langue ? Contactez-moi à l’adresse ssethi(__AT__)wikimedia.org et [[etherpad:p/language-community-meeting-may-2024|ajoutez des éléments à l’ordre du jour ici]].
Nous attendons votre participation !
<section end="message"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 14 mai 2024 à 23:22 (CEST)
<!-- Message envoyé par User:SSethi (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26390244 -->
== Actualités techniques n° 2024-21 ==
<section begin="technews-2024-W21"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/21|D’autres traductions]] sont disponibles.
'''Changements récents'''
* La fonctionnalité [[mw:Special:MyLanguage/Extension:Nuke|Nuke]], qui permet aux administrateurs de supprimer en masse des pages, supprimera désormais correctement les pages qui ont été déplacées vers un autre titre. [https://phabricator.wikimedia.org/T43351]
* De nouveaux changements ont été apportés à l'assistant de téléchargement de Wikimedia Commons : la mise en page générale a été améliorée, en suivant une nouvelle mise en forme et espacement pour le formulaire et ses champs ; les en-têtes et les textes d'aide pour chacun des champs ont été modifiés ; le champ Légende est désormais un champ obligatoire, et il y a une option pour que les utilisateurs copient leur légende dans la description du média. [https://commons.wikimedia.org/wiki/Commons:WMF_support_for_Commons/Upload_Wizard_Improvements#Changes_to_%22Describe%22_workflow][https://phabricator.wikimedia.org/T361049]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.6|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-05-21|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-05-22|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-05-23|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Le HTML utilisé pour afficher tous les titres [[mw:Heading_HTML_changes|est en train d'être modifié pour améliorer l'accessibilité]]. Il changera le 22 mai sur certains habillages (Timeless, Modern, CologneBlue, Nostalgie et Monobook). Vous pouvez tester les gadgets sur votre wiki avec ces habillages et [[phab:T13555|signaler tout problème lié]] afin qu'ils puissent être résolus avant que ce changement ne soit apporté à tous les autres habillages. Les développeurs envisagent également d'introduire une [[phab:T337286|API de gadget pour ajouter des boutons aux titres de section]] si cela peut être utile aux créateurs d'outils, et apprécieraient tout avis que vous pourriez avoir à ce sujet.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/21|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W21"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 21 mai 2024 à 01:04 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26786311 -->
== Invitation à des retours sur la Procédure pour le Cycle de vie des Projets parents ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Wikimedia Foundation Community Affairs Committee/Procedure for Sibling Project Lifecycle/Invitation for feedback (MM)|Ce message est également traduit dans d’autres langues sur Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Wikimedia Foundation Community Affairs Committee/Procedure for Sibling Project Lifecycle/Invitation for feedback (MM)}}&language=&action=page&filter= {{int:please-translate}}]''
[[File:Sibling Project Lifecycle Conversation 3.png|150px|right|link=:m:Special:MyLanguage/Wikimedia Foundation Community Affairs Committee/Procedure for Sibling Project Lifecycle]]
Chers membres de la communauté,
Le [[:m:Special:MyLanguage/Wikimedia Foundation Community Affairs Committee|Comité des affaires communautaires]] (le CAC) du [[:m:Special:MyLanguage/Wikimedia Foundation Board of Trustees|conseil d’administration de la Fondation Wikimédia]] vous invite à donner des retours à propos d’un '''[[:m:Special:MyLanguage/Wikimedia Foundation Community Affairs Committee/Procedure for Sibling Project Lifecycle|brouillon de Procédure pour le Cycle de vie des Projets parents]]'''. Cette Procédure décrit des étapes et des conditions pour lancer et arrêter des Projets parents Wikimédia, et vise à assurer le succès de tout nouveau projet approuvé. Ceci est distinct des procédures pour lancer ou arrêter de nouvelles langues pour des projets existants, dont s’occupent le [[:m:Special:MyLanguage/Language committee|Comité de Langue]] ou [[m:Special:MyLanguage/Closing_projects_policy|la politique de fermeture de projets]].
Les détails sont disponibles sur [[:m:Special:MyLanguage/Talk:Wikimedia Foundation Community Affairs Committee/Procedure for Sibling Project Lifecycle#Review|cette page]], ainsi que la manière d’apporter vos retours, et ceci jusqu’à la fin du '''23 juin 2024''', d’où que vous soyez.
Vous pouvez également partager des informations à ce sujet avec les communautés de projets intéressées avec lesquelles vous travaillez ou que vous soutenez, et vous pouvez aussi nous aider à traduire la procédure en plus de langues, afin que les gens puissent participer aux discussions dans leur propre langue.
De la part du CAC,<section end="announcement-content" />
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 22 mai 2024 à 04:24 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26390244 -->
== Actualités techniques n° 2024-22 ==
<section begin="technews-2024-W22"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/22|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Plusieurs bugs liés aux dernières mises à jour de l’assistant de téléversement sur Wikimedia Commons ont été corrigés. Pour plus d'informations, voir [[:phab:T365107|T365107]] et [[:phab:T365119|T365119]].
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] En mars 2024, une nouvelle API [[mw:ResourceLoader/Core_modules#addPortlet|addPortlet]] a été ajoutée pour permettre aux gadgets de créer de nouveaux portlets (menus) dans l'interface. Dans certains habillages, cela peut être utilisé pour créer des menus déroulants. Les développeurs de gadgets sont invités à l'essayer et à [[phab:T361661|donner leur avis]].
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Certains CSS sur l’habillage Minerva ont été supprimés pour permettre une configuration communautaire plus facile. Les admins d'interface devraient vérifier le rendu sur les appareils mobiles pour les aspects liés aux classes <bdi lang="zxx" dir="ltr"><code>.collapsible</code></bdi>{{int:comma-separator/fr}}<bdi lang="zxx" dir="ltr"><code>.multicol</code></bdi>{{int:comma-separator/fr}}<bdi lang="zxx" dir="ltr"><code>.reflist</code></bdi>{{int:comma-separator/fr}}<bdi lang="zxx" dir="ltr"><code>.coordinates</code></bdi>{{int:comma-separator/fr}}<bdi lang="zxx" dir="ltr"><code>.topicon</code></bdi>. [[phab:T361659|Des détails supplémentaires sur le remplacement du CSS sont disponibles]] si nécessaire.
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.7|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-05-28|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-05-29|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-05-30|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* Lorsque vous visitez un wiki où vous n'avez pas encore de compte local, des règles locales telles que les filtres de modification peuvent parfois empêcher la création de votre compte. À partir de cette semaine, MediaWiki prend en compte vos droits globaux pour évaluer si vous pouvez contourner de telles règles locales. [https://phabricator.wikimedia.org/T316303]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/22|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W22"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 28 mai 2024 à 02:15 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26832205 -->
== Annonce du premier Comité de Coordination du Code de Conduite Universel ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024/Announcement – results|Ce message est également traduit dans d'autres langues sur Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Universal Code of Conduct/Coordinating Committee/Election/2024/Announcement – results}}&language=&action=page&filter= {{int:please-translate}}]''
Bonjour,
Les scrutateurs ont terminé d'examiner les résultats du vote. Voici les résultats de la première [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024|élection du Comité de Coordination du Code de Conduite Universel (U4C)]].
Nous sommes heureux d'annoncer que les individus suivants sont les membres régionaux de l'U4C, qui rempliront un mandat de deux ans:
* Amérique du Nord (États-Unis et Canada)
** –
* Europe du Nord et de l'Ouest
** [[m:Special:MyLanguage/User:Ghilt|Ghilt]]
* Amérique latine et Caraïbes
** –
* Europe centrale et orientale (CEE)
** —
* Afrique sub-saharienne
** –
* Moyen-Orient et Afrique du Nord
** [[m:Special:MyLanguage/User:Ibrahim.ID|Ibrahim.ID]]
* Asie de l'Est, du Sud-Est et du Pacifique (ESEAP)
** [[m:Special:MyLanguage/User:0xDeadbeef|0xDeadbeef]]
* Asie du Sud
** –
Les personnes suivantes sont élues membres de le communauté au sens large dans le U4C, remplissant un mandat d'un an:
* [[m:Special:MyLanguage/User:Barkeep49|Barkeep49]]
* [[m:Special:MyLanguage/User:Superpes15|Superpes15]]
* [[m:Special:MyLanguage/User:Civvì|Civvì]]
* [[m:Special:MyLanguage/User:Luke081515|Luke081515]]
* –
* –
* –
* –
Merci encore à tous ceux qui ont participé au processus et particulièrement aux candidats pour leur leadership et leur dévouement au mouvement et à la communauté Wikimédia.
Pendant les prochaines semaines, le U4C va se réunir et planifier l'année 2024-25 dans le soutien de l'implémentation et la revue du UCoC et des Directives d'Application. Vous pouvez suivre leurs travaux sur [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Meta-wiki]].
Au nom de l'équipe du projet UCoC,<section end="announcement-content" />
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 3 juin 2024 à 10:14 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26390244 -->
== Actualités techniques n° 2024-23 ==
<section begin="technews-2024-W23"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/23|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Il est maintenant possible pour les administrateurs locaux d'ajouter de nouveaux liens en bas du menu Outils du site sans utiliser JavaScript. [[mw:Manual:Interface/Sidebar#Add or remove toolbox sections|La documentation est disponible]]. [https://phabricator.wikimedia.org/T6086]
* Le nom du message pour la définition de la catégorie de suivi de WikiHiero, « <bdi lang="zxx" dir="ltr"><code>MediaWiki:Wikhiero-usage-tracking-category</code></bdi> », a changé en « <bdi lang="zxx" dir="ltr"><code>MediaWiki:Wikihiero-usage-tracking-category</code></bdi> ». [https://gerrit.wikimedia.org/r/c/mediawiki/extensions/wikihiero/+/1035855]
* Un nouveau wiki a été créé : une {{int:project-localized-name-group-wikipedia}} en [[d:Q5317225|dusun kadazan]] ([[w:dtp:|<code>w:dtp:</code>]]) [https://phabricator.wikimedia.org/T365220]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.8|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-06-04|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-06-05|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-06-06|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
'''Futurs changements'''
* La semaine prochaine, sur les wikis avec l'habillage Vector 2022 par défaut, les utilisateurs sur ordinateur non connectés pourront choisir entre différentes tailles de police. La taille de police par défaut sera également augmentée pour eux. Ceci est destiné à rendre les projets Wikimédia plus faciles à lire. [[mw:Special:MyLanguage/Reading/Web/Accessibility for reading/Updates/2024-06 deployments|En savoir plus]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/23|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W23"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 4 juin 2024 à 00:34 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26844397 -->
== Actualités techniques n° 2024-24 ==
<section begin="technews-2024-W24"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/24|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Le logiciel utilisé pour afficher les fichiers SVG a été mis à jour vers une nouvelle version, corrigeant de nombreux problèmes de longue date de rendu SVG. [https://phabricator.wikimedia.org/T265549]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Le HTML utilisé pour rendre tous les titres [[mw:Heading HTML changes|est en train d'être modifié pour améliorer l'accessibilité]]. Il a été modifié la semaine dernière dans certains habillages (Vector legacy et Minerva). Veuillez tester les gadgets sur votre wiki sur ces habillages et [[phab:T13555|signalez tout problème connexe]] afin qu'ils puissent être résolus avant que ce changement ne soit apporté à Vector 2022. Par ailleurs, les développeurs envisagent toujours l'introduction d'une [[phab:T337286|API pour gadgets pour ajouter des boutons aux titres de section]] si cela était utile aux créateurs d'outils, et apprécieraient tout retour que vous pourriez avoir à ce sujet.
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] La balise HTML utilisée pour les références par [[mw:Special:MyLanguage/Parsoid|Parsoid]] a changé la semaine dernière. Dans les endroits où Parsoid ajoutait précédemment la classe <bdi lang="zxx" dir="ltr"><code>mw-reference-text</code></bdi>, Parsoid ajoute maintenant également la classe <bdi lang="zxx" dir="ltr"><code>reference-text</code></bdi> pour une meilleure compatibilité avec l'analyseur historique. [[mw:Specs/HTML/2.8.0/Extensions/Cite/Announcement|Plus de détails sont disponibles]]. [https://gerrit.wikimedia.org/r/1036705]
'''Problèmes'''
* Il y avait un bug avec l'interface de traduction de contenu qui faisait apparaître les menus d'outils au mauvais endroit. Cela a maintenant été corrigé. [https://phabricator.wikimedia.org/T366374]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.9|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-06-11|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-06-12|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-06-13|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] La nouvelle version de MediaWiki inclut une autre modification du balisage HTML utilisé pour les références : [[mw:Special:MyLanguage/Parsoid|Parsoid]] enveloppera désormais les références nommées et non nommées dans une balise <bdi lang="zxx" dir="ltr"><code><nowiki><span class="mw-cite-backlink"></nowiki></code></bdi>, pour une meilleure compatibilité avec l'analyseur historique. Les administrateurs d'interface doivent vérifier que les gadgets qui interagissent avec les références sont compatibles avec le nouveau balisage. [[mw:Specs/HTML/2.8.0/Extensions/Cite/Announcement|Plus de détails sont disponibles]]. [https://gerrit.wikimedia.org/r/1035809]
* Sur les wikis multilingues qui utilisent le système <bdi lang="zxx" dir="ltr"><code><nowiki><translate></nowiki></code></bdi>, il existe une fonctionnalité qui affiche les traductions potentiellement obsolètes avec un arrière-plan rose jusqu'à ce qu'elles soient mises à jour ou confirmées. À partir de cette semaine, la confirmation d’une traduction sera journalisée et il existera un nouveau droit utilisateur qui peut être requis pour confirmer les traductions si la communauté le [[m:Special:MyLanguage/Requesting wiki configuration changes|demande]]. [https://phabricator.wikimedia.org/T49177]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/24|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W24"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 10 juin 2024 à 22:20 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26893898 -->
== Le texte final de la Charte du Mouvement Wikimédia est désormais disponible sur Meta ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Movement Charter/Drafting Committee/Announcement - Final draft available|Ce message est également traduit dans d'autres langues sur Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Movement Charter/Drafting Committee/Announcement - Final draft available}}&language=&action=page&filter= {{int:please-translate}}]''
Bonjour à toutes et à tous,
Le texte final de la [[m:Special:MyLanguage/Movement Charter|Charte du Mouvement Wikimédia]] est désormais disponible sur Meta dans plus de 20 langues pour consultation.
'''Qu'est-ce que la charte du mouvement Wikimédia?'''
La charte du mouvement Wikimédia est un document proposé pour définir les rôles et les responsabilités de tous les membres et entités du mouvement Wikimédia, y compris la création d'un nouvel organe - le Conseil mondial - pour la gouvernance du mouvement.
'''Rejoignez la "cérémonie de lancement" de la charte du mouvement Wikimédia.'''
Rejoignez la [[1$|« Soirée de lancement »]] le '''20 juin 2024''' à '''14.00-15.00 UTC''' ([https://zonestamp.toolforge.org/1718892000 votre heure locale]). Au cours de cet appel, nous célébrerons la publication de la Charte finale et présenterons le contenu de la Charte. Participez et renseignez-vous sur la Charte avant de passer au vote.
'''Vote de ratification de la Charte du Mouvement'''
Le vote débutera sur SecurePoll le '''25 juin 2024''' à '''00:01 UTC''' et se terminera le '''9 juillet 2024''' à '''23:59 UTC'''. Vous pouvez en savoir plus sur le processus de vote, les critères d'éligibilité et d'autres détails sur Meta.
Pour toute question, veuillez laisser un commentaire sur la [[m:Special:MyLanguage/Talk:Movement Charter|Page de discussion Meta]] ou envoyer un e-mail au MCDC à l'adresse suivante : [mailto:mcdc@wikimedia.org mcdc@wikimedia.org].
Au nom du MCDC,<section end="announcement-content" />
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 11 juin 2024 à 10:44 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26390244 -->
== Actualités techniques n° 2024-25 ==
<section begin="technews-2024-W25"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/25|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Les personnes qui tentent d’ajouter un lien externe via l’éditeur visuel recevront désormais immédiatement un avertissement si le lien mène vers un domaine que le projet a décidé de bloquer. Plus de détails sont sur la page sur la [[mw:Special:MyLanguage/Edit_check#11_June_2024|vérification des modifications]]. [https://phabricator.wikimedia.org/T366751]
* La nouvelle extension [[mw:Special:MyLanguage/Extension:CommunityConfiguration|Configuration communautaire]] est disponible [[testwiki:Special:CommunityConfiguration|sur la Wikipédia de test]]. Cette extension permet aux communautés de personnaliser des fonctionnalités spécifiques pour répondre à leurs besoins locaux. Actuellement, seules les fonctionnalités de croissance sont configurables, mais l’extension prendra en charge [[mw:Special:MyLanguage/Community_configuration#Use_cases|d’autres cas d’utilisation]] à l’avenir. [https://phabricator.wikimedia.org/T323811][https://phabricator.wikimedia.org/T360954]
* La [[Special:Preferences#mw-prefsection-betafeatures|fonctionnalité bêta]] « mode sombre » est désormais disponible sur les pages de catégories et d'aide, ainsi que sur d'autres pages spéciales. Il peut y avoir des problèmes de contraste. Merci de signaler les bogues sur la [[mw:Talk:Reading/Web/Accessibility_for_reading|page de discussion du projet]]. [https://phabricator.wikimedia.org/T366370]
'''Problèmes'''
* [[File:Octicons-tools.svg|12px|link=|alt=|Sujet technique]] Les outils des services d'informatique en nuage (<i lang="en">Cloud Service tools</i>) n'ont pas été disponibles pendant 25 minutes la semaine dernière. Cela a été causé par un câble défectueux dans le centre de données. [https://wikitech.wikimedia.org/wiki/Incidents/2024-06-11_WMCS_Ceph]
* La semaine dernière, des changements de mise en forme ont été appliqués à l’habillage Vector 2022. Cela a causé des problèmes imprévus avec des modèles, bandeaux et images. Les changements aux modèles et bandeaux ont été annulés. La plupart des problèmes avec les images ont été corrigés. Si vous en rencontrez encore, [[phab:T367463|signalez-les]]. [https://phabricator.wikimedia.org/T367480]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.10|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-06-18|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-06-19|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-06-20|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* À partir du 18 juin, l’outil de [[mw:Special:MyLanguage/Help:Edit check#ref|vérification du sourçage]] sera déployé sur [[phab:T361843|un nouvel ensemble de Wikipédia]]. Cette fonctionnalité a pour but d'aider les nouveaux arrivants et d'assister les patrouilleurs en invitant les nouveaux arrivants qui ajoutent du nouveau contenu à un article de Wikipédia à ajouter une référence lorsqu'ils ne le font pas eux-mêmes. Lors d’un [[mw:Special:MyLanguage/Edit_check#Reference_Check_A/B_Test|test sur onze wikis]], le nombre de références ajoutées a [https://diff.wikimedia.org/?p=127553 plus que doublé] lorsque l’outil était affiché. Le contrôle des références est [[mw:Special:MyLanguage/Edit check/Configuration|configurable par la communauté]]. [https://phabricator.wikimedia.org/T361843]<!-- NOTE: THE DIFF BLOG WILL BE PUBLISHED ON MONDAY -->
* Les [[m:Special:MyLanguage/Mailing_lists|listes de diffusion]] seront indisponibles pendant deux heures environ mardi de 10 h à 12 h UTC, afin de permettre la migration vers un nouveau serveur et mettre à jour le logiciel. [https://phabricator.wikimedia.org/T367521]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/25|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W25"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 18 juin 2024 à 01:48 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26911987 -->
== Actualités techniques n° 2024-26 ==
<section begin="technews-2024-W26"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/26|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Vous avez remarqué un changement dans les couleurs d’arrière-plan du texte des diffs et celle du différentiel d’octets, la semaine dernière. Ces changements visent à rendre le texte plus lisible, en mode clair comme en mode sombre, dans le cadre d’une amélioration de l’accessibilité. Vos commentaires et questions sont possible sur [[mw:Talk:Reading/Web/Accessibility for reading|la page de discussion du projet]]. [https://phabricator.wikimedia.org/T361717]
* Les couleurs de texte utilisées pour les liens visités, survolés ou actifs ont aussi légèrement changé la semaine dernière pour améliorer l’accessibilité dans le mode clair comme dans le mode sombre. [https://phabricator.wikimedia.org/T366515]
'''Problèmes'''
* Vous pouvez [[mw:Special:MyLanguage/Help:DiscussionTools#Talk pages permalinking|copier des liens permanents vers des messages des pages de discussion]] en cliquant sur l’horodatage des messages. [[mw:Talk pages project/Permalinks|Cette fonctionnalité]] ne fonctionnait pas toujours quand le titre du sujet était très long et que le lien était utilisé en wikicode, mais cela a été corrigé. Merci à Lofhi d’avoir signalé le beugue. [https://phabricator.wikimedia.org/T356196]
'''Changements à venir cette semaine'''
* [[File:Octicons-sync.svg|12px|link=|alt=|Sujet récurrent]] La [[mw:MediaWiki 1.43/wmf.11|nouvelle version]] de MediaWiki sera installée sur les wikis de test et sur MediaWiki.org à partir du {{#time:j xg|2024-06-25|fr}}. Elle sera installée sur tous les wikis hormis la majorité des Wikipédia le {{#time:j xg|2024-06-26|fr}} et enfin sur toutes les Wikipédia restantes le {{#time:j xg|2024-06-27|fr}} ([[mw:MediaWiki 1.43/Roadmap|calendrier]]). [https://wikitech.wikimedia.org/wiki/Deployments/Train][https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
* À partir du 26 juin, tous les horodatages des messages des pages de discussion deviendront un lien sur Wikipédia en anglais : tous les wikis auront désormais cette fonctionnalité. Ce lien est un lien permanent vers le message. Il permet aux utilisateurs de retrouver le message lié, même s’il a été déplacé entretemps. Vous pouvez en savoir plus sur cette fonctionnalité [[DiffBlog:/2024/01/29/talk-page-permalinks-dont-lose-your-threads/|sur Diff]] ou [[mw:Special:MyLanguage/Help:DiscussionTools#Talk pages permalinking|sur MediaWiki.org]]. [https://phabricator.wikimedia.org/T365974]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/26|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W26"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 25 juin 2024 à 00:32 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=26989424 -->
== Le vote pour la ratification de la Charte du Mouvement Wikimédia est maintenant ouvert - votez ! ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Movement Charter/Drafting Committee/Announcement - Ratification vote opens|Ce message est également traduit dans d'autres langues sur Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Movement Charter/Drafting Committee/Announcement - Ratification vote opens}}&language=&action=page&filter= {{int:please-translate}}]''
Bonjour à toutes et à tous,
Le vote pour ratifier la [[m:Special:MyLanguage/Movement Charter|'''Charte du Mouvement Wikimédia''']] est désormais ouvert. La Charte du Mouvement Wikimédia est un document qui définit les rôles et les responsabilités de tous les membres et entités du mouvement Wikimédia, y compris la création d'un nouvel organe - le Conseil mondial - pour la gouvernance du mouvement.
La version finale de la Charte du Mouvement Wikimédia est [[m:Special:MyLanguage/Movement Charter|disponible sur Meta dans différentes langues]].
Le vote a débuté sur SecurePoll le '''25 juin 2024''' à '''00:01 UTC''' et se terminera le '''9 juillet 2024''' à '''23:59 UTC'''. Pour en savoir plus, consultez la page [[m:Special:MyLanguage/Movement Charter/Ratification/Voting|informations sur les votants et les conditions d'éligibilité]].
Après avoir lu la Charte, veuillez [[Special:SecurePoll/vote/398|'''voter ici''']] et faites partager cette note.
Si vous avez des questions concernant le vote de ratification, veuillez contacter la Commission électorale de la Charte à l'adresse suivante: [mailto:cec@wikimedia.org '''cec@wikimedia.org'''].
Au nom du CEC,<section end="announcement-content" />
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 25 juin 2024 à 12:51 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26989444 -->
== Actualités techniques n° 2024-27 ==
<section begin="technews-2024-W27"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/27|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Au cours des trois prochaines semaines, le mode sombre sera disponible pour tous les utilisateurs, qu'ils soient connectés ou non, en commençant par la version Web mobile. Cela répond à l'un des [[m:Special:MyLanguage/Community_Wishlist_Survey_2023/Reading/Dark_mode|souhaits communautaires les plus demandés]] et améliore la lisibilité et l'utilisation en faible contraste dans les environnements peu éclairés. Dans le cadre de ces changements, le mode sombre fonctionnera également sur les pages utilisateur et les portails. Vous trouverez plus d'informations dans [[mw:Special:MyLanguage/Reading/Web/Accessibility_for_reading/Updates#June_2024:_Typography_and_dark_mode_deployments,_new_global_preferences|la dernière note d’actualité de l'équipe Web]]. [https://phabricator.wikimedia.org/T366364]
* Les utilisateurs connectés peuvent désormais définir [[m:Special:GlobalPreferences#mw-prefsection-rendering-skin-skin-prefs|leurs préférences globales pour la taille du texte et le mode sombre]], grâce à un effort commun entre les équipes de la Fondation. Cela permet aux Wikimédiens utilisant plusieurs wikis de mettre en place facilement une expérience de lecture cohérente, par exemple en passant du mode clair au mode sombre une seule fois pour tous les wikis. [https://phabricator.wikimedia.org/T341278]
* Si vous utilisez un navigateur Web très ancien, certaines fonctionnalités pourraient ne pas fonctionner sur les wikis de Wikimedia. Cela concerne Internet Explorer 11 et les versions de Chrome, Firefox et Safari antérieures à 2016. Ce changement permet d'utiliser de nouvelles fonctionnalités en [[d:Q46441|CSS]] et d'envoyer moins de code à tous les lecteurs. [https://phabricator.wikimedia.org/T288287][https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:How_to_make_a_MediaWiki_skin#Using_CSS_variables_for_supporting_different_themes_e.g._dark_mode]
* Les administrateurs de Wikipedia peuvent facilement personnaliser les options de configuration locale du wiki en utilisant la [[mw:Special:MyLanguage/Community Configuration|Configuration de la communauté]]. La Configuration de la communauté a été créée pour permettre aux communautés de personnaliser le fonctionnement de certaines fonctionnalités, car chaque Wiki de langue a des besoins uniques. Pour le moment, les administrateurs peuvent configurer les [[mw:Special:MyLanguage/Growth/Feature_summary|Fonctionnalités de croissance]] sur leurs wikis locaux, afin de mieux recruter et retenir les nouveaux éditeurs. D'autres options seront fournies dans les mois à venir. [https://phabricator.wikimedia.org/T366458]
* Les éditeurs intéressés par les questions linguistiques liées aux [[w:en:Unicode|normes Unicode]] peuvent désormais discuter de ces sujets dans [[mw:Talk:WMF membership with Unicode Consortium|un nouvel espace de discussion sur MediaWiki.org]]. La Fondation Wikimedia est maintenant un [[mw:Special:MyLanguage/WMF membership with Unicode Consortium|membre du Consortium Unicode]], et le groupe de coordination peut examiner de manière collaborative les problèmes discutés et, le cas échéant, les porter à l'attention du Consortium Unicode.
* Un nouveau Wiki a été créé : un {{int:project-localized-name-group-wikipedia}} en [[d:Q2891049|Mandailing]] ([[w:btm:|<code>w:btm:</code>]]) [https://phabricator.wikimedia.org/T368038]
'''Problèmes'''
* Les contributeurs peuvent à nouveau cliquer sur les liens dans l'aperçu des références de l'éditeur visuel, grâce à une correction de beugue par l'équipe Contribution. [https://phabricator.wikimedia.org/T368119]
'''Futurs changements'''
* [https://wikimediafoundation.limesurvey.net/758713?lang=fr Aidez-nous à améliorer les ''Actualités techniques'' en répondant à cette enquête]. L'objectif est de mieux répondre aux besoins des différents types de personnes qui lisent les ''Actualités techniques''. L'enquête sera ouverte pendant 2 semaines. L'enquête est couverte par [https://foundation.wikimedia.org/wiki/Legal:Tech_News_Survey_2024_Privacy_Statement cette déclaration de confidentialité]. Des traductions sont disponibles.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/27|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W27"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 2 juillet 2024 à 01:59 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27038456 -->
== Le vote pour la ratification de la charte du mouvement Wikimédia prend fin bientôt ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Movement Charter/Drafting Committee/Announcement - Final reminder|Ce message est également traduit dans d'autres langues sur Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Movement Charter/Drafting Committee/Announcement - Final reminder}}&language=&action=page&filter= {{int:please-translate}}]''
Bonjour à toutes et à tous,
Ceci est un aimable rappel que la période de vote pour la ratification de la [[m:Special:MyLanguage/Movement Charter|Charte du Mouvement Wikimédia]] prendra fin le '''9 juillet 2024''', à '''23:59 UTC'''.
Si vous n'avez pas encore voté, veuillez le faire [[m:Special:SecurePoll/vote/398|sur SecurePoll]].
Au nom de la [[m:Special:MyLanguage/Movement_Charter/Ratification/Voting#Electoral_Commission|Commission électorale de la Charte]],<section end="announcement-content" />
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 8 juillet 2024 à 05:45 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26989444 -->
== Actualités techniques n° 2024-28 ==
<section begin="technews-2024-W28"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/28|D’autres traductions]] sont disponibles.
'''Changements récents'''
* Chez Wikimedia Foundation, un nouveau groupe de travail a été créé pour remplacer l’extension désactivée Graph par [[mw:Special:MyLanguage/Extension:Chart/Project|Chart, une extension plus sécurisée, facile à utiliser et extensible]]. Vous pouvez vous abonner [[mw:Special:MyLanguage/Newsletter:Chart Project|abonner à l’infolettre]] pour être informé{{GENDER:||e}} des actualités du projet et concernant les graphiques.
* L’extension [[m:Special:MyLanguage/CampaignEvents|CampaignEvents]] (évènements) est désormais disponible sur Meta-Wiki, Wikipédia en igbo et Wikipédia en swahili ; son activation sur votre wiki peut être demandée. Cette extension aide à gérer et rendre les événements plus visibles, en donnant aux organisateurs d'événements la possibilité d'utiliser des outils tels que l'outil d'inscription aux événements. Pour en savoir plus sur l'état du déploiement et comment demander cette extension pour votre wiki, visitez la page [[m:Special:MyLanguage/CampaignEvents/Deployment_status|CampaignEvents sur Meta-wiki]].
* Les contributeurs et contributrices qui utilisent l’appli Wikipedia pour iOS qui ont plus de 50 modifications peuvent désormais utiliser la fonction d’[[mw:Special:MyLanguage/Wikimedia Apps/iOS Suggested edits#Add an image|ajout d’image]]. Celle-ci est un atout pour de petites, mais utiles contributions à Wikipédia.
* Merci à tous les [[mw:MediaWiki Product Insights/Contributor retention and growth/Celebration|contributeurs et contributrices]] du cœur de MediaWiki. Grâce à ces contributions, le [[mw:MediaWiki Product Insights/Contributor retention and growth|pourcentage de participants à plus de 5 correctifs a augmenté de 25 % par rapport à l’an passé]], ce qui participe à la durabilité de la plateforme pour les projets Wikimedia.
'''Problèmes'''
* Un problème qui rendait la couleur de l’onglet Discussion toujours bleue dans l’habillage Vector 2022, même quand la page n’existait pas et devait donc être rouge, a [[phab:T367982|été corrigé]].
'''Futurs changements'''
* L’équipe Produits pour la confiance et la sécurité veut introduire des [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] avec le moins de perturbations possibles pour les outils et le flux de travail. Les développeurs et développeuses bénévoles, y compris dans la maintenance de gadgets et scripts utilisateur, sont invités à mettre à jour le code de leurs outils pour gérer les comptes temporaires. L’équipe a [[mw:Trust and Safety Product/Temporary Accounts/For developers|créé une documentation]] expliquant comment mettre à jour le code. [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/For developers/2024-04 CTA|En savoir plus]].
'''Enquête des ''Actualités techniques'''''
* [https://wikimediafoundation.limesurvey.net/758713?lang=fr Aidez-nous à améliorer les ''Actualités techniques'' en répondant à cette courte enquête]. L’objectif est de mieux répondre aux besoins des différents types de personnes lisant les ''Actualités techniques''. L’enquête sera ouverte pendant plus d’une semaine. Elle est régie par [https://foundation.wikimedia.org/wiki/Legal:Tech_News_Survey_2024_Privacy_Statement cette déclaration de confidentialité]. Des traductions sont disponibles.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/28|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W28"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 8 juillet 2024 à 23:31 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27080357 -->
== <span lang="en" dir="ltr" class="mw-content-ltr">U4C Special Election - Call for Candidates</span> ==
<div lang="en" dir="ltr" class="mw-content-ltr">
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election/Announcement – call for candidates|You can find this message translated into additional languages on Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election/Announcement – call for candidates}}&language=&action=page&filter= {{int:please-translate}}]''
Hello all,
A special election has been called to fill additional vacancies on the U4C. The call for candidates phase is open from now through July 19, 2024.
The [[:m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the [[:foundation:Wikimedia Foundation Universal Code of Conduct|UCoC]]. Community members are invited to submit their applications in the special election for the U4C. For more information and the responsibilities of the U4C, please review the [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|U4C Charter]].
In this special election, according to [[Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter#2. Elections and Terms|chapter 2 of the U4C charter]], there are 9 seats available on the U4C: '''four''' community-at-large seats and '''five''' regional seats to ensure the U4C represents the diversity of the movement. [[Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter#5. Glossary|No more than two members of the U4C can be elected from the same home wiki]]. Therefore, candidates must not have English Wikipedia, German Wikipedia, or Italian Wikipedia as their home wiki.
Read more and submit your application on [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election|Meta-wiki]].
In cooperation with the U4C,<section end="announcement-content" />
</div>
-- [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 10 juillet 2024 à 02:02 (CEST)
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26989444 -->
== Actualités techniques n° 2024-29 ==
<section begin="technews-2024-W29"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/29|D’autres traductions]] sont disponibles.
'''Enquête des ''Actualités techniques'''''
* [https://wikimediafoundation.limesurvey.net/758713?lang=fr Aidez-nous à améliorer les ''Actualités techniques'' en répondant à cette courte enquête]. L’objectif est de mieux répondre aux besoins des différents types de personnes lisant les ''Actualités techniques''. L’enquête reste ouverte pour encore trois jours. Elle est régie par [https://foundation.wikimedia.org/wiki/Legal:Tech_News_Survey_2024_Privacy_Statement cette déclaration de confidentialité]. Des traductions sont disponibles.
'''Changements récents'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Les développeurs et développeuses Wikimedia peuvent officiellement continuer à utiliser à la fois [[mw:Special:MyLanguage/Gerrit|Gerrit]] et [[mw:Special:MyLanguage/GitLab|GitLab]], après la décision de Wikimedia Foundation du juin de prendre en charge le développement sur les deux plateformes. Gerrit et GitLab sont deux hébergeurs de code utilisés pour écrire, relire et déployer le code logiciel de MediaWiki (le logiciel central des projets wiki) et des outils annexes pour créer et améliorer le contenu. Cette décision protège la productivité de nos développeurs et évite que des problèmes de relecture du code affectent nos utilisateurs. D’autres détails sont sur la page sur [[mw:GitLab/Migration status|l’état de la migration]].
* Wikimedia Foundation recherche des candidats au [[m:Special:MyLanguage/Product and Technology Advisory Council/Proposal|Conseil consultatif Produit et Technologie]] (PTAC). Ce groupe réunira des contributeurs et contributrices techniques et Wikimedia Foundation pour définir ensemble la plateforme du futur, résiliente. Les membres du Conseil évalueront et consulteront les activités techniques et produits du mouvement, afin de développer des projets multigénérationnels. Nous recherchons une diversité de profil de contribution technique, du monde entier, venant de divers projets Wikimedia. [[m:Special:MyLanguage/Product and Technology Advisory Council/Proposal#Joining the PTAC as a technical volunteer|Candidatez ici avant le 10 aout]] !
* Les contributeurs et contributrices avec le droit de révocation qui utilisent l’appli Wikipédia Android peuvent utiliser les nouvelles fonctionnalité de [[mw:Special:MyLanguage/Wikimedia Apps/Team/Android/Anti Vandalism|révision des modifications]]. Cela inclut un nouveau flux des Modifications récentes, avec les liens liés tels que l’annulation et la révocation, et la possibilité de créer et d’enregistrer une collection personnelle de messages pour communiquer lors de la patrouille. Si votre wiki veut rendre ces fonctions disponibles pour les personnes ayant un certains nombres de modification plutôt que le statut de Patrouille, vous pouvez [[mw:Special:MyLanguage/Wikimedia Apps/Team/Android#Contact us|contacter l’équipe]]. [[diffblog:2024/07/10/ِaddressing-vandalism-with-a-tap-the-journey-of-introducing-the-patrolling-feature-in-the-mobile-app/|En savoir plus sur ce projet sur le blog ''Diff'']].
* Les contributeurs et contributrices qui ont accès à la [[m:Special:MyLanguage/The_Wikipedia_Library|Bibliothèque de Wikipédia]] peuvent à nouveau voir les contenus restreints de SpringerLinks, Wikimedia les [[phab:T368865|ayant contacté]] pour restaurer l’accès. En savoir plus sur [[m:Tech/News/Recently_resolved_community_tasks|cela et 21 autres demandes communautaires traitées la semaine dernière]].
'''Changements à venir cette semaine'''
* Cette semaine, [[mw:Special:MyLanguage/Reading/Web/Accessibility for reading/Updates/2024-07 deployments|le mode sombre sera disponible sur plusieurs Wikipédia]], sur ordinateur comme sur mobile, que l’on soit connecté ou non. Les admins d’interface et mainteneur et mainteneuse de scripts utilisateur sont encouragés à vérifier les gadgets et scripts avec le mode sombre, pour trouver des couleurs figées dans le code et les corriger. Voici [[mw:Special:MyLanguage/Recommendations for night mode compatibility on Wikimedia wikis|quelques recommandations pour la compatibilité avec le mode sombre]], si besoin.
'''Futurs changements'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] La semaine prochaine, les admins, bénévoles de maintenance d’outils et équipes de développement de logiciel sont invités à tester les [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] sur testwiki. Les comptes temporaires sont une fonctionnalité qui protège mieux la vie privée sur les wikis. Leur déploiement n’est pas encore programmé. [[mw:Talk:Trust and Safety Product/Temporary Accounts|Vos opinions et questions sont les bienvenus sur la page de discussion du projet]]. [https://phabricator.wikimedia.org/T348895]
* Si vous mettez en ligne des fichiers entre wikis, ou dites aux autres comment le faire, vous êtes invités à participer à une discussion sur Wikimedia Commons concernant l’éventuelle restriction de qui peut mettre en ligne des fichiers grâce à la boite de dialogue inter-wiki aux personnes autoconfirmées sur Commons. Cette discussion vient du grand nombre de violations du droit d’auteur par ce moyen. Un court résumé est présent sur [[c:Special:MyLanguage/Commons:Cross-wiki upload|Commons:Téléversement entre-wikis]] et [[c:Commons:Village pump/Proposals#Deactivate cross-wiki uploads for new users|les discussions sur le bistro de Commons]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/29|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' Vous pouvez aussi voir d’autres actualités dans le [[m:Special:MyLanguage/Wikimedia Foundation Bulletin|Bulletin de Wikimedia Foundation]].
</div><section end="technews-2024-W29"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 16 juillet 2024 à 03:30 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27124561 -->
== <span lang="en" dir="ltr" class="mw-content-ltr">Wikimedia Movement Charter ratification voting results</span> ==
<div lang="en" dir="ltr" class="mw-content-ltr">
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Movement Charter/Drafting Committee/Announcement - Results of the ratification vote|You can find this message translated into additional languages on Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Movement Charter/Drafting Committee/Announcement - Results of the ratification vote}}&language=&action=page&filter= {{int:please-translate}}]''
Hello everyone,
After carefully tallying both individual and affiliate votes, the [[m:Special:MyLanguage/Movement Charter/Ratification/Voting#Electoral Commission|Charter Electoral Commission]] is pleased to announce the final results of the Wikimedia Movement Charter voting.
As [[m:Special:MyLanguage/Talk:Movement Charter#Thank you for your participation in the Movement Charter ratification vote!|communicated]] by the Charter Electoral Commission, we reached the quorum for both Affiliate and individual votes by the time the vote closed on '''July 9, 23:59 UTC'''. We thank all 2,451 individuals and 129 Affiliate representatives who voted in the ratification process. Your votes and comments are invaluable for the future steps in Movement Strategy.
The final results of the [[m:Special:MyLanguage/Movement Charter|Wikimedia Movement Charter]] ratification voting held between 25 June and 9 July 2024 are as follows:
'''Individual vote:'''
Out of 2,451 individuals who voted as of July 9 23:59 (UTC), 2,446 have been accepted as valid votes. Among these, '''1,710''' voted “yes”; '''623''' voted “no”; and '''113''' selected “–” (neutral). Because the neutral votes don’t count towards the total number of votes cast, 73.30% voted to approve the Charter (1710/2333), while 26.70% voted to reject the Charter (623/2333).
'''Affiliates vote:'''
Out of 129 Affiliates designated voters who voted as of July 9 23:59 (UTC), 129 votes are confirmed as valid votes. Among these, '''93''' voted “yes”; '''18''' voted “no”; and '''18''' selected “–” (neutral). Because the neutral votes don’t count towards the total number of votes cast, 83.78% voted to approve the Charter (93/111), while 16.22% voted to reject the Charter (18/111).
'''Board of Trustees of the Wikimedia Foundation:'''
The Wikimedia Foundation Board of Trustees voted '''not to ratify''' the proposed Charter during their special Board meeting on July 8, 2024. The Chair of the Wikimedia Foundation Board of Trustees, Nataliia Tymkiv, [[m:Special:MyLanguage/Wikimedia_Foundation_Board_noticeboard/Board_resolution_and_vote_on_the_proposed_Movement_Charter|shared the result of the vote, the resolution, meeting minutes and proposed next steps]].
With this, the Wikimedia Movement Charter in its current revision is '''not ratified'''.
We thank you for your participation in this important moment in our movement’s governance.
The Charter Electoral Commission,
[[m:User:Abhinav619|Abhinav619]], [[m:User:Borschts|Borschts]], [[m:User:Iwuala Lucy|Iwuala Lucy]], [[m:User:Tochiprecious|Tochiprecious]], [[m:User:Der-Wir-Ing|Der-Wir-Ing]]<section end="announcement-content" />
</div>
[[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 18 juillet 2024 à 19:51 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26989444 -->
== Actualités techniques n° 2024-30 ==
<section begin="technews-2024-W30"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/30|D’autres traductions]] sont disponibles.
<span lang="en" dir="ltr" class="mw-content-ltr">'''Feature News'''</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">Stewards can now [[:m:Special:MyLanguage/Global_blocks|globally block]] accounts. Before [[phab:T17294|the change]] only IP addresses and IP ranges could be blocked globally. Global account blocks are useful when the blocked user should not be logged out. [[:m:Special:MyLanguage/Global_locks|Global locks]] (a similar tool logging the user out of their account) are unaffected by this change. The new global account block feature is related to the [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|Temporary Accounts]] project, which is a new type of user account that replaces IP addresses of unregistered editors that are no longer made public.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">Later this week, Wikimedia site users will notice that the Interface of [[mw:Special:MyLanguage/Extension:FlaggedRevs|FlaggedRevs]] (also known as "Pending Changes") is improved and consistent with the rest of the MediaWiki interface and [[mw:Special:MyLanguage/Codex|Wikimedia's design system]]. The FlaggedRevs interface experience on mobile and [[mw:Special:MyLanguage/Skin:MinervaNeue|Minerva skin]] was inconsistent before it was fixed and ported to [[mw:Special:MyLanguage/Codex|Codex]] by the WMF Growth team and some volunteers.</span> [https://phabricator.wikimedia.org/T191156]
* <span lang="en" dir="ltr" class="mw-content-ltr">Wikimedia site users can now submit account vanishing requests via [[m:Special:GlobalVanishRequest|GlobalVanishRequest]]. This feature is used when a contributor wishes to stop editing forever. It helps you hide your past association and edit to protect your privacy. Once processed, the account will be locked and renamed.</span> [https://phabricator.wikimedia.org/T367329]
* <span lang="en" dir="ltr" class="mw-content-ltr">Have you tried monitoring and addressing vandalism in Wikipedia using your phone? [https://diff.wikimedia.org/2024/07/10/%d9%90addressing-vandalism-with-a-tap-the-journey-of-introducing-the-patrolling-feature-in-the-mobile-app/ A Diff blog post on Patrolling features in the Mobile App] highlights some of the new capabilities of the feature, including swiping through a feed of recent changes and a personal library of user talk messages for use when patrolling from your phone.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">Wikimedia contributors and GLAM (galleries, libraries, archives, and museums) organisations can now learn and measure the impact Wikimedia Commons is having towards creating quality encyclopedic content using the [https://doc.wikimedia.org/generated-data-platform/aqs/analytics-api/reference/commons.html Commons Impact Metrics] analytics dashboard. The dashboard offers organizations analytics on things like monthly edits in a category, the most viewed files, and which Wikimedia articles are using Commons images. As a result of these new data dumps, GLAM organisation can more reliably measure their return on investment for programs bringing content into the digital Commons.</span> [https://diff.wikimedia.org/2024/07/19/commons-impact-metrics-now-available-via-data-dumps-and-api/]
'''Mises à jour de project'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Come share your ideas for improving the wikis on the newly reopened [[m:Special:MyLanguage/Community Wishlist|Community Wishlist]]. The Community Wishlist is Wikimedia’s forum for volunteers to share ideas (called wishes) to improve how the wikis work. The new version of the wishlist is always open, works with both wikitext and Visual Editor, and allows wishes in any language.</span>
'''En savoir plus'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Have you ever wondered how Wikimedia software works across over 300 languages? This is 253 languages more than the Google Chrome interface, and it's no accident. The Language and Product Localization Team at the Wikimedia Foundation supports your work by adapting all the tools and interfaces in the MediaWiki software so that contributors in our movement who translate pages and strings can translate them and have the sites in all languages. Read more about the team and their upcoming work on [https://diff.wikimedia.org/2024/07/17/building-towards-a-robust-multilingual-knowledge-ecosystem-for-the-wikimedia-movement/ Diff].</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">How can Wikimedia build innovative and experimental products while maintaining such heavily used websites? A recent [https://diff.wikimedia.org/2024/07/09/on-the-value-of-experimentation/ blog post] by WMF staff Johan Jönsson highlights the work of the [[m:Future Audiences#Objectives and Key Results|WMF Future Audience initiative]], where the goal is not to build polished products but test out new ideas, such as a [[m:Future_Audiences/Experiments: conversational/generative AI|ChatGPT plugin]] and [[m:Future_Audiences/Experiment:Add a Fact|Add a Fact]], to help take Wikimedia into the future.</span>
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/30|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' <span lang="en" dir="ltr" class="mw-content-ltr">You can also get other news from the [[m:Special:MyLanguage/Wikimedia Foundation Bulletin|Wikimedia Foundation Bulletin]].</span>
</div><section end="technews-2024-W30"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 23 juillet 2024 à 02:04 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27142915 -->
== <span lang="en" dir="ltr" class="mw-content-ltr">Vote now to fill vacancies of the first U4C</span> ==
<div lang="en" dir="ltr" class="mw-content-ltr">
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election/Announcement – voting opens|You can find this message translated into additional languages on Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election/Announcement – voting opens}}&language=&action=page&filter= {{int:please-translate}}]''
Dear all,
I am writing to you to let you know the voting period for the Universal Code of Conduct Coordinating Committee (U4C) is open now through '''August 10, 2024'''. Read the information on the [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election|voting page on Meta-wiki]] to learn more about voting and voter eligibility.
The Universal Code of Conduct Coordinating Committee (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. Community members were invited to submit their applications for the U4C. For more information and the responsibilities of the U4C, please [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|review the U4C Charter]].
Please share this message with members of your community so they can participate as well.
In cooperation with the U4C,<section end="announcement-content" />
</div>
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 27 juillet 2024 à 04:46 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=26989444 -->
== Actualités techniques n° 2024-31 ==
<section begin="technews-2024-W31"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/31|D’autres traductions]] sont disponibles.
'''Actualités des fonctionnalités'''
* Les éditeurs utilisant l'Éditeur visuel dans des langues qui utilisent des caractères non latins pour les chiffres, comme le hindi, le manipuri et l'arabe oriental, peuvent remarquer quelques changements dans la mise en forme des numéros de référence. Il s'agit d'un effet secondaire de la préparation d'une nouvelle fonctionnalité de sous-référencement, qui permettra également de corriger certains problèmes de numérotation générale dans l'Éditeur visuel. Si vous constatez des problèmes connexes sur votre wiki, veuillez partager les détails sur la [[m:Talk:WMDE Technical Wishes/Sub-referencing|page de discussion du projet]].
'''État des bugs'''
* Certains éditeurs connectés ont brièvement été incapables de modifier ou de charger des pages la semaine dernière. [[phab:T370304|Ces erreurs]] étaient principalement dues à l'ajout de nouvelles règles de [[mw:Special:MyLanguage/Help:Extension:Linter|linter]] qui ont entraîné des problèmes de mise en cache. Des correctifs ont été appliqués et les enquêtes se poursuivent.
* Les éditeurs peuvent utiliser l'[[mw:Special:MyLanguage/Trust and Safety Product/IP Info|outil d'informations sur les IP]] pour obtenir des informations sur les adresses IP. Cet outil est disponible en tant que fonctionnalité bêta dans vos préférences. Pendant quelques jours la semaine dernière, l'outil n'était pas accessible, mais il est de nouveau opérationnel. Un grand merci à Shizhao pour avoir signalé le problème. Vous pouvez en lire plus à ce sujet, et sur les [[m:Tech/News/Recently resolved community tasks#2024-07-25|28 autres tâches soumises par la communauté]] qui ont été résolues la semaine dernière.
'''Mises à jour du projet'''
* Il y a de nouvelles fonctionnalités et améliorations apportées sur Phabricator par les équipes d'ingénierie des versions et de services de collaboration, ainsi que par certains volontaires, y compris : les systèmes de recherche, le nouveau système de création de tâches, les systèmes de connexion, la configuration de traduction qui a permis le support de plus de langues (grâce à Pppery), et des corrections pour de nombreuses erreurs spécifiques. Vous pouvez [[phab:phame/post/view/316/iterative_improvements/|lire les détails concernant ces améliorations et d'autres dans ce résumé]].
* Il y a une [[mw:Special:MyLanguage/Extension:Chart/Project/Updates|mise à jour sur le projet des graphiques]]. L'équipe a décidé quelle bibliothèque de visualisation utiliser, sur quels types de graphiques se concentrer en premier et où stocker les définitions de graphiques.
* Un nouveau wiki a été créé : {{int:project-localized-name-group-wikivoyage}} en [[d:Q9056|tchèque]] ([[voy:cs:|<code>voy:cs:</code>]]) [https://phabricator.wikimedia.org/T370905]
'''En savoir plus'''
* Il y a un [[diffblog:2024/07/26/the-journey-to-open-our-first-data-center-in-south-america/|nouveau centre de données de la Wikimedia Foundation]] à São Paulo, Brésil qui aide à réduire les temps de chargement.
* Il y a de nouvelles [[diffblog:2024/07/22/the-perplexing-process-of-uploading-images-to-wikipedia/|recherches utilisateurs]] sur les problèmes liés au processus de téléchargement d'images.
* Les métriques d'impact de Commons sont [[diffblog:2024/07/19/commons-impact-metrics-now-available-via-data-dumps-and-api/|désormais disponibles]] via des dépôts de données et API.
* Le dernier bulletin trimestriel [[mw:Technical Community Newsletter/2024/July|Bulletin de la communauté technique]] est maintenant disponible.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/31|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W31"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 30 juillet 2024 à 01:10 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27164109 -->
== Actualités techniques n° 2024-32 ==
<section begin="technews-2024-W32"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/32|D’autres traductions]] sont disponibles.
'''Actualités des fonctionnalités'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Deux nouvelles fonctions de l’analyseur syntaxique seront disponibles cette semaine : <code><nowiki>{{</nowiki>[[mw:Special:MyLanguage/Help:Magic_words#dir|#dir]]<nowiki>}}</nowiki></code> et <code><nowiki>{{</nowiki>[[mw:Special:MyLanguage/Help:Magic_words#bcp47|#bcp47]]<nowiki>}}</nowiki></code>. Elles permettront de remplacer les modèles <code>Template:Dir</code> et <code>Template:BCP47</code> sur Commons et ainsi de [[phab:T343131|retirer 100 millions de lignes]] de la base de données des pages liées. Les contributeurs des wikis qui utilisent ces modèles peuvent aider en remplaçant ces modèles par ces nouvelles fonctions. Les modèles de Commons seront mis à jour pendant le hackathon de Wikimania. [https://phabricator.wikimedia.org/T359761][https://phabricator.wikimedia.org/T366623]
* Les communautés peuvent demander l’activation de l’éditeur visuel sur des espaces de noms où des discussions peuvent occasionnement avoir lieu (par exemple ''Wikipédia:'' ou ''Wikisource:'') si elles ont compris les [[mw:Special:MyLanguage/Help:VisualEditor/FAQ#WPNS|limitations connues]]. Pour les discussions, les utilisateurs peuvent déjà utiliser les [[mw:Special:MyLanguage/Help:DiscussionTools|outils de discussion]] dans ces espaces de noms.
* La catégorie de suivi "Pages utilisant une chronologie" a été renommée en "Pages utilisant l'extension EasyTimeline" [https://translatewiki.net/wiki/Special:Translations?message=MediaWiki%3ATimeline-tracking-category&namespace=8 dans TranslateWiki]. Les wikis qui ont créé la catégorie localement doivent renommer leur création locale pour correspondre.
'''Actualités des projets'''
* Les contributeurs qui participent à l’organisation de wikiprojets et d’espace de collaboration similaires sur les wikis sont invités à faire part de leurs idées et exemple de collaborations réussies avec les équipes Campagnes et Programmes. Vous pouvez répondre à [[m:Special:MyLanguage/Campaigns/WikiProjects|une courte enquête]] ou faire part de vos idées [[m:Talk:Campaigns/WikiProjects|sur la page de discussion]] Les équipes recherchent notamment des collaborations fructueuses sur des wikis non-anglophones.
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Le nouvel analyseur syntaxique va être progressivement déployé sur les wikis {{int:project-localized-name-group-wikivoyage}} dans les prochains mois. {{int:project-localized-name-enwikivoyage}} et {{int:project-localized-name-hewikivoyage}} sont [[phab:T365367|déjà passé]] à Parsoid la semaine dernière.Vous pouvez en apprendre davantage sur [[mw:Parsoid/Parser_Unification|l’unification de Parsoid et de l’analyseur syntaxique]].
'''Autres notes d’actualité'''
* Il y aura plus de 200 sessions à Wikimania cette semaine. Voici un résumé de certaines des [[diffblog:2024/08/05/interested-in-product-and-tech-here-are-some-wikimania-sessions-you-dont-want-to-miss/|principales sessions liées au domaine des produits et de la technologie]].
* Le dernier ''[[m:Special:MyLanguage/Wikimedia Foundation Bulletin/2024/07-02|Bulletin de Wikimedia Foundation]]'' est disponible.
* La dernière [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2024/July|infolettre trimestrielle du département Langues et Internationalisation]] est disponible. Sont notamment abordés : les aperçus du nouveau design des pages traduisibles ; les actualités de MinT pour le lectorat wiki ; la publication des clichés mémoire des traductions.
* La dernière [[mw:Special:MyLanguage/Growth/Newsletters/31|infolettre trimestrielle du département Croissance]] est disponible.
* Le dernier numéro mensuel du [[mw:Special:MyLanguage/MediaWiki Product Insights/Reports/July 2024|bulletin d'informations sur les produits MediaWiki]] est maintenant disponible.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/32|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W32"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 5 août 2024 à 22:43 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27233905 -->
== <span lang="en" dir="ltr" class="mw-content-ltr">Reminder! Vote closing soon to fill vacancies of the first U4C</span> ==
<div lang="en" dir="ltr" class="mw-content-ltr">
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election/Announcement – reminder to vote|You can find this message translated into additional languages on Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election/Announcement – reminder to vote}}&language=&action=page&filter= {{int:please-translate}}]''
Dear all,
The voting period for the Universal Code of Conduct Coordinating Committee (U4C) is closing soon. It is open through 10 August 2024. Read the information on [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2024_Special_Election#Voting|the voting page on Meta-wiki to learn more about voting and voter eligibility]]. If you are eligible to vote and have not voted in this special election, it is important that you vote now.
'''Why should you vote?''' The U4C is a global group dedicated to providing an equitable and consistent implementation of the UCoC. Community input into the committee membership is critical to the success of the UCoC.
Please share this message with members of your community so they can participate as well.
In cooperation with the U4C,<section end="announcement-content" />
</div>
-- [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 6 août 2024 à 17:29 (CEST)
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27183190 -->
== Actualités techniques n° 2024-33 ==
<section begin="technews-2024-W33"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/33|D’autres traductions]] sont disponibles.
'''Actualités des fonctionnalités'''
* Les personnes contribuant et maintenant des [[mw:Special:MyLanguage/Extension:AbuseFilter|filtres anti-abus]] peuvent désormais [[mw:Special:MyLanguage/Extension:AbuseFilter/Actions#Show a CAPTCHA|faire résoudre un captcha si la modification correspond à un filtre]]. Cela permet aux communautés de répondre rapidement aux pollupostages des robots. [https://phabricator.wikimedia.org/T20110]
* Les [[m:Special:MyLanguage/Stewards|stewards]] peuvent désormais spécifier si un blocage global doit également empêcher la création de compte. Avant [[phab:T17273|ce changement]] par l’équipe [[mw:Special:MyLanguage/Trust and Safety Product|Produits pour la confiance et la sécurité]], tous les blocages globaux empêchaient systématiquement la création de compte. Cela permet aux stewards de réduire les effets indésirés des blocages globaux d’adresses IP.
'''Actualités des projets'''
* Les [[wikitech:Help talk:Toolforge/Toolforge standards committee#August_2024_committee_nominations|candidatures sont ouvertes sur Wikitech]] pour mettre du nouveau dans la [[wikitech:Help:Toolforge/Toolforge standards committee|commission des normes pour Toolforge]]. Cette commission supervise notamment le [[wikitech:Help:Toolforge/Right to fork policy|règlement sur le droit à embrancher]] et [[wikitech:Help:Toolforge/Abandoned tool policy|celui sur les outils abandonnés]] de Toolforge. Les candidatures restent possibles jusqu’au 26 aout 2024.
* Un nouveau wiki a été créé : une {{int:project-localized-name-group-wikipedia}} en [[d:Q2880037|bajau de la côte occidentale]] ([[w:bdr:|<code>w:bdr:</code>]]) [https://phabricator.wikimedia.org/T371757]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/33|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W33"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 13 août 2024 à 01:21 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27253654 -->
== Bientôt : une nouvelle fonctionnalité de sous-référence – essayez-la ! ==
<section begin="Sub-referencing"/>
[[File:Sub-referencing reuse visual.png|{{#ifeq:{{#dir}}|ltr|right|left}}|400px]]
Bonjour,
Depuis plusieurs années, des membres de la communauté demandent une façon simple de réutiliser des références en précisant certains détails. Aujourd’hui, une solution pour MediaWiki arrive : la nouvelle fonctionnalité de sous-référence fonctionnera en wikicode et avec l’éditeur visuel en améliorant le système de notes de bas de page existant. Vous pourrez continuer à utiliser les différentes manières d’ajouter une note, mais vous rencontrerez surement des sous-références ajoutées par d’autres dans des articles. Plus d’informations dans [[m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing|la page du projet]].
'''Nous aimerions avoir vos retours''' pour nous assurer que cette fonctionnalité fonctionne bien pour vous :
* [[m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing#Test|essayez]] la dernière version de développement sur le wiki bêta et [[m:Talk:WMDE Technical Wishes/Sub-referencing|dites-nous ce que vous en pensez]] ;
* [[m:WMDE Technical Wishes/Sub-referencing/Sign-up|inscrivez-vous ici]] pour recevoir les actualités et invitations à participer aux études d’utilisation.
L’équipe des [[m:Special:MyLanguage/WMDE Technical Wishes|souhaits techniques]] de [[m:Special:MyLanguage/Wikimedia Deutschland|Wikimedia Allemagne]] prévoit de doter les wikis Wikimedia de cette fonctionnalité cette année. Nous contacterons auparavant les personnes créant et maintenant des outils et modèles liés aux notes de bas de page.
Votre aide est la bienvenue pour diffuser le message ! --[[m:User:Johannes Richter (WMDE)|Johannes Richter (WMDE)]] ([[m:User talk:Johannes Richter (WMDE)|talk]]) 10:36, 19 August 2024 (UTC)
<section end="Sub-referencing"/>
<!-- Message envoyé par User:Johannes Richter (WMDE)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Johannes_Richter_(WMDE)/Sub-referencing/massmessage_list&oldid=27309345 -->
== <span lang="en" dir="ltr">Tech News: 2024-34</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2024-W34"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2024/34|Translations]] are available.
'''Feature news'''
* Editors who want to re-use references but with different details such as page numbers, will be able to do so by the end of 2024, using a new [[m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing#Sub-referencing in a nutshell|sub-referencing]] feature. You can read more [[m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing|about the project]] and [[m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing#Test|how to test the prototype]].
* Editors using tracking categories to identify which pages use specific extensions may notice that six of the categories have been renamed to make them more easily understood and consistent. These categories are automatically added to pages that use specialized MediaWiki extensions. The affected names are for: [https://translatewiki.net/wiki/Special:Translations?message=MediaWiki%3Aintersection-category&namespace=8 DynamicPageList], [https://translatewiki.net/wiki/Special:Translations?message=MediaWiki%3Akartographer-tracking-category&namespace=8 Kartographer], [https://translatewiki.net/wiki/Special:Translations?message=MediaWiki%3Aphonos-tracking-category&namespace=8 Phonos], [https://translatewiki.net/wiki/Special:Translations?message=MediaWiki%3Arss-tracking-category&namespace=8 RSS], [https://translatewiki.net/wiki/Special:Translations?message=MediaWiki%3Ascore-use-category&namespace=8 Score], [https://translatewiki.net/wiki/Special:Translations?message=MediaWiki%3Awikihiero-usage-tracking-category&namespace=8 WikiHiero]. Wikis that have created the category locally should rename their local creation to match. Thanks to Pppery for these improvements. [https://phabricator.wikimedia.org/T347324]
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Advanced item]] Technical volunteers who edit modules and want to get a list of the categories used on a page, can now do so using the <code><bdi lang="zxx" dir="ltr">categories</bdi></code> property of <code><bdi lang="zxx" dir="ltr">[[mediawikiwiki:Special:MyLanguage/Extension:Scribunto/Lua reference manual#Title objects|mw.title objects]]</bdi></code>. This enables wikis to configure workflows such as category-specific edit notices. Thanks to SD001 for these improvements. [https://phabricator.wikimedia.org/T50175][https://phabricator.wikimedia.org/T85372]
'''Bugs status'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Advanced item]] Your help is needed to check if any pages need to be moved or deleted. A maintenance script was run to clean up unreachable pages (due to Unicode issues or introduction of new namespaces/namespace aliases). The script tried to find appropriate names for the pages (e.g. by following the Unicode changes or by moving pages whose titles on Wikipedia start with <code>Talk:WP:</code> so that their titles start with <code>Wikipedia talk:</code>), but it may have failed for some pages, and moved them to <bdi lang="zxx" dir="ltr">[[Special:PrefixIndex/T195546/]]</bdi> instead. Your community should check if any pages are listed there, and move them to the correct titles, or delete them if they are no longer needed. A full log (including pages for which appropriate names could be found) is available in [[phab:P67388]].
* Editors who volunteer as [[mw:Special:MyLanguage/Help:Growth/Mentorship|mentors]] to newcomers on their wiki are once again able to access lists of potential mentees who they can connect with to offer help and guidance. This functionality was restored thanks to [[phab:T372164|a bug fix]]. Thank you to Mbch331 for filing the bug report. You can read about that, and 18 other community-submitted tasks that were [[m:Tech/News/Recently resolved community tasks|resolved last week]].
'''Project updates'''
* The application deadline for the [[m:Special:MyLanguage/Product and Technology Advisory Council/Proposal|Product & Technology Advisory Council]] (PTAC) has been extended to September 16. Members will help by providing advice to Foundation Product and Technology leadership on short and long term plans, on complex strategic problems, and help to get feedback from more contributors and technical communities. Selected members should expect to spend roughly 5 hours per month for the Council, during the one year pilot. Please consider applying, and spread the word to volunteers you think would make a positive contribution to the committee.
'''Learn more'''
* The [[m:Special:MyLanguage/Coolest Tool Award#2024 Winners|2024 Coolest Tool Awards]] were awarded at Wikimania, in seven categories. For example, one award went to the ISA Tool, used for adding structured data to files on Commons, which was recently improved during the [[m:Event:Wiki Mentor Africa ISA Hackathon 2024|Wiki Mentor Africa Hackathon]]. You can see video demonstrations of each tool at the awards page. Congratulations to this year's recipients, and thank you to all tool creators and maintainers.
* The latest [[m:Special:MyLanguage/Wikimedia Foundation Bulletin/2024/08-01|Wikimedia Foundation Bulletin]] is available, and includes some highlights from Wikimania, an upcoming Language community meeting, and other news from the movement.
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2024/34|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2024-W34"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 20 août 2024 à 02:53 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27307284 -->
== Sign up for the language community meeting on August 30th, 15:00 UTC ==
Hi all,
The next language community meeting is scheduled in a few weeks—on August 30th at 15:00 UTC. If you're interested in joining, you can [https://www.mediawiki.org/wiki/Wikimedia_Language_and_Product_Localization/Community_meetings#30_August_2024 sign up on this wiki page].
This participant-driven meeting will focus on sharing language-specific updates related to various projects, discussing technical issues related to language wikis, and working together to find possible solutions. For example, in the last meeting, topics included the Language Converter, the state of language research, updates on the Incubator conversations, and technical challenges around external links not working with special characters on Bengali sites.
Do you have any ideas for topics to share technical updates or discuss challenges? Please add agenda items to the document [https://etherpad.wikimedia.org/p/language-community-meeting-aug-2024 here] and reach out to ssethi(__AT__)wikimedia.org. We look forward to your participation!
[[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 23 août 2024 à 01:18 (CEST)
<!-- Message envoyé par User:SSethi (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27183190 -->
:Hi
:# Message in english,
:# {{g|''few weeks''}} --> only one week
:# No language specific problems, but project-specific problems [[Wikilivres:Problèmes]].
:-- ◄ [[Utilisateur:DavidL|'''D'''avid '''L''']] • [[Discussion Utilisateur:DavidL|discuter]] ► 23 août 2024 à 01:35 (CEST)
== Actualités techniques n° 2024-35 ==
<section begin="technews-2024-W35"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/35|D’autres traductions]] sont disponibles.
'''Actualités des fonctionnalités'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Les administrateurs peuvent désormais tester la fonctionnalité des [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] sur test2wiki. Cela a été fait pour permettre des tests inter-wikis de comptes temporaires, lorsque ces comptes temporaires passent d'un projet à un autre. La fonctionnalité a été activée sur testwiki il y a quelques semaines. Aucun déploiement supplémentaire de comptes temporaires n'est encore prévu. Les Comptes Temporaires sont un projet visant à créer un nouveau type de compte utilisateur remplaçant les adresses IP des éditeurs non enregistrés qui ne sont plus rendues publiques. Veuillez [[mw:Talk:Trust and Safety Product/Temporary Accounts|partager vos opinions et questions sur la page de discussion du projet]].
* Plus tard cette semaine, les éditeurs des wikis qui utilisent [[mw:Special:MyLanguage/Extension:FlaggedRevs|FlaggedRevs]] (également connu sous le nom de "Modifications en attentes") peuvent remarquer que les indicateurs en haut des articles ont changé. Ce changement rend le système plus cohérent avec le reste de l'interface MediaWiki. [https://phabricator.wikimedia.org/T191156]
'''État des beugues'''
* Les éditeurs qui utilisent l'éditeur de wikitexte de 2010 et les boutons d'insertion de caractères ne rencontreront plus de problèmes avec les boutons ajoutant du contenu dans le résumé de modification au lieu de la fenêtre de modification. Vous pouvez en savoir plus à ce sujet et sur les 26 autres tâches soumises par la communauté qui ont été [[m:Tech/News/Recently resolved community tasks|résolues la semaine dernière]].
'''Actualités des projets'''
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] Allez lire et voter pour les [[m:Special:MyLanguage/Community Wishlist/Focus areas|sujets cibles]], qui sont des groupes de souhaits pour corriger un même problème. Les sujets cibles ont été créés pour la nouvelle mouture des Souhaits de la communauté qui est désormais ouverte toute l’année. Le premier lot de sujets cibles est spécifique à la routine de modération, à l’accueil des néophytes, à la minimisation des tâches répétitives et à la priorisation des tâches. Une fois que les bénévoles auront relu et voté pour les sujets cibles, Wikimedia examinera et choisira les sujets cibles à prioriser.
* Vous avez un projet et avez envie d’assister un stagiaire pour une mission de trois (3) mois ? [[mw:Special:MyLanguage/Outreachy|Outreachy]] est un programme biannuel permettant à des personnes d’être rémunérées pour un stage débutant en décembre 2024 et finissant en mars 2025 ; et ils ont besoin de tuteurs ou tutrices et de projets sur lesquels travailler. Les projets peuvent cibler du codage ou non (design, documentation, traduction, recherche). Consultez la page Outreachy pour plus d’informations ou pour voir les précédents projets depuis 2013.
'''Autres notes d’actualité'''
* Si vous êtes curieux des améliorations apportées aux produits et à la technologie par la Fondation Wikimedia l'année dernière, lisez [[diffblog:2024/08/21/wikimedia-foundation-product-technology-improving-the-user-experience/|ce résumé récent des points forts sur Diff]].
* Pour en savoir plus sur la technologie derrière les projets Wikimedia, vous pouvez maintenant regarder les sessions sur la technologique à Wikimania 2024 sur Commons. Cette semaine, regardez :
** [[c:File:Wikimania 2024 - Ohrid - Day 2 - Community Configuration - Shaping On-Wiki Functionality Together.webm|Configuration communautaire - Contruire des fonctionnalités du Wiki ensemble]] (55 mins) - à propos du projet de [[mw:Special:MyLanguage/Community Configuration|Configuration communautaire]].
** [[c:File:Wikimania 2024 - Belgrade - Day 1 - Future of MediaWiki. A sustainable platform to support a collaborative user base and billions of page views.webm|Avenir de MediaWiki. Une plateforme durable pour soutenir une base d'utilisateurs collaborative et des milliards de vues de pages]] (30 minutes) - un aperçu à la fois pour les publics techniques et non techniques, couvrant certains des défis et des questions ouvertes, liés à la recherche sur l'[[mw:MediaWiki Product Insights|évolution de la plateforme, à la gouvernance et aux expériences des développeurs]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/35|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W35"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 26 août 2024 à 22:33 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27341211 -->
== <span lang="en" dir="ltr">Announcing the Universal Code of Conduct Coordinating Committee</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
:''[https://lists.wikimedia.org/hyperkitty/list/board-elections@lists.wikimedia.org/thread/OKCCN2CANIH2K7DXJOL2GPVDFWL27R7C/ Original message at wikimedia-l]. [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election/Announcement - results|You can find this message translated into additional languages on Meta-wiki.]] [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election/Announcement - results}}&language=&action=page&filter= {{int:please-translate}}]''
Hello all,
The scrutineers have finished reviewing the vote and the [[m:Special:MyLanguage/Elections Committee|Elections Committee]] have certified the [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election/Results|results]] for the [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2024 Special Election|Universal Code of Conduct Coordinating Committee (U4C) special election]].
I am pleased to announce the following individual as regional members of the U4C, who will fulfill a term until 15 June 2026:
* North America (USA and Canada)
** Ajraddatz
The following seats were not filled during this special election:
* Latin America and Caribbean
* Central and East Europe (CEE)
* Sub-Saharan Africa
* South Asia
* The four remaining Community-At-Large seats
Thank you again to everyone who participated in this process and much appreciation to the candidates for your leadership and dedication to the Wikimedia movement and community.
Over the next few weeks, the U4C will begin meeting and planning the 2024-25 year in supporting the implementation and review of the UCoC and Enforcement Guidelines. You can follow their work on [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Meta-Wiki]].
On behalf of the U4C and the Elections Committee,<section end="announcement-content" />
</div>
[[m:User:RamzyM (WMF)|RamzyM (WMF)]] 2 septembre 2024 à 16:05 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27183190 -->
== Actualités techniques n° 2024-36 ==
<section begin="technews-2024-W36"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/36|D’autres traductions]] sont disponibles.
'''<span lang="en" dir="ltr" class="mw-content-ltr">Weekly highlight</span>'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Editors and volunteer developers interested in data visualisation can now test the new software for charts. Its early version is available on beta Commons and beta Wikipedia. This is an important milestone before making charts available on regular wikis. You can [[mw:Special:MyLanguage/Extension:Chart/Project/Updates|read more about this project update]] and help to test the charts.</span>
'''<span lang="en" dir="ltr" class="mw-content-ltr">Feature news</span>'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Editors who use the [[{{#special:Unusedtemplates}}]] page can now filter out pages which are expected to be there permanently, such as sandboxes, test-cases, and templates that are always substituted. Editors can add the new magic word [[mw:Special:MyLanguage/Help:Magic words#EXPECTUNUSEDTEMPLATE|<code dir="ltr"><nowiki>__EXPECTUNUSEDTEMPLATE__</nowiki></code>]] to a template page to hide it from the listing. Thanks to Sophivorus and DannyS712 for these improvements.</span> [https://phabricator.wikimedia.org/T184633]
* <span lang="en" dir="ltr" class="mw-content-ltr">Editors who use the New Topic tool on discussion pages, will [[phab:T334163|now be reminded]] to add a section header, which should help reduce the quantity of newcomers who add sections without a header.</span> <span lang="en" dir="ltr" class="mw-content-ltr">You can read more about that, and {{formatnum:28}} other community-submitted tasks that were [[m:Tech/News/Recently resolved community tasks|resolved last week]].</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">Last week, some Toolforge tools had occasional connection problems. The cause is still being investigated, but the problems have been resolved for now.</span> [https://phabricator.wikimedia.org/T373243]
* <span lang="en" dir="ltr" class="mw-content-ltr">Translation administrators at multilingual wikis, when editing multiple translation units, can now easily mark which changes require updates to the translation. This is possible with the [[phab:T298852#10087288|new dropdown menu]].</span>
'''<span lang="en" dir="ltr" class="mw-content-ltr">Project updates</span>'''
* <span lang="en" dir="ltr" class="mw-content-ltr">A new draft text of a policy discussing the use of Wikimedia's APIs [[m:Special:MyLanguage/API Policy Update 2024|has been published on Meta-Wiki]]. The draft text does not reflect a change in policy around the APIs; instead, it is an attempt to codify existing API rules. Comments, questions, and suggestions are welcome on [[m:Talk:API Policy Update 2024|the proposed update’s talk page]] until September 13 or until those discussions have concluded.</span>
'''<span lang="en" dir="ltr" class="mw-content-ltr">Learn more</span>'''
* <span lang="en" dir="ltr" class="mw-content-ltr">To learn more about the technology behind the Wikimedia projects, you can now watch sessions from the technology track at Wikimania 2024 on Commons. This week, check out:</span>
** <span lang="en" dir="ltr" class="mw-content-ltr">[[c:File:Wikimania 2024 - Ohrid - Day 2 - Charts, the successor of Graphs - A secure and extensible tool for data visualization.webm|Charts, the successor of Graphs - A secure and extensible tool for data visualization]] (25 mins) – about the above-mentioned Charts project.</span>
** <span lang="en" dir="ltr" class="mw-content-ltr">[[c:File:Wikimania 2024 - Ohrid - Day 3 - State of Language Technology and Onboarding at Wikimedia.webm|State of Language Technology and Onboarding at Wikimedia]] (90 mins) – about some of the language tools that support Wikimedia sites, such as [[mw:Special:MyLanguage/Content translation|Content]]/[[mw:Special:MyLanguage/Content translation/Section translation|Section Translation]], [[mw:Special:MyLanguage/MinT|MinT]], and LanguageConverter; also the current state and future of languages onboarding.</span> [https://phabricator.wikimedia.org/T368772]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/36|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W36"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 3 septembre 2024 à 03:06 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27390268 -->
== Exprimez-vous : Votez pour le Conseil d'administration 2024 ! ==
<section begin="announcement-content" />
Bonjour à tous et à toutes,
La période de vote pour l'élection du conseil d'administration de [[m:Special:MyLanguage/Wikimedia Foundation elections/2024|2024]] est maintenant ouverte. Douze (12) candidats et candidates se présentent pour quatre (4) sièges au conseil d'administration.
Apprenez-en plus sur les candidats et candidates en [[m:Special:MyLanguage/Wikimedia Foundation elections/2024/Candidates|lisant leurs déclarations]] et leurs [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2024/Questions_for_candidates|réponses aux questions de la communauté]].
Lorsque vous serez en mesure de voter, rendez-vous sur la page de vote du [[Special:SecurePoll/vote/400|SecurePoll]] pour voter. Le vote est ouvert du 3 septembre à 00:00 UTC au 17 septembre à 23:59 UTC.
Pour vérifier votre éligibilité, veuillez consulter la page [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2024/Voter_eligibility_guidelines|éligibilité de l'électeur]].
Bien à vous,
Le comité des élections et le groupe de travail sur la sélection des membres du conseil d'administration<section end="announcement-content" />
[[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 3 septembre 2024 à 14:13 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27183190 -->
== <span lang="en" dir="ltr">Tech News: 2024-37</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2024-W37"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2024/37|Translations]] are available.
'''Feature news'''
* Starting this week, the standard [[mw:Special:MyLanguage/Extension:CodeMirror|syntax highlighter]] will receive new colors that make them compatible in dark mode. This is the first of many changes to come as part of a major upgrade to syntax highlighting. You can learn more about what's to come on the [[mw:Special:MyLanguage/Help:Extension:CodeMirror|help page]]. [https://phabricator.wikimedia.org/T365311][https://phabricator.wikimedia.org/T259059]
* Editors of wikis using Wikidata will now be notified of only relevant Wikidata changes in their watchlist. This is because the Lua functions <bdi lang="zxx" dir="ltr"><code>entity:getSitelink()</code></bdi> and <bdi lang="zxx" dir="ltr"><code>mw.wikibase.getSitelink(qid)</code></bdi> will have their logic unified for tracking different aspects of sitelinks to reduce junk notifications from [[m:Wikidata For Wikimedia Projects/Projects/Watchlist Wikidata Sitelinks Tracking|inconsistent sitelinks tracking]]. [https://phabricator.wikimedia.org/T295356]
'''Project updates'''
* Users of all Wikis will have access to Wikimedia sites as read-only for a few minutes on September 25, starting at 15:00 UTC. This is a planned datacenter switchover for maintenance purposes. More information will be published in Tech News and will also be posted on individual wikis in the coming weeks. [https://phabricator.wikimedia.org/T370962]
* Contributors of [[phab:T363538#10123348|11 Wikipedias]], including English will have a new <bdi lang="zxx" dir="ltr"><code>MOS</code></bdi> namespace added to their Wikipedias. This improvement ensures that links beginning with <bdi lang="zxx" dir="ltr"><code>MOS:</code></bdi> (usually shortcuts to the [[w:en:Wikipedia:Manual of Style|Manual of Style]]) are not broken by [[w:en:Mooré|Mooré]] Wikipedia (language code <bdi lang="zxx" dir="ltr"><code>mos</code></bdi>). [https://phabricator.wikimedia.org/T363538]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2024/37|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2024-W37"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 9 septembre 2024 à 20:52 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27424457 -->
== Actualités techniques n° 2024-38 ==
<section begin="technews-2024-W38"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/38|D’autres traductions]] sont disponibles.
'''Améliorations et Maintenance'''
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] Les utilisateurs et utilisatrices de modèles peuvent apporter leur aide en lisant le champ d'action issu de la liste de souhaits le plus récent, « [[m:Special:MyLanguage/Community Wishlist/Focus areas/Template recall and discovery|Réutilisation et découverte de modèles]] », et partager leur retours en page de discussion. Ces retours aideront la l'équipe technique de la communauté à déterminer la meilleure approche pour la réalisation de ces souhaits. Tout le monde est encouragé à continuer à [[m:Special:MyLanguage/Community Wishlist|proposer de nouveaux souhaits]].
* <span class="mw-translate-fuzzy">Les éditeurs qui veulent comprendre quels [[mw:Special:MyLanguage/Help:Namespaces|espaces de noms]] existent sur chaque wiki, et quelques détails sur la façon dont ils sont configurés, peuvent utiliser la nouvelle page [[{{#special:NamespaceInfo}}]] automatisée. Merci à DannyS712 pour ces améliorations.</span> [https://phabricator.wikimedia.org/T263513]
* [[mw:Special:MyLanguage/Help:Edit check#Reference check|La vérification des références]] est une fonctionnalité qui encourage les éditeurs à ajouter une citation lorsqu’ils ajoutent un nouveau paragraphe à un article de Wikipédia. Pendant une courte période de temps, la balise « Modifier la vérification (références) activée » a été appliquée aux modifications en dehors de l’espace de noms principal. Ce problème a été résolu. [https://phabricator.wikimedia.org/T373692]
* <span lang="en" dir="ltr" class="mw-content-ltr">It is now possible for a wiki community to change the order in which a page’s categories are displayed on their wiki. By default, categories are displayed in the order they appear in the wikitext. Now, wikis with a consensus to do so can [[m:Special:MyLanguage/Requesting wiki configuration changes|request]] a configuration change to display them in alphabetical order.</span> [https://phabricator.wikimedia.org/T373480]
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] <span lang="en" dir="ltr" class="mw-content-ltr">Tool authors can now access ToolsDB's [[wikitech:Portal:Data Services#ToolsDB|public databases]] from both [[m:Special:MyLanguage/Research:Quarry|Quarry]] and [[wikitech:Superset|Superset]]. Those databases have always been accessible to every [[wikitech:Portal:Toolforge|Toolforge]] user, but they are now more broadly accessible, as Quarry can be accessed by anyone with a Wikimedia account. In addition, Quarry's internal database can now be [[m:Special:MyLanguage/Research:Quarry#Querying Quarry's own database|queried from Quarry itself]]. This database contains information about all queries that are being run and starred by users in Quarry. This information was already public through the web interface, but you can now query it using SQL.</span> <span lang="en" dir="ltr" class="mw-content-ltr">You can read more about that, and {{formatnum:20}} other community-submitted tasks that were [[m:Tech/News/Recently resolved community tasks|resolved last week]].</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">Any pages or tools that still use the very old CSS classes <bdi lang="zxx" dir="ltr"><code>mw-message-box</code></bdi> need to be updated. These old classes will be removed next week or soon afterwards. Editors can use a [https://global-search.toolforge.org/?q=mw-message-box®ex=1&namespaces=&title= global-search] to determine what needs to be changed. It is possible to use the newer <bdi lang="zxx" dir="ltr"><code>cdx-message</code></bdi> group of classes as a replacement (see [https://doc.wikimedia.org/codex/latest/components/demos/message.html#css-only-version the relevant Codex documentation], and [https://meta.wikimedia.org/w/index.php?title=Tech/Header&diff=prev&oldid=27449042 an example update]), but using locally defined onwiki classes would be best.</span> [https://phabricator.wikimedia.org/T374499]
'''Mises à jour techniques du projet'''
* La semaine prochaine, les utilisateurs de tous les wikis auront accès aux sites Wikimédia en lecture seule pendant quelques minutes. Cela commencera le 25 septembre à [https://zonestamp.toolforge.org/1727276400 15:00 UTC]. Il s’agit d’un basculement de centre de données prévu à des fins de maintenance. [[m:Special:MyLanguage/Tech/Server switch|Ce processus de maintenance vise également d’autres services.]] <span lang="en" dir="ltr" class="mw-content-ltr">The previous switchover took 3 minutes, and the Site Reliability Engineering teams use many tools to make sure that this essential maintenance work happens as quickly as possible.</span> [https://phabricator.wikimedia.org/T370962]
'''Technique en profondeur'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Le dernier numéro mensuel du [[mw:Special:MyLanguage/MediaWiki Product Insights/Reports/August 2024|bulletin d'informations sur les produits MediaWiki]] est disponible. <span lang="en" dir="ltr" class="mw-content-ltr">This edition includes details about: research about [[mw:Special:MyLanguage/Manual:Hooks|hook]] handlers to help simplify development, research about performance improvements, work to improve the REST API for end-users, and more.</span>
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Pour en apprendre plus sur la technologie derrière les projets Wikimedia, vous pouvez maintenant regarder les sessions de le volet technologique à Wikimania 2024 sur Commons. Cette semaine, regardez:
** <span lang="en" dir="ltr" class="mw-content-ltr">[[c:File:Wikimania 2024 - Auditorium Kyiv - Day 4 - Hackathon Showcase.webm|Hackathon Showcase]] (45 mins) - 19 short presentations by some of the Hackathon participants, describing some of the projects they worked on, such as automated testing of maintenance scripts, a video-cutting command line tool, and interface improvements for various tools. There are [[phab:T369234|more details and links available]] in the Phabricator task.</span>
** <span lang="en" dir="ltr" class="mw-content-ltr">[[c:File:Co-Creating a Sustainable Future for the Toolforge Ecosystem.webm|Co-Creating a Sustainable Future for the Toolforge Ecosystem]] (40 mins) - a roundtable discussion for tool-maintainers, users, and supporters of Toolforge about how to make the platform sustainable and how to evaluate the tools available there.</span>
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/38|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W38"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 17 septembre 2024 à 02:02 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27460876 -->
== Votre wiki sera bientôt en lecture seule ==
<section begin="server-switch"/><div class="plainlinks">
[[:m:Special:MyLanguage/Tech/Server switch|Lire ce message dans une autre langue]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-Tech%2FServer+switch&language=&action=page&filter= {{int:please-translate}}]
La [[foundation:|Fondation Wikimedia]] va basculer le trafic entre ses centres de données. Cela permettra de s’assurer que Wikipédia et les autres wikis de Wikimedia peuvent rester en ligne même après une catastrophe.
Le trafic sera basculé le '''{{#time:j xg|2024-09-25|fr}}'''. La bascule débutera à '''[https://zonestamp.toolforge.org/{{#time:U|2024-09-25T15:00|en}} {{#time:H:i e|2024-09-25T15:00}}]'''.
Malheureusement, en raison de certaines limites de [[mw:Special:MyLanguage/Manual:What is MediaWiki?|MediaWiki]], toutes les modifications de pages devront être arrêtées durant le passage d’un centre de données à l’autre. Nous nous excusons pour ce dérangement et nous travaillons à le minimiser dans le futur.
Une bannière sera affichée sur tous les wikis 30 minutes avant le début de l’opération. Cette bannière restera visible jusqu’à la fin de l’opération.
'''Pendant une courte période, vous pourrez lire les wikis mais pas les modifier.'''
*Vous ne pourrez pas effectuer de modification pendant une durée pouvant aller jusqu’à une heure, le {{#time:l j xg Y|2024-09-25|fr}}.
*Si vous essayez de faire une modification ou de sauvegarder pendant cette période, vous verrez un message d’erreur. Nous espérons qu’aucune modification ne sera perdue durant ce temps, mais nous ne pouvons le garantir. Si vous voyez un message d’erreur, merci de patienter jusqu’au retour à la normale. Vous pourrez alors enregistrer votre modification. Mais nous vous conseillons de faire une copie de votre modification avant, au cas où.
''Autres conséquences :''
*Les tâches de fond seront ralenties et certaines pourraient être stoppées. Les liens rouges ne seront pas mis à jour aussi vite que d’habitude. Si vous créez un article qui est déjà lié depuis une autre page, le lien rouge pourrait rester rouge plus longtemps que d’habitude. Certains scripts ayant un long temps d’exécution devront être stoppés.
* Nous espérons que le déploiement de code se passe comme chaque semaine. Cependant, certains codes particuliers pourraient être gelés si l’opération le nécessitait.
* [[mw:Special:MyLanguage/GitLab|GitLab]] sera indisponible durant environ 90 minutes.
Ce projet pourra être reporté si nécessaire. Vous pouvez [[wikitech:Switch_Datacenter|consulter le calendrier sur wikitech.wikimedia.org]]. Tout changement sera annoncé dans le calendrier.
'''Merci de partager ces informations avec votre communauté.'''</div><section end="server-switch"/>
[[User:Trizek_(WMF)|Trizek_(WMF)]], 20 septembre 2024 à 11:37 (CEST)
<!-- Message envoyé par User:Trizek (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=27248326 -->
== Actualités techniques n° 2024-39 ==
<section begin="technews-2024-W39"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/39|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Tous les wikis seront [[m:Special:MyLanguage/Tech/Server switch|en lecture seule]] pendant quelques minutes le mercredi 25 septembre à [https://zonestamp.toolforge.org/1727276400 15 h UTC]. La lecture sera toujours possible, mais pas la modification. Ces procédures biannuelles permettent aux équipes de Wikimedia responsables de la fiabilité des sites de rester prêtes à intervenir pour garder les wikis fonctionnels en cas de panne d’un de nos centres de données.
'''Actualités pour la contribution'''
[[File:Add alt text from a halfsheet, with the article behind.png|thumb|Capture d’écran de l’interface de la fonctionnalité de suggestion de modification pour les textes alternatifs (alt).]]
* Les contributeurs et contributrices qui utilisent l’appli Wikipédia pour iOS en espagnol, portugais, français ou chinois peuvent voir [[mw:Special:MyLanguage/Wikimedia Apps/iOS Suggested edits project/Alt Text Experiment|l’expérimentation de la suggestion de modification pour les textes alternatifs]] après la modification d’un article ou la réalisation d’une modification suggérée « [[mw:Special:MyLanguage/Wikimedia Apps/iOS Suggested edits project#Hypothesis 2 Add an Image Suggested Edit|Ajoutez une image]] ». Le texte alternatif aide les personnes malvoyantes à lire les articles Wikipédia. L’équipe cherche à savoir si l’ajout de textes alternatifs aux images est une tâche que des novices peuvent correctement réaliser. Vos retours sont les bienvenus sur [[mw:Talk:Wikimedia Apps/iOS Suggested edits project/Alt Text Experiment|la page de discussion]].
* La palette de couleurs de Codex a été mise à jour avec les changements de couleurs des interfaces utilisateur de MediaWiki. Les [[mw:Special:MyLanguage/Design System Team/Color/Design documentation#Updates|changements les plus notables]] pour la contribution concernent les couleurs du mode sombre pour les liens et pour les boutons de calme (progressifs et destructeurs), les couleurs des liens visités dans les modes sombres et lumineux, et les couleurs d’arrière-plan des messages système dans les modes sombres et lumineux.
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Il est désormais possibles d’inclure des liens cliquables internes et externes dans les blocs de code. Cela concerne les liens utilisés dans les balises <code><nowiki><syntaxhighlight></nowiki></code> sur les pages de code (JavaScript, CSS, Scribunto et CSS assaini). L’utilisation de la syntaxe de modèle <code><nowiki>{{…}}</nowiki></code> crée aussi un lien vers la page du modèle. Merci à SD0001 pour ces améliorations. [https://phabricator.wikimedia.org/T368166]
* Deux beugues ont été corrigés dans le système [[m:Special:MyLanguage/Account vanishing|GlobalVanishRequest]] en améliorant la journalisation et en supprimant un message de remplacement inapproprié. [https://phabricator.wikimedia.org/T370595][https://phabricator.wikimedia.org/T372223]
* Voir {{PLURAL:25|la tâche soumise|les {{formatnum:25}} tâches soumises}} par la communauté [[m:Tech/News/Recently resolved community tasks|résolue{{PLURAL:25||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Sur [[m:Special:MyLanguage/Wikimedia Enterprise|Wikimedia Entreprise]] :
** L’API permet désormais de soumettre {{formatnum:5000}} requêtes API par mois et de récupérer des images instantanées HTML deux fois par mois gratuitement et librement. D’autres actualités, notamment l’amélioration de la trousse de développement logiciel (SDK) sont présentées sur [https://enterprise.wikimedia.com/blog/enhanced-free-api/ le billet de blog du projet]. Même si les API de Wikimedia Entreprise sont conçues pour des réutilisations commerciales à haut volume, ce changement permet que le service soit utilisé pour de nombreux usages communautaires.
** L’API des images instantanées (copies HTML) a été dotée en bêta de points finaux Contenus structurés ([https://enterprise.wikimedia.com/blog/structured-contents-snapshot-api/ article de blog]) et de deux jeux de données bêta (Wikipédia en anglais et en français) depuis ce point final vers Hugging Face pour utilisation publique et recueil des retours ([https://enterprise.wikimedia.com/blog/hugging-face-dataset/ article de blog]). Ces jeux de données pré-analysés permet de nouvelles options pour la recherche, le développement et l’analyse de données.
'''En détails'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Le Service de requêtes de Wikidata (WDQS) est utilisé pour obtenir des réponses aux questions en utilisant le jeu de données de Wikidata. Comme Wikidata grandissait, nous devions effectuer un changement majeur d’architecture pour que WDQS puissent rester performant. Dans le cadre du [[d:Special:MyLanguage/Wikidata:SPARQL query service/WDQS graph split|projet Séparation des graphes de WDQS]], nous avons défini de nouveaux points finaux QPARQL pour servir les sous-graphes « [https://query-scholarly.wikidata.org érudit] » et « [https://query-main.wikidata.org principal] » de Wikidata. Le [http://query.wikidata.org point d’accès terminal query.wikidata.org] continuera de servir le graphe Wikidata entier jusqu’à mars 2025. Après cette date, il ne servira plus que le graphe principal. Pour plus d’informations, lisez [[d:Special:MyLanguage/Wikidata:SPARQL query service/WDQS backend update/September 2024 scaling update|l’annonce sur Wikidata]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/39|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W39"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 24 septembre 2024 à 01:36 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27493779 -->
== 'Wikidata item' link is moving. Find out where... ==
<div lang="en" dir="ltr" class="mw-content-ltr"><i>Apologies for cross-posting in English. Please consider translating this message.</i>{{tracked|T66315}}
Hello everyone, a small change will soon be coming to the user-interface of your Wikimedia project.
The [[d:Q16222597|Wikidata item]] [[w:|sitelink]] currently found under the <span style="color: #54595d;"><u>''General''</u></span> section of the '''Tools''' sidebar menu will move into the <span style="color: #54595d;"><u>''In Other Projects''</u></span> section.
We would like the Wiki communities feedback so please let us know or ask questions on the [[m:Talk:Wikidata_For_Wikimedia_Projects/Projects/Move_Wikidata_item_link|Discussion page]] before we enable the change which can take place October 4 2024, circa 15:00 UTC+2.
More information can be found on [[m:Wikidata_For_Wikimedia_Projects/Projects/Move_Wikidata_item_link|the project page]].<br><br>We welcome your feedback and questions.<br> [[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 27 septembre 2024 à 20:58 (CEST)
</div>
<!-- Message envoyé par User:Danny Benjafield (WMDE)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Danny_Benjafield_(WMDE)/MassMessage_Test_List&oldid=27524260 -->
== Actualités techniques n° 2024-40 ==
<section begin="technews-2024-W40"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/40|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les lecteurs de [[phab:T375401|42 autres wikis]] peuvent maintenant utiliser le mode sombre. Si l’option n’est pas encore disponible pour les utilisateurs non connectés de votre wiki, c’est probablement parce que de nombreux modèles ne s’affichent pas encore correctement en mode sombre. Utilisez l’outil [https://night-mode-checker.wmcloud.org/ night-mode-checker] (« Vérificateur du mode nuit ») si vous avez envie d’aider à résoudre ces problèmes. La [[mw:Special:MyLanguage/Recommendations for night mode compatibility on Wikimedia wikis|page de recommandations]] fournit des conseils à ce sujet. Le mode sombre est activé sur de nouveaux wikis une fois par mois.
* Les contributeurs qui utilisent l’éditeur de wikicode 2010 par défaut peuvent accéder aux fonctions de l’éditeur de wikicode 2017 en ajoutant <code dir=ltr>?veaction=editsource</code> à l’URL. Si vous souhaitez activer l’éditeur 2017 par défaut, cela se fait dans [[Special:Preferences#mw-input-wpvisualeditor-newwikitext|vos préférences]]. [https://phabricator.wikimedia.org/T239796]
* Pour le lectorat non-connecté utilisant l’habillage Vector 2022, le lien « Donner » a été déplacé du menu déroulant à côté de la zone du contenu vers un menu supérieur plus visible, à côté de « Créer un compte ». Il retrouve la visibilité qu’il avait dans l’habillage Vector 2010. Vous pouvez en savoir plus sur [[mw:Readers/2024 Reader and Donor Experiences#Donor Experiences (Key Result WE 3.2 and the related hypotheses)|les changements concernant l’expérience de don]]. [https://phabricator.wikimedia.org/T373585]
* L’extension CampaignEvents (« Campagnes évènementielles ») fournit des outils pour les équipes d’organisation afin de gérer plus facilement des évènements, communiquer avec les participants et promouvoir leurs évènements sur les wikis. L’extension a été activée sur les Wikipédia en arabe, igbo et swahili, ainsi que sur Méta-Wiki. La [[w:zh:Wikipedia:互助客栈/其他#引進CampaignEvents擴充功能|Wikipédia en chinois a décidé]] d’activer l’extension, des discussions ont lieu [[w:es:Wikipedia:Votaciones/2024/Sobre la política de Organizadores de Eventos|sur la Wikipédia en espagnol]] et [[d:Wikidata:Project chat#Enabling the CampaignEvents Extention on Wikidata|sur Wikidata]]. Pour savoir comment activer l’extension sur votre wiki, visitez [[m:Special:MyLanguage/CampaignEvents|la page CampaignEvents sur Méta-Wiki]].
* Voir {{PLURAL:22|la tâche soumise|les {{formatnum:22}} tâches soumises}} par la communauté [[m:Tech/News/Recently resolved community tasks|résolue{{PLURAL:22||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les développeurs et développeuses ayant un compte sur le wiki Wikiteh devraient [[wikitech:Wikitech/SUL-migration|vérifier si une action est nécessaire]] pour leur compte. Le wiki est en train de changer pour utiliser le système de connexion par utilisateur unique (SUL) et d’autres changements de configuration. Ce changement va réduire la complexité générale des mises à jour hebdomadaires sur tous nos wikis.
'''En détails'''
* L’[[m:Special:MyLanguage/Tech/Server switch|interversion de serveurs]] s’est terminée avec succès la semaine dernière, avec un blocage des modifications pendant [[wikitech:Switch Datacenter#Past Switches|seulement 2 minutes 46 secondes]]. Cette procédure périodique garantit que nos ingénieurs savent passer d’un centre de donnée à l’autre en gardant tous les wikis accessibles au lectorat, même en cas de problème technique majeur. Cela permet aussi de faire des opérations de maintenance sur les systèmes qui tournent normalement 24 heures sur 24, et cela révèle souvent les éventuelles faiblesses de l’infrastructure. Le processus implique des dizaines de services logiciels et des centaines de serveurs matériel, et nécessite que de multiples équipes collaborent. Le travail de ces dernières années a réduit le temps de lecture seule de 17 minutes à 2-3 minutes. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/66ZW7B2MG63AESQVTXDIFQBDBS766JGW/]
'''Rencontres et évènements'''
* 4 au 6 octobre : [[m:Special:MyLanguage/WikiIndaba conference 2024|Hackathon de la Conférence WikiIndaba]] à Johannesburg, en Afrique du Sud ;
* 4 au 6 novembre : [[mw:Special:MyLanguage/MediaWiki Users and Developers Conference Fall 2024|Conférence de l’utilisation et du développement de MediaWiki de l’automne 2024]] à Vienne, en Autriche.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/40|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W40"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 1 octobre 2024 à 00:19 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27530062 -->
== Invitation to Participate in Wiki Loves Ramadan Community Engagement Survey ==
Dear all,
Apologies for writing in English. Please help to translate in your language.
We are excited to announce the upcoming [[m:Wiki Loves Ramadan|Wiki Loves Ramadan]] event, a global initiative aimed at celebrating Ramadan by enriching Wikipedia and its sister projects with content related to this significant time of year. As we plan to organize this event globally, your insights and experiences are crucial in shaping the best possible participation experience for the community.
To ensure that Wiki Loves Ramadan is engaging, inclusive, and impactful, we kindly invite you to participate in our community engagement survey. Your feedback will help us understand the needs of the community, set the event's focus, and guide our strategies for organizing this global event.
Survey link: https://forms.gle/f66MuzjcPpwzVymu5
Please take a few minutes to share your thoughts. Your input will make a difference!
Thank you for being a part of our journey to make Wiki Loves Ramadan a success.
Warm regards,
User:ZI Jony 6 octobre 2024 à 05:19 (CEST)
Wiki Loves Ramadan Organizing Team
<!-- Message envoyé par User:ZI Jony@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=27510935 -->
== Actualités techniques n° 2024-41 ==
<section begin="technews-2024-W41"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/41|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Les communautés peuvent désormais demander l’installation d’[[mw:Special:MyLanguage/Moderator Tools/Automoderator|Automoderator]] sur leur wiki. Automoderator est un outil automatisé de lutte contre le vandalisme qui annule les mauvaises modifications en se basant sur leur notation attribuée par le nouveau modèle d’apprentissage automatique « Risque d’annulation ». Vous pouvez [[mw:Special:MyLanguage/Extension:AutoModerator/Deploying|lire les étapes à suivre]] pour l’installation et la configuration. [https://phabricator.wikimedia.org/T336934]
'''Actualités pour la contribution'''
* Les traducteurs et traductrices sur les wikis où [[mw:Special:MyLanguage/Content translation/Section translation#Try the tool|l’expérience mobile de l’Outil de traduction de contenu est disponible]] peuvent désormais personnaliser leurs suggestions d’articles grâce à 41 choix de filtrage. Cette fonctionnalité de suggestion d’article en fonction du sujet facilite la découverte d’articles à traduire selon les centres d’intérêts. Vous pouvez [https://test.wikipedia.org/w/index.php?title=Special:ContentTranslation&active-list=suggestions l’essayer sur votre appareil mobile]. [https://phabricator.wikimedia.org/T368422]
* Voir {{PLURAL:12|la tâche soumise|les {{formatnum:12}} tâches soumises}} par la communauté [[m:Tech/News/Recently resolved community tasks|résolue{{PLURAL:12||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Il est désormais possible pour les blocs de code <bdi lang="zxx" dir="ltr"><code><nowiki><syntaxhighlight></nowiki></code></bdi> de proposer un bouton « Copier » au lectorat en [[mw:Special:MyLanguage/Extension:SyntaxHighlight#copy|définissant l’attribut]] <bdi lang="zxx" dir="ltr"><code><nowiki>copy=1</nowiki></code></bdi> sur la balise. Merci à SD0001 pour cette amélioration. [https://phabricator.wikimedia.org/T40932]
* Les messages personnalisés en pied de page concernant le droit d’auteur vont être actualisés sur tous les wikis. La nouvelle version utilisera du wikicode, au lieu de nécessiter une mise en forme en HTML. [https://phabricator.wikimedia.org/T375789]
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Ce mois-ci, les [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] vont faire leur apparition sur plusieurs wikis pilotes. La liste définitive des wikis sera publiée pendant la deuxième quinzaine du mois. Si vous maintenez un outil, robot ou gadget sur un de [[phab:T376499|ces 11 wikis]] et que votre logiciel utilise des données concernant les adresses IP ou est disponible pour les utilisateurs non-connectés, vérifiez si une modification doit être apportée pour fonctionner avec les comptes temporaires. Un [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/For developers|guidage pour la mise à jour du code]] est disponible.
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Une limite d’accès a été activée pour les outils de revue de code [[Wikitech:Gerrit|Gerrit]] et [[Wikitech:GitLab|GitLab]] pour répondre aux problèmes en cours causés par du trafic et moissonnage malveillants. Les clients qui ouvrent trop de connexions simultanées seront restreints pendant quelques minutes. Cette limitation est gérée avec des règles de parefeu [[Wikitech:nftables|nftables]]. Des détails sont sur Wikitech concernant le [[Wikitech:Firewall#Throttling with nftables|parefeu]], les [[Wikitech:GitLab/Abuse and rate limiting|limites dans GitLab]] et les [[Wikitech:Gerrit/Operations#Throttling IPs|opérations de Gerrit]].
* Cinq nouveaux wikis ont été créés :
** une {{int:project-localized-name-group-wikipedia}} en [[d:Q49224|komering]] ([[w:kge:|<code>w:kge:</code>]]) [https://phabricator.wikimedia.org/T374813]
** une {{int:project-localized-name-group-wikipedia}} en [[d:Q36096|mooré]] ([[m:mos:|<code>m:mos:</code>]]) [https://phabricator.wikimedia.org/T374641]
** un {{int:project-localized-name-group-wiktionary}} en [[d:Q36213|madurais]] ([[wikt:mad:|<code>wikt:mad:</code>]]) [https://phabricator.wikimedia.org/T374968]
** une {{int:project-localized-name-group-wikiquote}} en [[d:Q2501174|gorontalo]] ([[q:gor:|<code>q:gor:</code>]]) [https://phabricator.wikimedia.org/T375088]
** une {{int:project-localized-name-group-wikinews}} en [[d:Q56482|shan]] ([[n:shn:|<code>n:shn:</code>]]) [https://phabricator.wikimedia.org/T375430]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/41|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W41"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 8 octobre 2024 à 01:42 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27557422 -->
== Résultats préliminaires des élections de 2024 du conseil d'administration de la Fondation Wikimédia ==
<section begin="announcement-content" />
Bonjour,
Merci à tous ceux qui ont participé à [[m:Special:MyLanguage/Wikimedia Foundation elections/2024|l'élection de 2024 du conseil d'administration de la Fondation Wikimédia]]. Près de 6000 membres de la communauté issus de plus de 180 projets wiki ont voté.
Les quatre candidats suivants ont obtenu le plus de voix:
# [[User:Kritzolina|Christel Steigenberger]]
# [[User:Nadzik|Maciej Artur Nadzikiewicz]]
# [[User:Victoria|Victoria Doronina]]
# [[User:Laurentius|Lorenzo Losa]]
Bien que ces candidats aient été classés par nombre de votes, ils doivent encore être nommés au conseil d'administration. Ils doivent réussir une vérification des antécédents et satisfaire aux qualifications décrites dans les statuts. Les nouveaux membres seront nommés lors de la prochaine réunion du conseil en décembre 2024.
[[m:Special:MyLanguage/Wikimedia_Foundation_elections/2024/Results|Vous pouvez en savoir plus sur les résultats sur Méta-Wiki.]]
Bien à vous,
Le comité des élections et le groupe de travail sur la sélection des membres du conseil d'administration
<section end="announcement-content" />
[[User:MPossoupe_(WMF)|MPossoupe_(WMF)]] 14 octobre 2024 à 10:24 (CEST)
<!-- Message envoyé par User:MPossoupe (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27183190 -->
== <span lang="en" dir="ltr">Tech News: 2024-42</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2024-W42"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2024/42|Translations]] are available.
'''Updates for editors'''
* The Structured Discussion extension (also known as Flow) is starting to be removed. This extension is unmaintained and causes issues. It will be replaced by [[mw:Special:MyLanguage/Help:DiscussionTools|DiscussionTools]], which is used on any regular talk page. [[mw:Special:MyLanguage/Structured Discussions/Deprecation#Deprecation timeline|A first set of wikis]] are being contacted. These wikis are invited to stop using Flow, and to move all Flow boards to sub-pages, as archives. At these wikis, a script will move all Flow pages that aren't a sub-page to a sub-page automatically, starting on 22 October 2024. On 28 October 2024, all Flow boards at these wikis will be set in read-only mode. [https://www.mediawiki.org/wiki/Structured_Discussions/Deprecation][https://phabricator.wikimedia.org/T370722]
* WMF's Search Platform team is working on making it easier for readers to perform text searches in their language. A [[phab:T332342|change last week]] on over 30 languages makes it easier to find words with accents and other diacritics. This applies to both full-text search and to types of advanced search such as the <bdi lang="en" dir="ltr">''hastemplate''</bdi> and <bdi lang="en" dir="ltr">''incategory''</bdi> keywords. More technical details (including a few other minor search upgrades) are available. [https://www.mediawiki.org/wiki/User:TJones_%28WMF%29/Notes/Language_Analyzer_Harmonization_Notes#ASCII-folding/ICU-folding_%28T332342%29]
* View all {{formatnum:20}} community-submitted {{PLURAL:20|task|tasks}} that were [[m:Tech/News/Recently resolved community tasks|resolved last week]]. For example, [[mw:Special:MyLanguage/Help:Edit check|EditCheck]] was installed at Russian Wikipedia, and fixes were made for some missing user interface styles.
'''Updates for technical contributors'''
* Editors who use the Toolforge tool [[toolforge:copyvios|Earwig's Copyright Violation Detector]] will now be required to log in with their Wikimedia account before running checks using the "search engine" option. This change is needed to help prevent external bots from misusing the system. Thanks to Chlod for these improvements. [https://en.wikipedia.org/wiki/Wikipedia_talk:New_pages_patrol/Reviewers#Authentication_is_now_required_for_search_engine_checks_on_Earwig's_Copyvio_Tool]
* [[m:Special:MyLanguage/Phabricator|Phabricator]] users can create tickets and add comments on existing tickets via Email again. [[mw:Special:MyLanguage/Phabricator/Help#Using email|Sending email to Phabricator]] has been fixed. [https://phabricator.wikimedia.org/T356077]
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Advanced item]] Some HTML elements in the interface are now wrapped with a <code><nowiki><bdi></nowiki></code> element, to make our HTML output more aligned with Web standards. More changes like this will be coming in future weeks. This change might break some tools that rely on the previous HTML structure of the interface. Note that relying on the HTML structure of the interface is [[mw:Special:MyLanguage/Stable interface policy/Frontend#What is not stable?|not recommended]] and might break at any time. [https://phabricator.wikimedia.org/T375975]
'''In depth'''
* The latest monthly [[mw:Special:MyLanguage/MediaWiki Product Insights/Reports/September 2024|MediaWiki Product Insights newsletter]] is available. This edition includes: updates on Wikimedia's authentication system, research to simplify feature development in the MediaWiki platform, updates on Parser Unification and MathML rollout, and more.
* The latest quarterly [[mw:Technical Community Newsletter/2024/October|Technical Community Newsletter]] is now available. This edition include: research about improving topic suggestions related to countries, improvements to PHPUnit tests, and more.
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2024/42|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2024-W42"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 14 octobre 2024 à 23:20 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27597254 -->
== À la recherche de bénévoles pour rejoindre plusieurs comités du mouvement ==
<section begin="announcement-content" />
Chaque année, généralement d’octobre à décembre, plusieurs comités du mouvement cherchent de nouveaux bénévoles.
Pour en savoir plus sur les comités, consultez leurs pages Meta-wiki :
* [[m:Special:MyLanguage/Affiliations_Committee|Comité des affiliations (AffCom)]]
* [[m:Special:MyLanguage/Ombuds_commission|Commission de médiation (OC)]]
* [[m:Special:MyLanguage/Wikimedia Foundation/Legal/Community Resilience and Sustainability/Trust and Safety/Case Review Committee|Comité d'examen des cas (CRC)]]
Les candidatures aux comités sont ouvertes à partir du 16 octobre 2024. Les candidatures au Comité des affiliations sont closes le 18 novembre 2024, et les candidatures à la commission de médiation et au Comité d'examen des cas sont closes le 2 décembre 2024. Pour savoir comment postuler, [[m:Special:MyLanguage/Wikimedia_Foundation/Legal/Committee_appointments|consultez la page de nomination sur Meta-wiki]]. Publiez sur la page de discussion ou envoyez un e-mail à [mailto:cst@wikimedia.org cst@wikimedia.org] pour toute question que vous pourriez avoir.
Pour l'équipe de soutien du comité,
<section end="announcement-content" />
-- [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17 octobre 2024 à 01:07 (CEST)
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27601062 -->
== Actualités techniques n° 2024-43 ==
<section begin="technews-2024-W43"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/43|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* L’équipe Applis mobile a sorti [[mw:Special:MyLanguage/Wikimedia Apps/Team/iOS/Navigation Refresh#Phase 1: Creating a user Profile Menu (T373714)|une mise à jour]] de la navigation de l’appli iOS, désormais disponible dans la dernière version sur l’AppStore. L’équipe a ajouté un nouveau menu Profile qui permets un accès facile aux fonctions de contribution telles que les notifications ou la liste de suivi depuis la vue Article. Le bouton « Donner » est aussi plus accessible lors de la lecture d’un article. Ces changements constituent la première phase du [[mw:Special:MyLanguage/Wikimedia Apps/Team/iOS/Navigation Refresh|rafraichissement de la navigation]] en cours, qui vise à passer d’une appli centrée sur la lecture à une appli qui prend en charge entièrement la contribution. Wikimedia Foundation a ajouté d’autres fonction pour contribuer et communiquer sur le wiki, à partir des demandes ces dernières années.
[[File:IOS App Navigation refresh first phase 05.png|thumb|Contenu et menu Profil dans l’appli Wikipedia sur iOS.]]
'''Actualités pour la contribution'''
* Le lectorat de Wikipédia peut désormais télécharger une extension de navigateur pour tester certaines idées de fonctionnalités potentielles (recommandations d’articles liés, résumés automatique d’articles, amélioration de la recherche). Pour plus de détails et être informé{{GENDER:||e}} des actus, consultez la page [[mw:Special:MyLanguage/Reading/Web/Content Discovery Experiments|Expérimentation de la découverte de contenus]] de l’équipe Web, et [[mw:Special:MyLanguage/Newsletter:Web team's projects|abonnez-vous à leur infolettre]].
* Ce mois-ci, les contributeurs non-connectés de [[phab:T376499|douze wikis]] commenceront à avoir des [[mw:Special:Mylanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] créés. Certains wikis seront peut-être retirés de l’expérimentation. Les comptes temporaires constituent un nouveau [[mw:Special:Mylanguage/Trust and Safety Product/Temporary Accounts|type de compte utilisateur]] qui protège mieux la vie privée des contributeurs non-connectés et facilite la communication entre la communauté et eux. Si vous maintenez un outil, robot ou gadget sur ces douze wikis et que votre logiciel utilisent des données sur les adresses IP ou est disponible pour les utilisateurs non-connectés, vérifiez si une mise à jour doit être faite pour fonctionner avec les comptes temporaires. Un [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/For developers|guidage sur la jour du code]] est disponible. Vous pouvez consulter le [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Updates|calendrier de déploiement sur tous les wikis]].
* Voir {{PLURAL:33|la tâche soumise|les {{formatnum:33}} tâches soumises}} par la communauté [[m:Tech/News/Recently resolved community tasks|résolue{{PLURAL:33||s}} la semaine dernière]]. Par exemple, les Wikipédia en [[w:nr:Main Page|ndébélé du sud]], [[w:rsk:Главни бок|ruthène de Pannonie]], [[w:ann:Uwu|obolo]], [[w:iba:Lambar Keterubah|iban]] et [[w:tdd:ᥞᥨᥝᥴ ᥘᥣᥲ ᥖᥥᥰ|tai nüa]] ont été créées cette semaine. [https://www.wikidata.org/wiki/Q36785][https://www.wikidata.org/wiki/Q35660][https://www.wikidata.org/wiki/Q36614][https://www.wikidata.org/wiki/Q33424][https://www.wikidata.org/wiki/Q36556]
* Il est désormais possible de créer des fonctions sur Wikifunctions en utilisant des lexèmes de Wikidata avec le nouveau [[f:Z6005|type <i lang="en">Wikidata lexeme</i>]] apparu cette semaine. Quand vous visitez une de ces fonctions, l’interface propose un sélecteur de lexème qui permet de choisir un lexème sur Wikidata qui correspond au mot que vous saisissez. Lors de l’exécution, le lexème choisi est récupéré sur Wikidata, transtypé en <i lang="en">Wikidata lexeme</i> et transmis à la fonction choisie. Lisez [[f:Special:MyLanguage/Wikifunctions:Status updates/2024-10-17#Function of the Week: select representation from lexeme|la dernière infolettre de Wikifunctions]] pour plus d’info.
'''Actualités pour la contribution technique'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Les utilisateurs des sites Wikimedia peuvent désormais mettre en forme les dates plus facilement dans différentes langues avec la nouvelle fonction d’analyse <code dir="ltr">{{[[mw:Special:MyLanguage/Help:Extension:ParserFunctions##timef|#timef]]:…}}</code>. Par exemple, <code dir="ltr"><nowiki>{{#timef:now|date|en}}</nowiki></code> affichera « <bdi lang="en" dir="ltr">{{#timef:now|date|en}}</bdi> ». Auparavant, <code dir="ltr"><nowiki>{{#time:…}}</nowiki></code> pouvait être utilisé pour mettre en forme des dates, mais il fallait connaitre l’ordre des composants de la date et heure, ainsi que les règles de ponctuation. <code dir="ltr">#timef</code> (ou <code dir="ltr">#timefl</code> pour l’heure locale) fournit un accès aux formats de date standards utilisés par MediaWiki dans l’interface utilisateur. Cela peut aider à simplifier des modèles sur les wikis multilingues tels que Commons et Méta-Wiki. [https://phabricator.wikimedia.org/T223772][https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Extension:ParserFunctions##timef]
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Les utilisateurs de Commons et de Meta-Wiki peuvent désormais efficacement [[mw:Special:MyLanguage/Help:Magic words#Localization|obtenir la langue du lecteur ou lectrice]] en utilisant <code dir="ltr"><nowiki>{{USERLANGUAGE}}</nowiki></code> au lieu de <code dir="ltr"><nowiki>{{int:lang}}</nowiki></code>. [https://phabricator.wikimedia.org/T4085]
* [[m:Special:MyLanguage/Product and Technology Advisory Council|Le Conseil consultatif pour les produits et technologies]] (PTAC) a désormais son comité de pilotage, avec des représentants issus d’Afrique, Asie, Europe, Amérique du Nord et Amérique du Sud. Ils travailleront sur le projet du [[Special:MyLanguage/Movement Strategy/Initiatives/Technology Council|« Conseil sur les technologies » de la Stratégie du Mouvement]] qui vise à avoir une plateforme technologique définie conjointement et plus robuste. [https://meta.wikimedia.org/wiki/Movement_Strategy/Initiatives/Technology_Council]
'''En détails'''
* La dernière [[mw:Special:MyLanguage/Growth/Newsletters/32|infolettre Croissance]] est disponible. Elle aborde : l’arrivée d’un module Actualités de la communauté sur la page d’accueil des nouveaux, les nouvelles options pour la Configuration de la communauté, de nouveaux projets.
* Wikimedia Foundation est [[mw:Special:MyLanguage/Wikimedia Security Team#CNA Partnership|désormais un partenaire officiel du programme CVE]], un travail international de catalogage des vulnérabilités publiquement dévoilées. Ce partenariat permettra à l’équipe Sécurité de publier instantanément les [[w:fr:Common Vulnerabilities and Exposures|vulnérabilités et expositions communes]] (CVE) qui concernent MediaWiki ou ses extensions et habillages, ou d’autres codes auxquels contribue Wikimedia.
* Les [[m:Special:MyLanguage/Community Wishlist|Souhaits communautaires]] testent désormais [[m:Community Wishlist/Updates#October 16, 2024: Conversations Made Easier: Machine-Translated Wishes Are Here!|la traduction automatique des souhaits]]. Des versions traduites automatiquement des souhaits sont désormais lisibles et discutées avant même qu’une traduction humaine ne soit réalisée.
'''Rencontres et évènements'''
* 24 octobre – Webinaire Wiki Éducation : [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/N4XTB4G55BUY3M3PNGUAKQWJ7A4UOPAK/ Techno à code source ouvert : construire le tableau de bord de Wiki Éducation], avec des stagiaires de Wikimedia et un développeur web.
* 20 – 22 décembre 2024 – [[m:Special:MyLanguage/Indic Wikimedia Hackathon Bhubaneswar 2024|Hackathon Wikimedia Inde 2024 à Bhubaneswar]], en Odisha (Inde) : un hackathon pour les membres de la communauté (développement, design, contribution au contenu) pour construire des solutions techniques et améliorer l’expérience des contributeurs et contributrices.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/43|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W43"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 21 octobre 2024 à 22:52 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27634672 -->
== 'Wikidata item' link is moving, finally. ==
Hello everyone, I previously wrote on the 27th September to advise that the ''Wikidata item'' sitelink will change places in the sidebar menu, moving from the '''General''' section into the '''In Other Projects''' section. The scheduled rollout date of 04.10.2024 was delayed due to a necessary request for Mobile/MinervaNeue skin. I am happy to inform that the global rollout can now proceed and will occur later today, 22.10.2024 at 15:00 UTC-2. [[m:Talk:Wikidata_For_Wikimedia_Projects/Projects/Move_Wikidata_item_link|Please let us know]] if you notice any problems or bugs after this change. There should be no need for null-edits or purging cache for the changes to occur. Kind regards, -[[m:User:Danny Benjafield (WMDE)|Danny Benjafield (WMDE)]] 22 octobre 2024 à 13:30 (CEST)
<!-- Message envoyé par User:Danny Benjafield (WMDE)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Danny_Benjafield_(WMDE)/MassMessage_Test_List&oldid=27535421 -->
== Actualités techniques n° 2024-44 ==
<section begin="technews-2024-W44"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/44|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Plus tard en novembre, l'extension Chart sera déployée sur les wikis de test afin d'aider à identifier et corriger le moindre problème. Une révision de sécurité est en cours pour ensuite activer le déploiement sur des wiki pilotes pour des tests plus larges. Vous pouvez lire [[mw:Special:MyLanguage/Extension:Chart/Project/Updates#October 2024: Working towards production deployment|la mise à jour du project d'octobre]] and voir la [https://en.wikipedia.beta.wmflabs.org/wiki/Charts dernière documentation et exemples sur Beta Wikipedia].
* Voir les {{formatnum:32}} {{PLURAL:32|tâche|tâches}} soumises par la communauté qui ont été [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolues la semaine dernière]]. Par exemple [[w:fr:PediaPress|Pediapress.com]], un service externe qui crée des livres depuis Wikipédia, peut maintenant utiliser [[mw:Special:MyLanguage/Wikimedia Maps|Wikimedia Maps]] pour inclure des images de cartes d'infobox existantes dans leurs livres imprimés sur Wikipédia. [https://phabricator.wikimedia.org/T375761]
'''Actualités pour la contribution technique'''
* Les wiki peuvent utiliser l'[[:mw:Special:MyLanguage/Extension:GuidedTour|extension Guided Tour]] pour aider les nouveaux arrivants à comprendre comment modifier. L'extension Guided Tour est maintenant compatible avec le [[mw:Special:MyLanguage/Manual:Dark mode|mode sombre]]. Les mainteneurs de ''Guided Tour'' peuvent vérifier leurs tours pour s'assurer que rien ne semble étrange. Ils peuvent aussi mettre <code>emitTransitionOnStep</code> à <code>true</code> pour corriger un ancien bug. Ils peuvent utiliser le nouveau ''flag'' <code>allowAutomaticBack</code> pour éviter les boutons de retour dont ils ne veulent pas. [https://phabricator.wikimedia.org/T73927#10241528]
* Les administrateurs de projets Wikimédia qui utilisent l'[[mw:Special:MyLanguage/Help:Extension:Nuke|extension Nuke]] remarqueront que les suppressions de masse réalisées avec cet outil ont la balise « Nuke ». Ce changement va faciliter la révision et l'analyse des suppressions effectuées avec l'outil. [https://phabricator.wikimedia.org/T366068]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/44|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W44"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 28 octobre 2024 à 21:56 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27668811 -->
== Final Reminder: Join us in Making Wiki Loves Ramadan Success ==
Dear all,
We’re thrilled to announce the Wiki Loves Ramadan event, a global initiative to celebrate Ramadan by enhancing Wikipedia and its sister projects with valuable content related to this special time of year. As we organize this event globally, we need your valuable input to make it a memorable experience for the community.
Last Call to Participate in Our Survey: To ensure that Wiki Loves Ramadan is inclusive and impactful, we kindly request you to complete our community engagement survey. Your feedback will shape the event’s focus and guide our organizing strategies to better meet community needs.
* Survey Link: [https://docs.google.com/forms/d/e/1FAIpQLSffN4prPtR5DRSq9nH-t1z8hG3jZFBbySrv32YoxV8KbTwxig/viewform?usp=sf_link Complete the Survey]
* Deadline: November 10, 2024
Please take a few minutes to share your thoughts. Your input will truly make a difference!
'''Volunteer Opportunity''': Join the Wiki Loves Ramadan Team! We’re seeking dedicated volunteers for key team roles essential to the success of this initiative. If you’re interested in volunteer roles, we invite you to apply.
* Application Link: [https://docs.google.com/forms/d/e/1FAIpQLSfXiox_eEDH4yJ0gxVBgtL7jPe41TINAWYtpNp1JHSk8zhdgw/viewform?usp=sf_link Apply Here]
* Application Deadline: October 31, 2024
Explore Open Positions: For a detailed list of roles and their responsibilities, please refer to the position descriptions here: [https://docs.google.com/document/d/1oy0_tilC6kow5GGf6cEuFvdFpekcubCqJlaxkxh-jT4/ Position Descriptions]
Thank you for being part of this journey. We look forward to working together to make Wiki Loves Ramadan a success!
Warm regards,<br>
The Wiki Loves Ramadan Organizing Team 29 octobre 2024 à 06:11 (CET)
<!-- Message envoyé par User:ZI Jony@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=27568454 -->
== Actualités techniques n° 2024-45 ==
<section begin="technews-2024-W45"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/45|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les stewards peuvent maintenant faire en sorte que les [[m:Special:MyLanguage/Global blocks|blocages de comptes globaux]] causent un [[mw:Special:MyLanguage/Autoblock|blocage automatique]] global. Cela va aider les stewards à prévenir des abus d'utilisateurs qui ont été bloqués globalement. Cela inclut d'empêcher les comptes temporaires bloqués globalement de quitter leur session ou de changer de navigateur pour effectuer des modifications ultérieures pendant 24 heures. Auparavant, les comptes temporaires pouvaient quitter leur session en cours ou changer de navigateur pour continuer à modifier. Il s'agit d'une amélioration de l'outil anti-abus pour le projet des [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|Comptes temporaires]]. Vous pouvez en savoir plus sur les [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Updates|progrès réalisés sur les fonctionnalités clés pour les comptes temporaires]]. [https://phabricator.wikimedia.org/T368949]
* Les wikis qui ont l'[[m:Special:MyLanguage/CampaignEvents/Deployment status|extension CampaignEvents activée]] peuvent maintenant utiliser la fonctionnalité de [[m:Special:MyLanguage/Campaigns/Foundation Product Team/Event list#October 29, 2024: Collaboration List launched|Liste de Collaboration]]. Cette liste fournit un nouveau moyen simple aux contributeurs d'en apprendre plus sur les Wikiprojets de leurs wikis. Merci à l'équipe Campagnes pour ce travail qui fait partie du [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2024-2025/Product %26 Technology OKRs#WE KRs|plan annuel 2024-2025]]. Si vous souhaitez activer l'extension CampaignEvents sur votre wiki, vous pouvez [[m:Special:MyLanguage/CampaignEvents/Deployment status#How to Request the CampaignEvents Extension for your wiki|suivre ces étapes]] ou contacter User:Udehb-WMF pour obtenir de l'aide.
* La couleur de texte pour les liens rouges sera légèrement modifée plus tard cette semaine pour améliorer leur contraste en mode clair. [https://phabricator.wikimedia.org/T370446]
* Voir {{PLURAL:32|la tâche soumise|les {{formatnum:32}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:32||s}} la semaine dernière]]. Par exemple, sur les wikis multilingues, les utilisateurs [[phab:T216368|peuvent maintenant]] masquer les traductions sur la page spéciale WhatLinksHere.
'''Actualités pour la contribution technique'''
* [[m:Special:MyLanguage/Data dumps|Les sauvegardes]] (''dumps'') de données ont été temporairement mises en pause pendant qu'un bug est examiné. [https://lists.wikimedia.org/hyperkitty/list/xmldatadumps-l@lists.wikimedia.org/message/BXWJDPO5QI2QMBCY7HO36ELDCRO6HRM4/]
'''En détails'''
* Les comptes temporaires ont été déployés sur six wikis ; merci à l'équipe de produit Confiance et Sécurité pour [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|ce travail]], vous pouvez en lire plus sur [[phab:T340001|les plans de déploiement]]. À partir de la semaine prochaine, les comptes temporaires seront aussi activés sur [[phab:T378336|sept autres projets]]. Si vous êtes actif sur ces wikis et que vous avez besoin d'aide pour migrer vos outils, veuillez contacter [[m:User:Udehb-WMF|User:Udehb-WMF]] pour obtenir de l'aide.
* La dernière [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2024/October|newsletter Language and Internationalization]] trimestrielle est disponible. Elle comprend : de nouvelles langues prises en charge dans translatewiki ou dans MediaWiki ; de nouvelles méthodes de saisie au clavier pour certaines langues ; des détails sur les réunions récentes et à venir, et plus.
'''Rencontres et évènements'''
* La [[mw:Special:MyLanguage/MediaWiki Users and Developers Conference Fall 2024|Conférence de l’utilisation et du développement de MediaWiki de l’automne 2024]] se déroule à Vienne (Autriche) et en ligne du 4 au 6 novembre 2024. La conférence comportera des discussions autour de l'utilisation du logiciel MediaWiki par et au sein d'entreprises de différents secteurs et inspirera et intégrera de nouveaux utilisateurs.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/45|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W45"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 4 novembre 2024 à 21:50 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27693917 -->
== Actualités techniques n° 2024-46 ==
<section begin="technews-2024-W46"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/46|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Sur les wikis avec l'[[mw:Special:MyLanguage/Help:Extension:Translate|extension Translate]], les utilisateurs remarqueront que le FuzzyBot va maintenant automatiquement créer des versions traduites des catégories utilisées sur les pages traduites. [https://phabricator.wikimedia.org/T285463]
* Voir {{PLURAL:29|la tâche soumise|les {{formatnum:29}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:29||s}} la semaine dernière]]. <span lang="en" dir="ltr" class="mw-content-ltr">For example, the submitted task to use the [[mw:Special:MyLanguage/Extension:SecurePoll|SecurePoll extension]] for English Wikipedia's special [[w:en:Wikipedia:Administrator elections|administrator election]] was resolved on time.</span> [https://phabricator.wikimedia.org/T371454]
'''Actualités pour la contribution technique'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] <span lang="en" dir="ltr" class="mw-content-ltr">In <code dir="ltr">[[mw:MediaWiki_1.44/wmf.2|1.44.0-wmf-2]]</code>, the logic of Wikibase function <code>getAllStatements</code> changed to behave like <code>getBestStatements</code>. Invoking the function now returns a copy of values which are immutable.</span> [https://phabricator.wikimedia.org/T270851]
* <span lang="en" dir="ltr" class="mw-content-ltr">[https://en.wikipedia.org/api/rest_v1/ Wikimedia REST API] users, such as bot operators and tool maintainers, may be affected by ongoing upgrades. The API will be rerouting some page content endpoints from RESTbase to the newer [[mw:Special:MyLanguage/API:REST API|MediaWiki REST API]] endpoints. The [[phab:T374683|impacted endpoints]] include getting page/revision metadata and rendered HTML content. These changes will be available on testwiki later this week, with other projects to follow. This change should not affect existing functionality, but active users of the impacted endpoints should verify behavior on testwiki, and raise any concerns on the related [[phab:T374683|Phabricator ticket]].</span>
'''En détails'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Admins and users of the Wikimedia projects [[mw:Special:MyLanguage/Moderator_Tools/Automoderator#Usage|where Automoderator is enabled]] can now monitor and evaluate important metrics related to Automoderator's actions. [https://superset.wmcloud.org/superset/dashboard/unified-automoderator-activity-dashboard/ This Superset dashboard] calculates and aggregates metrics about Automoderator's behaviour on the projects in which it is deployed. Thanks to the Moderator Tools team for this Dashboard; you can visit [[mw:Special:MyLanguage/Moderator Tools/Automoderator/Unified Activity Dashboard|the documentation page]] for more information about this work.</span> [https://phabricator.wikimedia.org/T369488]
'''Rencontres et évènements'''
* <span lang="en" dir="ltr" class="mw-content-ltr">21 November 2024 ([[m:Special:MyLanguage/Event:Commons community discussion - 21 November 2024 8:00 UTC|8:00 UTC]] & [[m:Special:MyLanguage/Event:Commons community discussion - 21 November 2024 16:00 UTC|16:00 UTC]]) - [[c:Commons:WMF support for Commons/Commons community calls|Community call]] with Wikimedia Commons volunteers and stakeholders to help prioritize support efforts for 2025-2026 Fiscal Year. The theme of this call is how content should be organised on Wikimedia Commons.</span>
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/46|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W46"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 12 novembre 2024 à 01:07 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27732268 -->
== Actualités techniques n° 2024-47 ==
<section begin="technews-2024-W47"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/47|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les utilisateurs des sites Wikimedia seront désormais avertis lorsqu’ils créent une [[mw:Special:MyLanguage/Help:Redirects|redirection]] vers une page qui n’existe pas. Cela réduira le nombre de redirections cassées vers des liens rouges sur nos projets. [https://phabricator.wikimedia.org/T326057]
* Voir {{PLURAL:42|la tâche soumise|les {{formatnum:42}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:42||s}} la semaine dernière]]. Par exemple, [[mw:Special:MyLanguage/Manual:Pywikibot/Overview|Pywikibot]], qui automatise certains travaux sur les sites sous MediaWiki, a été mis à jour vers la version 9.5.0 sur Toolforge. [https://phabricator.wikimedia.org/T378676]
'''Actualités pour la contribution technique'''
* Sur les wikis qui utilisent l’[[mw:Special:MyLanguage/Extension:FlaggedRevs|extension FlaggedRevs]], les pages créées ou marquées par des utilisateurs avec les permissions appropriées sont marquées comme relues automatiquement. Cette fonction est actuellement cassée, le correctif devrait être déployé cette semaine. Merci à Daniel et Wargo d’avoir travaillé dessus. [https://phabricator.wikimedia.org/T379218][https://phabricator.wikimedia.org/T368380]
'''En détails'''
* Une nouvelle [https://diff.wikimedia.org/2024/11/05/say-hi-to-temporary-accounts-easier-collaboration-with-logged-out-editors-with-better-privacy-protection publication sur Diff] concernant les comptes temporaires est disponible dans 15 langues. Elle explique ce que sont les comptes temporaires, leurs conséquences sur les différents groupes utilisateur et le programme d’introduction du changement sur tous les wikis.
'''Rencontres et évènements'''
* Les bénévoles techniques peuvent désormais s’inscrire pour le [[mw:Special:MyLanguage/Wikimedia Hackathon 2025|hackathon Wikimedia 2025]] qui aura lieu à Istanbul en Turquie. Les [https://pretix.eu/wikimedia/hackathon2025/ demandes de bourse pour le transport et l’hébergement] sont ouvertes du '''12 novembre au 10 décembre 2024'''. L’inscription à l’évènement est possible jusqu’à la mi-avril 2025. Le Hackathon Wikimedia est un rassemblement annuel qui unifie la communauté technique mondiale autour de projets existants et de nouvelles idées.
* Participez à la [[C:Special:MyLanguage/Commons:WMF%20support%20for%20Commons/Commons%20community%20calls|conférence de la communauté Wikimedia Commons]] cette semaine pour aider à prioriser le travail de maintenance pour Commons prévu en 2025-2026. Le thème porte sur l’organisation du contenu sur Wikimedia Commons. Cela permet aux bénévoles de travailler sur divers sujets, se rassembler et discuter de ce qui compte pour l’avenir du projet. La conférence aura lieu le '''21 novembre 2024 à [[m:Special:MyLanguage/Event:Commons community discussion - 21 November 2024 8:00 UTC|8 h UTC]] puis [[m:Special:MyLanguage/Event:Commons community discussion - 21 November 2024 16:00 UTC|16 h UTC]]'''.
* Une [[mw:Special:MyLanguage/Wikimedia_Language_and_Product_Localization/Community meetings#29 November 2024|réunion communautaire sur les langues]] aura lieu le '''29 novembre à 16 h UTC''' pour discuter des actualités et de la résolution des problèmes techniques.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/47|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W47"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 19 novembre 2024 à 03:00 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27806858 -->
== Sign up for the language community meeting on November 29th, 16:00 UTC ==
Hello everyone,
The next language community meeting is coming up next week, on November 29th, at 16:00 UTC (Zonestamp! For your timezone <https://zonestamp.toolforge.org/1732896000>). If you're interested in joining, you can sign up on this wiki page: <https://www.mediawiki.org/wiki/Wikimedia_Language_and_Product_Localization/Community_meetings#29_November_2024>.
This participant-driven meeting will be organized by the Wikimedia Foundation’s Language Product Localization team and the Language Diversity Hub. There will be presentations on topics like developing language keyboards, the creation of the Moore Wikipedia, and the language support track at Wiki Indaba. We will also have members from the Wayuunaiki community joining us to share their experiences with the Incubator and as a new community within our movement. This meeting will have a Spanish interpretation.
Looking forward to seeing you at the language community meeting! Cheers, [[User:SSethi (WMF)|Srishti]] 21 novembre 2024 à 20:53 (CET)
<!-- Message envoyé par User:SSethi (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27746256 -->
== Actualités techniques n° 2024-48 ==
<section begin="technews-2024-W48"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/48|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] Une nouvelle version de la [[mw:Special:MyLanguage/Extension:CodeMirror|coloration syntaxique]] de l'éditeur de wikicode standard sera disponible en tant que [[Special:Preferences#mw-prefsection-betafeatures|fonctionnalité beta]] cette semaine. Cette version apporte de nombreuses nouvelles fonctionnalités et résolutions de bugs, tels que le support du texte de droite à gauche, le [[mw:Special:MyLanguage/Help:Extension:CodeMirror#Template folding|repliement de modèles]], l'[[mw:Special:MyLanguage/Help:Extension:CodeMirror#Autocompletion|autocomplétion]] et une amélioration de la recherche. Davantage d'informations sont disponibles sur la [[mw:Special:MyLanguage/Help:Extension:CodeMirror|page d'aide]].
* L'éditeur de wikicode 2010 supporte désormais les raccourcis clavier courants, comme <bdi lang="zxx" dir="ltr"><code>Ctrl</code>+<code>B</code></bdi> pour la mise en gras et <bdi lang="zxx" dir="ltr"><code>Ctrl</code>+<code>I</code></bdi> pour la mise en italique. Voir la [[mw:Help:Extension:WikiEditor#Keyboard shortcuts|liste complète des six raccourcis]]. Merci à SD0001 pour cette amélioration. [https://phabricator.wikimedia.org/T62928]
* À partir du 28 novembre, les pages utilisant le système de discussions structurées / Flow seront automatiquement archivées et passées en lecture seule sur les wikis suivants : <bdi>bswiki</bdi>{{int:comma-separator/fr}}<bdi>elwiki</bdi>{{int:comma-separator/fr}}<bdi>euwiki</bdi>{{int:comma-separator/fr}}<bdi>fawiki</bdi>{{int:comma-separator/fr}}<bdi>fiwiki</bdi>{{int:comma-separator/fr}}<bdi>frwikiquote</bdi>{{int:comma-separator/fr}}<bdi>frwikisource</bdi>{{int:comma-separator/fr}}<bdi>frwikiversity</bdi>{{int:comma-separator/fr}}<bdi>frwikivoyage</bdi>{{int:comma-separator/fr}}<bdi>idwiki</bdi>{{int:comma-separator/fr}}<bdi>lvwiki</bdi>{{int:comma-separator/fr}}<bdi>plwiki</bdi>{{int:comma-separator/fr}}<bdi>ptwiki</bdi>{{int:comma-separator/fr}}<bdi>urwiki</bdi>{{int:comma-separator/fr}}<bdi>viwikisource</bdi>{{int:comma-separator/fr}}<bdi>zhwikisource</bdi>. Il s'agit d'une étape de la [[mw:Special:MyLanguage/Structured_Discussions/Deprecation|mise en obsolescence des discussions structurées]]. Si vous souhaitez archiver votre page en avance, merci de contacter [[m:User:Trizek (WMF)|Trizek (WMF)]].
* Voir {{PLURAL:25|la tâche soumise|les {{formatnum:25}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:25||s}} la semaine dernière]]. Par exemple, un utilisateur créant un nouveau filtre anti-abus ne peut désormais définir le modèle comme « protégé » que [[phab:T377765|s'il inclut une variable protégée]].
'''Actualités pour les contributeurs techniques'''
* L'éditeur de l'extension [[mw:Special:MyLanguage/Extension:CodeEditor|CodeEditor]], qui est utilisé pour la modification des pages en JavaScript, CSS, JSON et Lua, [[phab:T377663|propose désormais]] un système d'autocomplétion. Merci à SD0001 pour cette amélioration. Cette fonctionnalité peut être désactivée temporairement sur une page en utilisant le raccourcie <bdi lang="zxx" dir="ltr"><code>Ctrl</code>+<code>,</code></bdi> et en désélectionnant « <bdi lang="en" dir="ltr">Live Autocompletion</bdi> ».
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Les mainteneurs d'outils qui utilisent le système Graphite pour suivre les statistiques d'utilisation doivent migrer vers le système plus récent Prometheus. Ils peuvent consulter [https://grafana.wikimedia.org/d/K6DEOo5Ik/grafana-graphite-datasource-utilization?orgId=1 ce tableau de bord] et la liste dans la description de la [[phab:T350592|tâche Phabricator T350592]] pour voir si leurs outils sont concernés et déclarer les métriques et tableaux de bords connectés à leurs outils. Ils peuvent ensuite désactiver ou migrer toutes les métriques existantes en suivant les instructions de la tâche. Le service Graphite passera en lecture seule en avril. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/KLUV4IOLRYXPQFWD6WKKJUHMWE77BMSZ/]
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Le [[mw:Special:MyLanguage/NewPP parser report|rapport de performances du nouveau préprocesseur d'analyse syntaxique]] a été corrigé pour donner un compte précis du nombre d'entités Wikibase chargées. Jusqu'ici, il revenait à zéro au-delà de 400 entités. [https://phabricator.wikimedia.org/T279069]
'''Rencontres et évènements'''
* Une [[mw:Special:MyLanguage/Wikimedia_Language_and_Product_Localization/Community meetings#29 November 2024|rencontre de communauté linguistique]] aura lieu le 29 novembre à [https://zonestamp.toolforge.org/1732896000 16h00 UTC]. Il y aura des présentations sur des sujets comme le développement de claviers linguistiques, la création de Wikipédia en mooré, le suivi du support de langues à [[m:Wiki Indaba|Wiki Indaba]] et un rapport de la communauté Wayuunaiki sur son expérience avec l'incubateur et en tant que nouvelle communauté sur les trois dernières années. Cette rencontre disposera d'un doublage en espagnol.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/48|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W48"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 25 novembre 2024 à 23:41 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27847039 -->
== Actualités techniques n° 2024-49 ==
<section begin="technews-2024-W49"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/49|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Deux nouvelles fonctions d’analyse syntaxique ont été ajoutées cette semaine. La fonction <code dir="ltr"><nowiki>{{</nowiki>[[mw:Special:MyLanguage/Help:Magic words#interwikilink|#interwikilink]]<nowiki>}}</nowiki></code> ajoute un [[mw:Special:MyLanguage/Help:Links#Interwiki links|lien interwiki]] et la fonction <code dir="ltr"><nowiki>{{</nowiki>[[mw:Special:MyLanguage/Help:Magic words#interlanguagelink|#interlanguagelink]]<nowiki>}}</nowiki></code> ajoute un [[mw:Special:MyLanguage/Help:Links#Interlanguage links|lien interlangue]]. Ces fonctions d’analyse sont utiles sur les wikis où des espaces de noms rentrent en conflit avec des préfixes interwikis. Par exemple, les liens commençant par <bdi lang="zxx" dir="ltr"><code>MOS:</code></bdi> sur Wikipédia en anglais [[phab:T363538|sont en conflit avec le code de langue <code>mos</code> qui est le préfixe pour la Wikipédia en mooré]].
* À partir de cette semaine, les wikis Wikimedia n’acceptent plus les connexions utilisant des certificats HTTPS basé sur RSA, notamment RSA-2048. Ce changement vise à améliorer la sécurité de tous. Certains vieux navigateurs ou ordiphones ne pourront plus se connecter et afficheront une ereur. Consultez la [[wikitech:HTTPS/Browser_Recommendations|page de recommandations pour les navigateurs HTTPS]] pour plus d’informations. Tous les systèmes d’exploitation et navigateurs modernes peuvent encore accéder aux projets Wikimedia. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/CTYEHVNSXUD3NFAAMG3BLZVTVQWJXJAH/]
* À partir du 16 décembre, les pages de discussions structurées (Flow) seront automatiquement archivées et passées en lecture seule sur les wikis suivants : <bdi>arwiki</bdi>{{int:comma-separator/fr}}<bdi>cawiki</bdi>{{int:comma-separator/fr}}<bdi>frwiki</bdi>{{int:comma-separator/fr}}<bdi>mediawikiwiki</bdi>{{int:comma-separator/fr}}<bdi>orwiki</bdi>{{int:comma-separator/fr}}<bdi>wawiki</bdi>{{int:comma-separator/fr}}<bdi>wawiktionary</bdi>{{int:comma-separator/fr}}<bdi>wikidatawiki</bdi>{{int:comma-separator/fr}}<bdi>zhwiki</bdi>. Il s’agit d'une étape de la [[mw:Special:MyLanguage/Structured_Discussions/Deprecation|mise en obsolescence des discussions structurées]]. Si vous avez besoin d’aide pour archiver votre page en avance, vous pouvez contacter [[m:User:Trizek (WMF)|Trizek (WMF)]]. [https://phabricator.wikimedia.org/T380910]
* L'extension Chart a été mise en production ce mois et est désormais utilisable sur Commons et Testwiki. L'examen de sécurité étant désormais achevé, le déploiement sur les wikis pilotes devrait commencer lors de la première semaine de décembre. Vous pouvez tester une version fonctionnelle [[testwiki:Charts|sur Testwiki]] et lire [[mw:Special:MyLanguage/Extension:Chart/Project/Updates|les nouvelles du projet de novembre]] pour plus d'informations.
* Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]]. Par exemple, un bug avec l'outil « Télécharger comme PDF » a été corrigé. [https://phabricator.wikimedia.org/T376438]
'''Actualités pour la contribution technique'''
* <span class="mw-translate-fuzzy">Fin février, les comptes temporaires seront déployés sur au moins dix wikis majeurs. Ce déploiement aura un effet important sur le code maintenu par la communauté. Tout code sur Toolforge, celui des outils, robots, gadgets et scripts utilisateur est susceptible d'être impacté s'il utilise des données liées à l'adresse IP ou utilisables par les utilisateurs non connectés. L'équipe Produits pour la confiance et la sécurité souhaite identifier le code concerné, le surveiller et aider à sa mise à jour avant le déploiement pour minimiser le cassage des outils lors de la transition. L'équipe demande aux contributeurs techniques et aux développeurs bénévoles d'aider à identifier ces outils en les ajoutant à [[mw:Trust and Safety Product/Temporary Accounts/For developers/Impacted tools|cette liste]]. Ils peuvent consulter la [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/For developers|documentation mise à jour]] pour apprendre à mettre à jour les outils. Rendez-vous sur la [[mw:Talk:Trust and Safety Product/Temporary Accounts|page de discussion du projet]] ou sur [[discord:channels/221049808784326656/1227616742340034722|le fil Discord dédié]] pour être assisté·e ou partager des retours.</span>
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/49|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W49"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 2 décembre 2024 à 23:22 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27873992 -->
== Actualités techniques n° 2024-50 ==
<section begin="technews-2024-W50"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/50|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Les personnes contribuant à la documentation technique trouveront des ressources actualisées et de nouvelles façons de communiquer entre pairs et avec l’équipe Documentation technique de Wikimedia sur la [[mw:Special:MyLanguage/Documentation|centrale Documentation]] sur MediaWiki.org. Cette page renvoie vers : des ressources pour écrire et améliorer la documentation, un nouveau canal IRC <bdi lang="zxx" dir="ltr">#wikimedia-techdocs</bdi> sur Libera.chat, un répertoire des évènements passés et à venir autour de la documentation, et des procédures pour demander la consultation ou la relecture de documentation. Si vous avez des commentaires ou des idées d’amélioration pour l’écosystème de la documentation, contactez [[mw:Wikimedia Technical Documentation Team#Contact us|l’équipe Documentation technique]].
'''Actualités pour la contribution'''
[[File:Edit Check on Desktop.png|thumb|Modification de la mise en page pour la fonction de vérification des modifications.]]
* Cette semaine, la [[mw:Special:MyLanguage/Edit check|vérification des modifications]] sera déplacée vers une barre latérale sur ordinateur. La vérification des modifications est la fonctionnalité qui guide les novices pour qu’ils respectent les règles et recommandations. Ce changement de mise en page crée de l’espace pour permettre l’affichage de [[mw:Edit check#1 November 2024|nouvelles vérifications]] ''pendant'' qu’ils saisissent du texte. Les [[mw:Special:MyLanguage/Edit check#Reference Check A/B Test|premiers résultats]] montrent que les novices confrontés aux vérifications es modifications sont 2,2 fois plus susceptibles de publier un ajout de contenu avec une source sans être révoqué.
* L’extension Chart, qui permet de créer des graphiques, a été activée avec succès sur MediaWiki.org et trois wikis pilotes (les Wikipédia en italien, suédois et hébreux). Vous pouvez voir des exemples fonctionnels [[testwiki:Charts|sur TestWiki]] et lire [[mw:Special:MyLanguage/Extension:Chart/Project/Updates|les actualités de novembre du projet]] pour plus de détails.
* Les traducteurs et traductrices sur les wikis où [[mw:Special:MyLanguage/Content translation/Section translation#Try the tool|l’adaptation mobile de l’outil de traduction de contenu est disponible]] peuvent désormais découvrir des articles dans les campagnes de wikiprojets qui les intéressent depuis la catégorie « [https://test.wikipedia.org/w/index.php?title=Special:ContentTranslation&campaign=specialcx&filter-type=automatic&filter-id=collections&active-list=suggestions&from=es&to=en Toutes les collections] » dans la fonctionnalité de suggestion d’articles. Les équipes d’organisation des campagnes de wikiprojets peuvent utiliser cette fonctionnalité pour permettre aux traducteurs de découvrir des articles qui les intéressent, en ajoutant la balise <code dir=ltr><nowiki><page-collection> </page-collection></nowiki></code> dans la page qui liste les articles pour leur campagne sur Meta-Wiki. Cela rendra ces articles visibles depuis l’outil de traduction de contenu. Pour plus d’informations sur l’utilisation de l’outil et de la balise, consultez le [[mw:Special:MyLanguage/Translation suggestions: Topic-based & Community-defined lists/How to use the features|guide pas-à-pas]]. [https://phabricator.wikimedia.org/T378958]
* La fonctionnalité [[mw:Special:MyLanguage/Extension:Nuke|Nuke]], qui permet aux admins de supprimer des pages en masse, est désormais d’un [[phab:T376379#10310998|filtre de multisélection pour le choix d’un espace de noms]]. Cela permet de choisir plusieurs espaces de noms (plutôt qu’un seul ou tous) pour rechercher des pages à supprimer.
* La fonctionnalité ''Nuke'' [[phab:T364225#10371365|fournit désormais des liens]] vers la page utilisateur de l’utilisateur ou utilisatrice dont les pages ont été supprimées, et vers les pages qui n’ont pas été sélectionnées pour suppression, après que le processus de suppression commence. Cela facilite les actions administratives suivantes. Merci à Chlod et l’équipe Outils de modération pour ces deux améliorations. [https://phabricator.wikimedia.org/T364225#10371365]
* L’équipe Rédaction travaille pour rendre plus facile l’ajout de références vers Archive.org grâce à l’outil [[mw:Special:MyLanguage/Citoid/Enabling Citoid on your wiki|Citoid]] qui génère des références. Ils demandent aux communautés d’ajouter deux paramètres préventivement — <code dir=ltr>archiveUrl</code> et <code dir=ltr>archiveDate</code> — dans les TemplateData pour chaque modèle de référence utilisé par Citoid. Vous pouvez voir un [https://en.wikipedia.org/w/index.php?title=Template%3ACite_web%2Fdoc&diff=1261320172&oldid=1260788022 exemple de changement dans un modèle] et une [https://global-search.toolforge.org/?namespaces=10&q=%5C%22citoid%5C%22%3A%20%5C%7B®ex=1&title= liste des modèles concernés]. [https://phabricator.wikimedia.org/T374831]
* Un nouveau wiki a été créé : {{int:project-localized-name-group-wikivoyage}} en [[d:Q9240|indonésien]] ([[voy:id:|<code>voy:id:</code>]]) [https://phabricator.wikimedia.org/T380726]
* La semaine dernière, tous les wikis ont rencontré des problèmes pour servir les pages aux utilisateurs et utilisatrices connectés et certains non-connectés, pendant 30 à 45 minutes. Cela vient d’un problème de base de données, les investigations sont en cours. [https://www.wikimediastatus.net/incidents/3g2ckc7bp6l9]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:19|la tâche soumise|les {{formatnum:19}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:19||s}} la semaine dernière]]. Par exemple, un beugue dans la fonctionnalité [[mw:Special:MyLanguage/Help:Growth/Tools/Add a link|Ajouter un lien]] a été corrigé. Auparavant, la liste des sections exclues de cette fonction était ignorée dans certains cas. [https://phabricator.wikimedia.org/T380455][https://phabricator.wikimedia.org/T380329]
'''Actualités pour la contribution technique'''
* [[mw:Special:MyLanguage/Codex|Codex]], le système de mise en forme de Wikimedia, a désormais une première [[gitiles:design/codex-php|implémentation en PHP]]. Elle peut être utilisée généralement pour les extensions MediaWiki et les applis de Toolforge grâce à [https://packagist.org/packages/wikimedia/codex Composer] ; pour le cœur de MediaWiki, cela arrive bientôt. D’autres informations sont dans [[wmdoc:design-codex-php/main/index.html|la documentation]]. Merci à Doğu pour l’inspiration et de nombreuses contributions à la bibliothèque. [https://phabricator.wikimedia.org/T379662]
* Les utilisateurs et utilisatrices de l’[https://en.wikipedia.org/api/rest_v1/ API Wikimedia REST] (qui gèrent par exemple des robots ou des outils) peuvent être affectés par des mises à jours en cours. Le 4 décembre, l’équipe Interfaces de MediaWiki a commencé à rediriger des points finaux de métadonnées de pages et révisions et de rendus HTML sur [[testwiki:|TestWiki]] depuis RESTbase vers des points finaux équivalents de l’API REST de MediaWiki. L’équipe encourage les utilisateurs de ces points finaux à vérifier le comportement de leurs outils sur TestWiki et à faire part de leurs questions sur la [[phab:T374683|tâche Phabricator]] avant la fin de l’année, puisque ces changements devraient être déployées sur tous les projets Wikimedia début janvier. Ces changements s’inscrivent dans le remplacement du système obsolète [[mw:RESTBase/deprecation|RESTBase]].
* L’[https://wikimediafoundation.limesurvey.net/986172 enquête 2024 de satisfaction des développeurs et développeuses] vise à récolter les opinions de la communauté de développement de Wikimedia. Participez à l’enquête si vous avez un quelconque rôle dans le développement des logiciels pour l’écosystème Wikimedia. L’enquête est ouverte jusqu’au 3 janvier 2025 et a une [[foundation:Legal:Developer Satisfaction Survey 2024 Privacy Statement|déclaration de confidentialité]] propre.
* Il n’y aura pas de nouvelle version de MediaWiki cette semaine. [https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar]
'''Rencontres et évènements'''
* Les prochaines réunions dans la série des [[c:Commons:WMF support for Commons/Commons community calls|échanges entre les Wikimedia Foundation et la communauté de Wikimedia Commons]] aura lieu le [[m:Event:Commons community discussion - 12 December 2024 08:00 UTC|12 décembre à 18 h UTC]] et [[m:Event:Commons community discussion - 12_December 2024 16:00 UTC|à 16 h UTC]]. Le sujet de ces conférences port sur les nouveaux médias et les nouveaux contributeurs et contributrices. Les personnes contribuant sur n’importe quel wiki sont invitées à participer.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/50|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W50"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 9 décembre 2024 à 23:15 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27919424 -->
== Actualités techniques n° 2024-51 ==
<section begin="technews-2024-W51"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2024/51|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Interested in improving event management on your home wiki? The [[m:Special:MyLanguage/CampaignEvents|CampaignEvents extension]] offers organizers features like event registration management, event/wikiproject promotion, finding potential participants, and more - all directly on-wiki. If you are an organizer or think your community would benefit from this extension, start a discussion to enable it on your wiki today. To learn more about how to enable this extension on your wiki, visit the [[m:CampaignEvents/Deployment status#How to Request the CampaignEvents Extension for your wiki|deployment status page]].</span>
'''Actualités pour la contribution'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Users of the iOS Wikipedia App in Italy and Mexico on the Italian, Spanish, and English Wikipedias, can see a [[mw:Special:MyLanguage/Wikimedia Apps/Team/iOS/Personalized Wikipedia Year in Review|personalized Year in Review]] with insights based on their reading and editing history.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">Users of the Android Wikipedia App in Sub-Saharan Africa and South Asia can see the new [[mw:Special:MyLanguage/Wikimedia Apps/Team/Android/Rabbit Holes|Rabbit Holes]] feature. This feature shows a suggested search term in the Search bar based on the current article being viewed, and a suggested reading list generated from the user’s last two visited articles.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">The [[m:Special:MyLanguage/Global reminder bot|global reminder bot]] is now active and running on nearly 800 wikis. This service reminds most users holding temporary rights when they are about to expire, so that they can renew should they want to. See [[m:Global reminder bot/Technical details|the technical details page]] for more information.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">The next issue of Tech News will be sent out on 13 January 2025 because of the end of year holidays. Thank you to all of the translators, and people who submitted content or feedback, this year.</span>
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]]. <span lang="en" dir="ltr" class="mw-content-ltr">For example, a bug was [[phab:T374988|fixed]] in the Android Wikipedia App which had caused translatable SVG images to show the wrong language when they were tapped.</span>
'''Actualités pour la contribution technique'''
* <span lang="en" dir="ltr" class="mw-content-ltr">There is no new MediaWiki version next week.</span> <span lang="en" dir="ltr" class="mw-content-ltr">The next deployments will start on 14 January.</span> [https://wikitech.wikimedia.org/wiki/Deployments/Yearly_calendar/2025]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2024/51|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2024-W51"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 16 décembre 2024 à 23:24 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=27942374 -->
gntqozv5awybl1etzxukvh74xcuabfi
Mathc initiation/a511
0
80955
763671
725881
2026-04-14T08:27:29Z
Xhungab
23827
763671
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/005v| Sommaire]]
:
{{Partie{{{type|}}}|Matrices hessiennes}}
:
En mathématiques, la matrice hessienne (ou simplement le hessien ou la hessienne) d'une fonction numérique f est la matrice carrée, notée, H(f) de ses dérivées partielles secondes. [https://w.wiki/9nHP wikipedia]
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c59a4|x_afile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]]
* [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]]
* [[Mathc initiation/Fichiers h : c39a3|x_Hess.h ............ Calculer le déterminant]]
:
.
:
Les fonctions pour les différents exemples :
* [[Mathc initiation/Fichiers h : c41a3|f.h]]
:
.
:
Exemple ... :
* [[Mathc initiation/a508|c00a.c ... "-x**2 - 4*x - y**2 + 2*y - 1"]]
* [[Mathc initiation/a509|c00b.c ... "x**2 + 4*y**2 - x + 2*y"]]
* [[Mathc initiation/a510|c00c.c ... "x**3 + 3*x*y - y**3"]]
:
.
:
{{AutoCat}}
io94ac4agbalumhxku46p5wec1mxs8v
Discussion Wikilivres:Le Bistro/2025
5
82063
763586
756882
2026-04-13T15:10:47Z
Uzume
11567
gitiles
763586
wikitext
text/x-wiki
== Actualités techniques n° 2025-03 ==
<section begin="technews-2025-W03"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/03|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Le système de connexion utilisateur unique (SUL) va être mis à jour durant les prochains mois. Il permet aux utilisateurs et utilisatrices d’être connectés sur tous les sites en même temps après avoir renseigné leurs identifiants sur un site Wikimedia. La mise à jour est nécessaire car les navigateurs restreignent de plus en plus les témoins de connexion inter-domaines. Pour s’adapter à ces restrictions, les pages de connexion et de création de compte seront déplacées vers un domaine central, mais cela apparaitra toujours comme si vous étiez sur le wiki d’origine. Le code mis à jour sera activé cette semaine pour les utilisations sur les wikis de test. Ce changement devrait être déployé pour tous durant février et mars. Consultez [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3#Deployment|la page du projet SUL3]] pour plus d’informations et un calendrier.
'''Actualités pour la contribution'''
* Sur les wikis ayant [[mw:Special:MyLanguage/Extension:PageAssessments|PageAssessments]] (évaluation des pages) installée, vous pouvez désormais [[mw:Special:MyLanguage/Extension:PageAssessments#Search|filtrer les résultats de recherche]] aux pages dans un projet donné à l’aide du mot-clé <code dir=ltr>inproject:</code>. (Ces wikis : {{int:project-localized-name-arwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-enwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-enwikivoyage/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-frwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-huwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-newiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-trwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-zhwiki/fr}}.) [https://phabricator.wikimedia.org/T378868]
* Un nouveau wiki a été créé : une Wikipédia en [[d:Q34129|tigré]] ([[w:tig:|<code>w:tig:</code>]]) [https://phabricator.wikimedia.org/T381377]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:35|la tâche soumise|les {{formatnum:35}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:35||s}} la semaine dernière]]. Par exemple, il y avait un beugue de mise à jour du compteur de modifications de quelqu’un effectuant une annulation d’une autre modification : cela est maintenant corrigé. [https://phabricator.wikimedia.org/T382592]
'''Actualités pour la contribution technique'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Les utilisateurs et utilisatrices de l’API REST de Wikimedia (par exemple pour des robots ou des outils) peuvent être impactés par des mises à jour en cours. À partir de la semaine du 13 janvier, nous commencerons à rediriger [[phab:T374683|certains points terminaux de contenu de page]] depuis RESTbase vers les nouveaux points terminaux de l’API REST de MediaWiki pour tous les projets wiki. Ce changement était disponible sur testwiki, et ne devrait pas affecter les fonctionnalités existantes, mais les utilisateurs actifs des points terminaux concernés peuvent signaler directement à l’[[phab:project/view/6931/|équipe des interfaces de MediaWiki]] tout problème qui arriverait.
* Les personnes maintenant des outils sur Toolforge peuvent désormais partager leurs retour sur Toolforge UI, un projet visant à fournir une plateforme web pour la création et la gestion d’outils Toolforge depuis une interface graphique, en plus des processus existant par ligne de commande. Ce projet vise à simplifier les tâches des mainteneurs et mainteneuses actifs, ainsi qu’à rendre l’inscription et les procédures de déploiement plus accessibles aux nouveaux et nouvelles créatrices d’outils. Le projet en est encore à ses balbutiements et l’équipe des services en infonuage recueille des retours de la communauté Toolforge pour aiderà concevoir la solution correspondant à leurs besoins. [[wikitech:Wikimedia Cloud Services team/EnhancementProposals/Toolforge UI|En savoir plus et donner son avis sur Toolforge UI]].
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] <span class="mw-translate-fuzzy">Pour le développement d’outil et bibliothèque qui utilisent le système OAuth : le point terminal d’identité utilisé pour [[mw:Special:MyLanguage/OAuth/For Developers#Identifying the user|OAuth 1]] et [[mw:Special:MyLanguage/OAuth/For Developers#Identifying the user 2|OAuth 2]] retournait un objet JSON avec un entier dans le sous-champ, ce qui était incorrect (le champ doit toujours être une chaine de caractère); Cela a été corrigé ; le correctif sera déployé sur les wikis Wikimedia la semaine du 13 janvier.</span> [https://phabricator.wikimedia.org/T382139]
* De nombreux wikis utilisent actuellement le [[:mw:Parsoid/Parser Unification/Cite CSS|CSS de Cite]] pour insérer des marqueurs de note de bas de page personnalisés dans la sortie de Parsoid. À partir du 20 janvier, ces règles seront désactivées, mais les développeurs vous demandent de ''ne pas'' nettoyer votre <bdi lang="en" dir="ltr">[[MediaWiki:Common.css]]</bdi> avant le 20 février pour éviter des problèmes pendant la migration. Vos wikis rencontreront peut-être des petits changements dans les marqueurs de notes de bas page dans l’éditeur visuel ou en utilisant le mode de lecture expérimental Parsoid, mais s’il y a des changements, ils devraient garder le rendu cohérent avec la sortie de l’analyseur classique. [https://phabricator.wikimedia.org/T370027]
'''Rencontres et évènements'''
* Les prochaines réunions de la série des [[c:Special:MyLanguage/Commons:WMF support for Commons/Commons community calls|Discussions communautaires entre Wikimedia Foundation et la communauté de Wikimedia Commons]] aura lieu le [[m:Special:MyLanguage/Event:Commons community discussion - 15 January 2025 08:00 UTC|15 janvier à 8 h UTC]] et [[m:Special:MyLanguage/Event:Commons community discussion - 15 January 2025 16:00 UTC|à 16 h UTC]]. Le sujet de cette conférence porte sur la définition des priorités d’investissement en outils pour Commons. Les contributeurs et contributrices de tous les wikis sont les bienvenus pour participer, notamment celles et ceux qui maintiennent des outils pour Commons.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/03|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W03"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 14 janvier 2025 à 02:42 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28048614 -->
== Launching! Join Us for Wiki Loves Ramadan 2025! ==
Dear All,
We’re happy to announce the launch of [[m:Wiki Loves Ramadan 2025|Wiki Loves Ramadan 2025]], an annual international campaign dedicated to celebrating and preserving Islamic cultures and history through the power of Wikipedia. As an active contributor to the Local Wikipedia, you are specially invited to participate in the launch.
This year’s campaign will be launched for you to join us write, edit, and improve articles that showcase the richness and diversity of Islamic traditions, history, and culture.
* Topic: [[m:Event:Wiki Loves Ramadan 2025 Campaign Launch|Wiki Loves Ramadan 2025 Campaign Launch]]
* When: Jan 19, 2025
* Time: 16:00 Universal Time UTC and runs throughout Ramadan (starting February 25, 2025).
* Join Zoom Meeting: https://us02web.zoom.us/j/88420056597?pwd=NdrpqIhrwAVPeWB8FNb258n7qngqqo.1
* Zoom meeting hosted by [[m:Wikimedia Bangladesh|Wikimedia Bangladesh]]
To get started, visit the [[m:Wiki Loves Ramadan 2025|campaign page]] for details, resources, and guidelines: Wiki Loves Ramadan 2025.
Add [[m:Wiki Loves Ramadan 2025/Participant|your community here]], and organized Wiki Loves Ramadan 2025 in your local language.
Whether you’re a first-time editor or an experienced Wikipedian, your contributions matter. Together, we can ensure Islamic cultures and traditions are well-represented and accessible to all.
Feel free to invite your community and friends too. Kindly reach out if you have any questions or need support as you prepare to participate.
Let’s make Wiki Loves Ramadan 2025 a success!
For the [[m:Wiki Loves Ramadan 2025/Team|International Team]] 16 janvier 2025 à 13:08 (CET)
<!-- Message envoyé par User:ZI Jony@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=27568454 -->
== Actualités techniques n° 2025-04 ==
<section begin="technews-2025-W04"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/04|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Administrators can mass-delete multiple pages created by a user or IP address using [[mw:Special:MyLanguage/Extension:Nuke|Extension:Nuke]]. It previously only allowed deletion of pages created in the last 30 days. It can now delete pages from the last 90 days, provided it is targeting a specific user or IP address.</span> [https://phabricator.wikimedia.org/T380846]
* <span lang="en" dir="ltr" class="mw-content-ltr">On [[phab:P72148|wikis that use]] the [[mw:Special:MyLanguage/Help:Patrolled edits|Patrolled edits]] feature, when the rollback feature is used to revert an unpatrolled page revision, that revision will now be marked as "manually patrolled" instead of "autopatrolled", which is more accurate. Some editors that use [[mw:Special:MyLanguage/Help:New filters for edit review/Filtering|filters]] on Recent Changes may need to update their filter settings.</span> [https://phabricator.wikimedia.org/T302140]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:31|la tâche soumise|les {{formatnum:31}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:31||s}} la semaine dernière]]. <span lang="en" dir="ltr" class="mw-content-ltr">For example, the Visual Editor's "Insert link" feature did not always suggest existing pages properly when an editor started typing, which has now been [[phab:T383497|fixed]].</span>
'''Actualités pour la contribution technique'''
* <span lang="en" dir="ltr" class="mw-content-ltr">The Structured Discussion extension (also known as Flow) is being progressively removed from the wikis. This extension is unmaintained and causes issues. It will be replaced by [[mw:Special:MyLanguage/Help:DiscussionTools|DiscussionTools]], which is used on any regular talk page. [[mw:Special:MyLanguage/Structured Discussions/Deprecation#Deprecation timeline|The last group of wikis]] ({{int:project-localized-name-cawikiquote/en}}{{int:comma-separator/en}}{{int:project-localized-name-fiwikimedia/en}}{{int:comma-separator/en}}{{int:project-localized-name-gomwiki/en}}{{int:comma-separator/en}}{{int:project-localized-name-kabwiki/en}}{{int:comma-separator/en}}{{int:project-localized-name-ptwikibooks/en}}{{int:comma-separator/en}}{{int:project-localized-name-sewikimedia/en}}) will soon be contacted. If you have questions about this process, please ping [[m:User:Trizek (WMF)|Trizek (WMF)]] at your wiki.</span> [https://phabricator.wikimedia.org/T380912]
* <span lang="en" dir="ltr" class="mw-content-ltr">The latest quarterly [[mw:Technical_Community_Newsletter/2025/January|Technical Community Newsletter]] is now available. This edition includes: updates about services from the Data Platform Engineering teams, information about Codex from the Design System team, and more.</span>
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/04|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W04"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 21 janvier 2025 à 02:36 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28129769 -->
== Universal Code of Conduct annual review: provide your comments on the UCoC and Enforcement Guidelines ==
<div lang="en" dir="ltr" class="mw-content-ltr">
My apologies for writing in English.
{{Int:Please-translate}}.
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 3 February 2025. This is the first step of several to be taken for the annual review.
[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 24 janvier 2025 à 02:10 (CET)
</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27746256 -->
== Actualités techniques n° 2025-05 ==
<section begin="technews-2025-W05"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/05|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Patrollers and admins - what information or context about edits or users could help you to make patroller or admin decisions more quickly or easily? The Wikimedia Foundation wants to hear from you to help guide its upcoming annual plan. Please consider sharing your thoughts on this and [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Product & Technology OKRs|13 other questions]] to shape the technical direction for next year.</span>
'''Actualités pour la contribution'''
* <span lang="en" dir="ltr" class="mw-content-ltr">iOS Wikipedia App users worldwide can now access a [[mw:Special:MyLanguage/Wikimedia Apps/Team/iOS/Personalized Wikipedia Year in Review/How your data is used|personalized Year in Review]] feature, which provides insights based on their reading and editing history on Wikipedia. This project is part of a broader effort to help welcome new readers as they discover and interact with encyclopedic content.</span>
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] <span lang="en" dir="ltr" class="mw-content-ltr">Edit patrollers now have a new feature available that can highlight potentially problematic new pages. When a page is created with the same title as a page which was previously deleted, a tag ('Recreated') will now be added, which users can filter for in [[{{#special:RecentChanges}}]] and [[{{#special:NewPages}}]].</span> [https://phabricator.wikimedia.org/T56145]
* <span lang="en" dir="ltr" class="mw-content-ltr">Later this week, there will be a new warning for editors if they attempt to create a redirect that links to another redirect (a [[mw:Special:MyLanguage/Help:Redirects#Double redirects|double redirect]]). The feature will recommend that they link directly to the second redirect's target page. Thanks to the user SomeRandomDeveloper for this improvement.</span> [https://phabricator.wikimedia.org/T326056]
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] <span lang="en" dir="ltr" class="mw-content-ltr">Wikimedia wikis allow [[w:en:WebAuthn|WebAuthn]]-based second factor checks (such as hardware tokens) during login, but the feature is [[m:Community Wishlist Survey 2023/Miscellaneous/Fix security key (WebAuthn) support|fragile]] and has very few users. The MediaWiki Platform team is temporarily disabling adding new WebAuthn keys, to avoid interfering with the rollout of [[mw:MediaWiki Platform Team/SUL3|SUL3]] (single user login version 3). Existing keys are unaffected.</span> [https://phabricator.wikimedia.org/T378402]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:30|la tâche soumise|les {{formatnum:30}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:30||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* <span lang="en" dir="ltr" class="mw-content-ltr">For developers that use the [[wikitech:Data Platform/Data Lake/Edits/MediaWiki history dumps|MediaWiki History dumps]]: The Data Platform Engineering team has added a couple of new fields to these dumps, to support the [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|Temporary Accounts]] initiative. If you maintain software that reads those dumps, please review your code and the updated documentation, since the order of the fields in the row will change. There will also be one field rename: in the <bdi lang="zxx" dir="ltr"><code>mediawiki_user_history</code></bdi> dump, the <bdi lang="zxx" dir="ltr"><code>anonymous</code></bdi> field will be renamed to <bdi lang="zxx" dir="ltr"><code>is_anonymous</code></bdi>. The changes will take effect with the next release of the dumps in February.</span> [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/LKMFDS62TXGDN6L56F4ABXYLN7CSCQDI/]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/05|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W05"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 27 janvier 2025 à 23:14 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28149374 -->
== Reminder: first part of the annual UCoC review closes soon ==
<div lang="en" dir="ltr" class="mw-content-ltr">
My apologies for writing in English.
{{Int:Please-translate}}.
This is a reminder that the first phase of the annual review period for the Universal Code of Conduct and Enforcement Guidelines will be closing soon. You can make suggestions for changes through [[d:Q614092|the end of day]], 3 February 2025. This is the first step of several to be taken for the annual review.
[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find a conversation to join on the UCoC page on Meta]]. After review of the feedback, proposals for updated text will be published on Meta in March for another round of community review.
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 3 février 2025 à 01:48 (CET)
</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28198931 -->
== <span lang="en" dir="ltr">Tech News: 2025-06</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W06"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/06|Translations]] are available.
'''Updates for editors'''
* Editors who use the "Special characters" editing-toolbar menu can now see the 32 special characters you have used most recently, across editing sessions on that wiki. This change should help make it easier to find the characters you use most often. The feature is in both the 2010 wikitext editor and VisualEditor. [https://phabricator.wikimedia.org/T110722]
* Editors using the 2010 wikitext editor can now create sublists with correct indentation by selecting the line(s) you want to indent and then clicking the toolbar buttons.[https://phabricator.wikimedia.org/T380438] You can now also insert <code><nowiki><code></nowiki></code> tags using a new toolbar button.[https://phabricator.wikimedia.org/T383010] Thanks to user stjn for these improvements.
* Help is needed to ensure the [[mw:Special:MyLanguage/Citoid/Enabling Citoid on your wiki|citation generator]] works properly on each wiki.
** (1) Administrators should update the local versions of the page <code dir=ltr>MediaWiki:Citoid-template-type-map.json</code> to include entries for <code dir=ltr>preprint</code>, <code dir=ltr>standard</code>, and <code dir=ltr>dataset</code>; Here are example diffs to replicate [https://en.wikipedia.org/w/index.php?title=MediaWiki%3ACitoid-template-type-map.json&diff=1189164774&oldid=1165783565 for 'preprint'] and [https://en.wikipedia.org/w/index.php?title=MediaWiki%3ACitoid-template-type-map.json&diff=1270832208&oldid=1270828390 for 'standard' and 'dataset'].
** (2.1) If the citoid map in the citation template used for these types of references is missing, [[mediawikiwiki:Citoid/Enabling Citoid on your wiki#Step 2.a: Create a 'citoid' maps value for each citation template|one will need to be added]]. (2.2) If the citoid map does exist, the TemplateData will need to be updated to include new field names. Here are example updates [https://en.wikipedia.org/w/index.php?title=Template%3ACitation%2Fdoc&diff=1270829051&oldid=1262470053 for 'preprint'] and [https://en.wikipedia.org/w/index.php?title=Template%3ACitation%2Fdoc&diff=1270831369&oldid=1270829480 for 'standard' and 'dataset']. The new fields that may need to be supported are <code dir=ltr>archiveID</code>, <code dir=ltr>identifier</code>, <code dir=ltr>repository</code>, <code dir=ltr>organization</code>, <code dir=ltr>repositoryLocation</code>, <code dir=ltr>committee</code>, and <code dir=ltr>versionNumber</code>. [https://phabricator.wikimedia.org/T383666]
* One new wiki has been created: a {{int:project-localized-name-group-wikipedia/en}} in [[d:Q15637215|Central Kanuri]] ([[w:knc:|<code>w:knc:</code>]]) [https://phabricator.wikimedia.org/T385181]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:27}} community-submitted {{PLURAL:27|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the [[mediawikiwiki:Special:MyLanguage/Help:Extension:Wikisource/Wikimedia OCR|OCR (optical character recognition) tool]] used for Wikisource now supports a new language, Church Slavonic. [https://phabricator.wikimedia.org/T384782]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/06|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W06"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 4 février 2025 à 01:08 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28203495 -->
== <span lang="en" dir="ltr">Tech News: 2025-07</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W07"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/07|Translations]] are available.
'''Weekly highlight'''
* The Product and Technology Advisory Council (PTAC) has published [[m:Special:MyLanguage/Product and Technology Advisory Council/February 2025 draft PTAC recommendation for feedback|a draft of their recommendations]] for the Wikimedia Foundation's Product and Technology department. They have recommended focusing on [[m:Special:MyLanguage/Product and Technology Advisory Council/February 2025 draft PTAC recommendation for feedback/Mobile experiences|mobile experiences]], particularly contributions. They request community [[m:Talk:Product and Technology Advisory Council/February 2025 draft PTAC recommendation for feedback|feedback at the talk page]] by 21 February.
'''Updates for editors'''
* The "Special pages" portlet link will be moved from the "Toolbox" into the "Navigation" section of the main menu's sidebar by default. This change is because the Toolbox is intended for tools relating to the current page, not tools relating to the site, so the link will be more logically and consistently located. To modify this behavior and update CSS styling, administrators can follow the instructions at [[phab:T385346|T385346]]. [https://phabricator.wikimedia.org/T333211]
* As part of this year's work around improving the ways readers discover content on the wikis, the Web team will be running an experiment with a small number of readers that displays some suggestions for related or interesting articles within the search bar. Please check out [[mw:Special:MyLanguage/Reading/Web/Content Discovery Experiments#Experiment 1: Display article recommendations in more prominent locations, search|the project page]] for more information.
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Advanced item]] Template editors who use TemplateStyles can now customize output for users with specific accessibility needs by using accessibility related media queries (<code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion prefers-reduced-motion]</code>, <code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-transparency prefers-reduced-transparency]</code>, <code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast prefers-contrast]</code>, and <code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors forced-colors]</code>). Thanks to user Bawolff for these improvements. [https://phabricator.wikimedia.org/T384175]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:22}} community-submitted {{PLURAL:22|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the global blocks log will now be shown directly on the {{#special:CentralAuth}} page, similarly to global locks, to simplify the workflows for stewards. [https://phabricator.wikimedia.org/T377024]
'''Updates for technical contributors'''
* Wikidata [[d:Special:MyLanguage/Help:Default values for labels and aliases|now supports a special language as a "default for all languages"]] for labels and aliases. This is to avoid excessive duplication of the same information across many languages. If your Wikidata queries use labels, you may need to update them as some existing labels are getting removed. [https://phabricator.wikimedia.org/T312511]
* The function <code dir="ltr">getDescription</code> was invoked on every Wiki page read and accounts for ~2.5% of a page's total load time. The calculated value will now be cached, reducing load on Wikimedia servers. [https://phabricator.wikimedia.org/T383660]
* As part of the RESTBase deprecation [[mw:RESTBase/deprecation|effort]], the <code dir="ltr">/page/related</code> endpoint has been blocked as of February 6, 2025, and will be removed soon. This timeline was chosen to align with the deprecation schedules for older Android and iOS versions. The stable alternative is the "<code dir="ltr">morelike</code>" action API in MediaWiki, and [[gerrit:c/mediawiki/services/mobileapps/+/982154/13/pagelib/src/transform/FooterReadMore.js|a migration example]] is available. The MediaWiki Interfaces team [[phab:T376297|can be contacted]] for any questions. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/GFC2IJO7L4BWO3YTM7C5HF4MCCBE2RJ2/]
'''In depth'''
* The latest quarterly [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/January|Language and Internationalization newsletter]] is available. It includes: Updates about the "Contribute" menu; details on some of the newest language editions of Wikipedia; details on new languages supported by the MediaWiki interface; updates on the Community-defined lists feature; and more.
* The latest [[mw:Extension:Chart/Project/Updates#January 2025: Better visibility into charts and tabular data usage|Chart Project newsletter]] is available. It includes updates on the progress towards bringing better visibility into global charts usage and support for categorizing pages in the Data namespace on Commons.
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/07|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W07"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 11 février 2025 à 01:11 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28231022 -->
== Actualités techniques n° 2025-08 ==
<section begin="technews-2025-W08"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/08|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Les communautés utilisant les outils de croissance peuvent désormais mettre en avant un évènement pour les nouveaux contributeurs sur la <code>{{#special:Homepage}}</code>. Cette fonctionnalité aidera les nouveaux venus à être informés des activités d'édition auxquels ils peuvent participer. Les administrateurs peuvent ajouter un nouvel évènement à mettre en avant sur <code>{{#special:CommunityConfiguration}}</code>. Pour en apprendre davantage sur cette nouvelle fonctionnalité, vous pouvez lire [[diffblog:2025/02/12/community-updates-module-connecting-newcomers-to-your-initiatives/|l'annonce sur Diff]], la [[mw:Special:MyLanguage/Help:Growth/Tools/Community updates module|documentation]] ou [[mw:Talk:Growth|contacter l'équipe Croissance]].
'''Actualités pour la contribution'''
[[File:Page Frame Features on desktop.png|thumb|Mise en évidence des améliorations aux pages de discussion]]
* À partir de la semaine prochaine, les pages de discussions des wikis suivants recevront [[diffblog:2024/05/02/making-talk-pages-better-for-everyone/|une nouvelle présentation]] : {{int:project-localized-name-eswiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-frwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-itwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-jawiki/fr}}. Ce changement a été largement testé en tant que fonctionnalité beta et il s'agit de la dernière étape des [[mw:Special:MyLanguage/Talk pages project/Feature summary|améliorations aux pages de discussion]]. [https://phabricator.wikimedia.org/T379102]
* Vous pouvez désormais visualiser une page de redirection directement depuis ses pages d'action, comme la page d'historique. Auparavant, vous étiez automatiquement redirigé vers la page cible et deviez manuellement revenir à la page de redirection. Ce changement devrait aider les rédacteurs travaillant avec les redirections. Merci à stjn pour cette amélioration. [https://phabricator.wikimedia.org/T5324]
* Quand une référence est utilisée de nombreuses fois, les wikis affichent actuellement des nombres comme 1.23 ou des marqueurs avec des lettres comme a, b, c dans la liste de références. Avant, quand le nombre de références était trop important et que toutes les lettres avaient été utilisées, un [[MediaWiki:Cite error references no backlink label|message d'erreur]] était affiché. Dans le cadre des travaux pour la [[phab:T383036|modernisation de la personnalisation des références]], ces erreurs ne seront plus affichées et des marqueurs numériques comme 1.27 seront utilisés par défaut après épuisement des marqueurs alphabétiques.
* Les entrées de journal pour chaque changement aux groupes utilisateur d'un éditeur ont été clarifiés pour indiquer exactement ce qui a été modifié. Elles contenaient auparavant les deux listes des groupes avant et après le changement. Les traducteurs sont invités à [[phab:T369466|aider à traduire les messages système associés]]. Merci à Msz2001 pour ces améliorations.
* Un nouveau filtre a été ajouté à [[{{#special:Nuke}}]] — outil permettant aux administrateurs de supprimer en masse des pages — pour permettre aux utilisateurs de filtrer les pages en fonction de leur taille en octets. Cela permet par exemple de supprimer uniquement des pages inférieures à une certaine taille. [https://phabricator.wikimedia.org/T378488]
* Les non-administrateurs peuvent maintenant voir quelles pages peuvent être supprimées à l'aide de [[{{#special:Nuke}}]]. Merci à MolecularPilot pour cette amélioration et les précédentes. [https://phabricator.wikimedia.org/T376378]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:25|la tâche soumise|les {{formatnum:25}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:25||s}} la semaine dernière]]. Par exemple, un bug a été corrigé dans la configuration du format de fichier vidéo AV1, ce qui permet à ces fichiers d'être lus à nouveau. [https://phabricator.wikimedia.org/T382193]
'''Actualités pour la contribution technique'''
* Parsoid Read Views sera déployé sur la plupart des Wiktionnaires dans les prochaines semaines, à la suite de la transition avec succès des Wikivoyage à Parsoid l'année dernière. Pour davantage d'informations, voir la page du projet [[mw:Special:MyLanguage/Parsoid/Parser Unification|Parsoid/Parser Unification]]. [https://phabricator.wikimedia.org/T385923][https://phabricator.wikimedia.org/T371640]
* Les développeurs d'outils sur wiki sont informés que <code dir=ltr>mw.Uri</code> est obsolète. Les outils nécessitant <code dir=ltr>mw.Uri</code> doivent déclarer explicitement <code dir=ltr>mediawiki.Uri</code> comme une dépendance de ResourceLoader, et devraient migrer vers l'API <code dir=ltr>URL</code> native du navigateur prochainement. [https://phabricator.wikimedia.org/T384515]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/08|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W08"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 17 février 2025 à 22:16 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28275610 -->
== <span lang="en" dir="ltr"> Upcoming Language Community Meeting (Feb 28th, 14:00 UTC) and Newsletter</span> ==
<div lang="en" dir="ltr">
<section begin="message"/>
Hello everyone!
[[File:WP20Symbols WIKI INCUBATOR.svg|right|frameless|150x150px|alt=An image symbolising multiple languages]]
We’re excited to announce that the next '''Language Community Meeting''' is happening soon, '''February 28th at 14:00 UTC'''! If you’d like to join, simply sign up on the '''[[mw:Wikimedia_Language_and_Product_Localization/Community_meetings#28_February_2025|wiki page]]'''.
This is a participant-driven meeting where we share updates on language-related projects, discuss technical challenges in language wikis, and collaborate on solutions. In our last meeting, we covered topics like developing language keyboards, creating the Moore Wikipedia, and updates from the language support track at Wiki Indaba.
'''Got a topic to share?''' Whether it’s a technical update from your project, a challenge you need help with, or a request for interpretation support, we’d love to hear from you! Feel free to '''reply to this message''' or add agenda items to the document '''[[etherpad:p/language-community-meeting-feb-2025|here]]'''.
Also, we wanted to highlight that the sixth edition of the Language & Internationalization newsletter (January 2025) is available here: [[:mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/January|Wikimedia Language and Product Localization/Newsletter/2025/January]]. This newsletter provides updates from the October–December 2024 quarter on new feature development, improvements in various language-related technical projects and support efforts, details about community meetings, and ideas for contributing to projects. To stay updated, you can subscribe to the newsletter on its wiki page: [[:mw:Wikimedia Language and Product Localization/Newsletter|Wikimedia Language and Product Localization/Newsletter]].
We look forward to your ideas and participation at the language community meeting, see you there!
<section end="message"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 22 février 2025 à 09:28 (CET)
<!-- Message envoyé par User:SSethi (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28217779 -->
== Actualités techniques n° 2025-09 ==
<section begin="technews-2025-W09"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/09|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les administrateurs peuvent désormais personnaliser la manière dont les catégories de [[m:Special:MyLanguage/User language|Babel]] sont créées en utilisant [[{{#special:CommunityConfiguration/Babel}}]]. Ils peuvent renommer les catégories de langues, choisir si elles doivent être créées automatiquement et ajuster d'autres paramètres. [https://phabricator.wikimedia.org/T374348]
* Le portail <bdi lang="en" dir="ltr">[https://www.wikimedia.org/ wikimedia.org]</bdi> a été mis à jour pour moderniser et améliorer l'accessibilité de nos pages de portail. Il dispose désormais d'un meilleur support pour les mises en page mobiles, de meilleures formulations et liens et d'un support linguistique amélioré. De plus, tous les portails du projet Wikimedia, comme <bdi lang="en" dir="ltr">[https://wikibooks.org wikibooks.org]</bdi>, prennent maintenant en charge le mode sombre lorsqu'un lecteur utilise ce paramètre système. [https://phabricator.wikimedia.org/T373204][https://phabricator.wikimedia.org/T368221][https://meta.wikimedia.org/wiki/Project_portals]
* Un nouveau wiki a été créé : un {{int:project-localized-name-group-wiktionary/fr}} en [[d:Q33965|Santali]] ([[wikt:sat:|<code>wikt:sat:</code>]]) [https://phabricator.wikimedia.org/T386619]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:30|la tâche soumise|les {{formatnum:30}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:30||s}} la semaine dernière]]. Par exemple, un bogue qui empêchait de cliquer sur les résultats de recherche de l'interface web sur certaines configurations mobiles avec Firefox a été corrigé. [https://phabricator.wikimedia.org/T381289]
'''Rencontres et évènements'''
* La prochaine rencontre de la communauté linguistique aura lieu le 28 février à [https://zonestamp.toolforge.org/1740751200 14:00 UTC]. La rencontre de cette semaine couvrira : les points importants et mises-à-jour techniques pour les langues samis, les contributions à translatewiki.net de la part de la communauté Bahasa Lampung en Indonésie et une FAQ technique. Si vous souhaitez participer, inscrivez-vous sur la [[mw:Wikimedia Language and Product Localization/Community meetings#28 February 2025|page wiki]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/09|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W09"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 25 février 2025 à 01:41 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28296129 -->
== Actualités techniques n° 2025-10 ==
<section begin="technews-2025-W10"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/10|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les utilisateurs et utilisatrices connectés utilisant l’affichage mobile peuvent désormais modifier une page complète. Le lien « {{int:Minerva-page-actions-editfull}} » est accessible dans le menu « {{int:minerva-page-actions-overflow}} » de la barre d’outils. Ce lien était auparavant disponible uniquement lorsque le [[mw:Special:MyLanguage/Reading/Web/Advanced mobile contributions|mode avancé]] était activé. [https://phabricator.wikimedia.org/T387180]
* Les admins d’interface peuvent désormais retirer les utilisations de la classe CSS « <code dir="ltr">mw-ref</code> » de leur <bdi lang="en" dir="ltr">[[MediaWiki:Common.css]]</bdi> local. Cette classe de l’extension <span lang="en">Cite</span> est obsolète. La liste des wikis l’utilisant peut être trouvée par [https://global-search.toolforge.org/?q=mw-ref%5B%5E-a-z%5D®ex=1&namespaces=8&title=.*css cette recherche globale] et dans [https://ace.wikipedia.org/w/index.php?title=MediaWiki:Common.css&oldid=145662#L-139--L-144 cet exemple]. D’autres informations sur les manières d’aider sont données sur la [[mw:Parsoid/Parser Unification/Cite CSS|page du projet de migration du CSS]]. Les appels de note (<code dir="ltr">[1]</code>) sont désormais rendus par [[mw:Special:MyLanguage/Parsoid|Parsoid]] ; le CSS obsolète n’est plus nécessaire. Le CSS pour les rétroliens « <code dir="ltr">mw:referencedBy</code> » doit rester en place pour le moment. Ce nettoyage ne devrait pas avoir d’effet visible pour les lecteurs et lectrices. Merci d’aider à retirer ce code avant le 30 mars, après quoi l’équipe de développement le fera pour vous.
* Lorsque les contributeurs ajoutent un fichier (par exemple <code><nowiki>[[File:MediaWiki.png]]</nowiki></code>) sur une page protégée par une protection en cascade, le logiciel ne restreindra plus les modifications à la page de description du fichier, mais uniquement aux nouveaux téléchargements de fichiers. [https://phabricator.wikimedia.org/T24521] A l’inverse, la transclusion d’une page de description de fichier (par exemple <code><nowiki>{{:File:MediaWiki.png}}</nowiki></code>) dans une page protégée en cascade provoquera désormais une restriction des modifications à la page.[https://phabricator.wikimedia.org/T62109]
* Remettre un fichier dans une version antérieure nécessitera désormais les mêmes autorisations que le téléchargement d'une nouvelle version du fichier. Le logiciel vérifie désormais la possession des droits « <i lang="en">reupload</i> » ou « <i lang="en">reuplod-own</i> » [https://phabricator.wikimedia.org/T304474], et respecte la protection en cascade. [https://phabricator.wikimedia.org/T140010]
* Lorsque les admins listent des pages à supprimer avec l’outil Nuke, ils peuvent désormais également répertorier les pages de discussion associées et les redirections à supprimer, en plus des pages créées par la cible, plutôt que de devoir supprimer manuellement ces pages. [https://phabricator.wikimedia.org/T95797]
* La mise à jour [[m:Special:MyLanguage/Tech/News/2025/03|mentionnée précédemment]] de la connexion utilisateur unifiée, qui prendra en compte les restrictions du navigateur sur les cookies inter-domaines en déplaçant la connexion et la création de compte vers un domaine central, sera déployée pour tous les utilisateurs et utilisatrices en mars et avril. L'équipe prévoit de l'activer pour toutes les nouvelles créations de compte sur les wikis du [[wikitech:Deployments/Train#Tuesday|groupe 0]] cette semaine. Consultez la [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3#Deployment|page du projet SUL3]] pour plus de détails et un calendrier mis à jour.
* Depuis la semaine dernière, un bogue cause l'affichage de certaines icônes d'interface sous forme de carrés noirs jusqu'à ce que la page soit entièrement chargée. Cela sera corrigé cette semaine. [https://phabricator.wikimedia.org/T387351]
* Un nouveau wiki a été créé : une {{int:project-localized-name-group-wikipedia/fr}} en [[d:Q2044560|sylheti]] ([[w:syl:|<code>w:syl:</code>]]) [https://phabricator.wikimedia.org/T386441]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]]. Par exemple, un bogue avec le chargement d'images dans de très anciennes versions du navigateur Firefox sur mobile a été corrigé. [https://phabricator.wikimedia.org/T386400]
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.19|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/10|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W10"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 4 mars 2025 à 03:30 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28334563 -->
== Universal Code of Conduct annual review: proposed changes are available for comment ==
<div lang="en" dir="ltr" class="mw-content-ltr">
My apologies for writing in English.
{{Int:Please-translate}}.
I am writing to you to let you know that [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/Proposed_Changes|proposed changes]] to the [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Universal Code of Conduct (UCoC) Enforcement Guidelines]] and [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|Universal Code of Conduct Coordinating Committee (U4C) Charter]] are open for review. '''[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/Proposed_Changes|You can provide feedback on suggested changes]]''' through the [[d:Q614092|end of day]] on Tuesday, 18 March 2025. This is the second step in the annual review process, the final step will be community voting on the proposed changes.
[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find relevant links about the process on the UCoC annual review page on Meta]].
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] 7 mars 2025 à 19:50 (CET)
</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28307738 -->
== Actualités techniques n° 2025-11 ==
<section begin="technews-2025-W11"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/11|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les contributeurs qui utilisent des gestionnaires de mots de passe sur plusieurs wikis peuvent remarquer des changements à l’avenir. La manière dont nos wikis fournissent des informations aux gestionnaires de mots de passe sur la réutilisation des mots de passe entre les domaines a été récemment mise à jour, de sorte que certains gestionnaires de mots de passe peuvent maintenant vous proposer des identifiants de connexion que vous avez sauvegardés pour un autre site Wikimedia. Certains gestionnaires de mots de passe l'ont déjà fait, et le font maintenant pour d'autres domaines de Wikimedia. Cela fait partie du projet [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3|SUL3]] qui vise à améliorer le fonctionnement de notre connexion unifiée et à la rendre compatible avec les changements en cours dans les navigateurs web que nous utilisons. [https://phabricator.wikimedia.org/T385520][https://phabricator.wikimedia.org/T384844]
* L'équipe des applications Wikipédia invite les utilisateurs intéressés à contribuer à l'amélioration de l'utilisation de Wikipédia hors ligne ou en internet limité. Après les discussions de [[m:Afrika Baraza|Afrika Baraza]] et la dernière [[m:Special:MyLanguage/ESEAP Hub/Meetings|conférence ESEAP]], des défis clés comme la recherche, la modification et l'accès hors ligne sont explorés, avec des groupes de discussion à venir pour approfondir ces sujets. Toutes les langues sont les bienvenues et des interprètes seront disponibles. Vous souhaitez partager vos idées ? [[mw:Special:MyLanguage/Wikimedia Apps/Improving Wikipedia Mobile Apps for Offline & Limited Internet Use|Participez à la discussion]] ou envoyez un courriel à <bdi lang="en" dir="ltr">aramadan@wikimedia.org</bdi> !
* Tous les wikis seront en lecture seule pendant quelques minutes le 19 mars, à [https://zonestamp.toolforge.org/1742392800 14 h UTC]. De plus amples informations seront publiées dans les ''Actualités techniques'' et seront également publiées sur chaque wiki dans les semaines à venir.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.20|MediaWiki]]
'''En détails'''
* La dernière [[mw:Special:MyLanguage/Growth/Newsletters/33|infolettre trimestrielle du département Croissance]] est disponible. Elle présente : le lancement du module Actualités de la communauté, les dernières modifications de la configuration communautaire et le test à venir des suggestions d'articles pour les personnes contribuant pour la première fois.
* Une ancienne API utilisée dans l'application Android Wikipedia sera supprimée à la fin du mois de mars. Il n'y a pas d'utilisation logicielle en cours, mais les utilisateurs de l'application dont la version date de plus de 6 mois au moment de la suppression (2025-03-31), n'auront plus accès à la fonction Suggested Edits, jusqu'à ce qu'ils mettent à jour leur application. Vous pouvez [[diffblog:2025/02/24/sunset-of-wikimedia-recommendation-api/|lire plus de détails sur ce changement]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/11|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W11"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 11 mars 2025 à 00:09 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28372257 -->
== Votre wiki sera bientôt en lecture seule ==
<section begin="server-switch"/><div class="plainlinks">
[[:m:Special:MyLanguage/Tech/Server switch|Lire ce message dans une autre langue]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-Tech%2FServer+switch&language=&action=page&filter= {{int:please-translate}}]
La [[foundation:|Fondation Wikimedia]] va basculer le trafic entre ses centres de données. Cela permettra de s’assurer que Wikipédia et les autres wikis de Wikimedia peuvent rester en ligne même après une catastrophe.
Le trafic sera basculé le '''{{#time:j xg|2025-03-19|fr}}'''. La bascule débutera à '''[https://zonestamp.toolforge.org/{{#time:U|2025-03-19T14:00|en}} {{#time:H:i e|2025-03-19T14:00}}]'''.
Malheureusement, en raison de certaines limites de [[mw:Special:MyLanguage/Manual:What is MediaWiki?|MediaWiki]], toutes les modifications de pages devront être arrêtées durant le passage d’un centre de données à l’autre. Nous nous excusons pour ce dérangement, que nous nous efforçons de réduire pour le futur.
Une bannière sera affichée sur tous les wikis 30 minutes avant le début de l’opération. Cette bannière restera visible jusqu’à la fin de l’opération.
'''Pendant une courte période, vous pourrez lire les wikis mais pas les modifier.'''
*Vous ne pourrez pas effectuer de modification pendant une durée pouvant aller jusqu’à une heure, le {{#time:l j xg Y|2025-03-19|fr}}.
*Si vous essayez de faire une modification ou de sauvegarder pendant cette période, vous verrez un message d’erreur. Nous espérons qu’aucune modification ne sera perdue durant ce temps, mais nous ne pouvons le garantir. Si vous voyez un message d’erreur, merci de patienter jusqu’au retour à la normale. Vous pourrez alors enregistrer votre modification. Nous vous conseillons cependant de faire une copie de votre modification avant, au cas où.
''Autres conséquences :''
*Les tâches de fond seront ralenties et certaines pourraient être stoppées. Les liens rouges ne seront pas mis à jour aussi vite que d’habitude. Si vous créez un article qui est déjà lié depuis une autre page, le lien rouge pourrait rester rouge plus longtemps que d’habitude. Certains scripts ayant un long temps d’exécution devront être stoppés.
* Le déploiement de code devrait se dérouler comme chaque semaine. Cependant, certains codes particuliers pourraient être gelés si l’opération le nécessitait.
* [[mw:Special:MyLanguage/GitLab|GitLab]] sera indisponible durant environ 90 minutes.
Ce projet pourra être reporté si nécessaire. Vous pouvez [[wikitech:Switch_Datacenter|consulter le calendrier sur wikitech.wikimedia.org]]. Tout changement sera annoncé dans le calendrier.
'''Merci de partager ces informations avec votre communauté.'''</div><section end="server-switch"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 15 mars 2025 à 00:14 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=28307742 -->
== <span lang="en" dir="ltr">Tech News: 2025-12</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W12"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/12|Translations]] are available.
'''Weekly highlight'''
* Twice a year, around the equinoxes, the Wikimedia Foundation's Site Reliability Engineering (SRE) team performs [[m:Special:MyLanguage/Tech/Server switch|a datacenter server switchover]], redirecting all traffic from one primary server to its backup. This provides reliability in case of a crisis, as we can always fall back on the other datacenter. [http://listen.hatnote.com/ Thanks to the Listen to Wikipedia] tool, you can hear the switchover take place: Before it begins, you'll hear the steady stream of edits; Then, as the system enters a brief read-only phase, the sound stops for a couple of minutes, before resuming after the switchover. You can [[diffblog:2025/03/12/hear-that-the-wikis-go-silent-twice-a-year/|read more about the background and details of this process on the Diff blog]]. If you want to keep an ear out for the next server switchover, listen to the wikis on [https://zonestamp.toolforge.org/1742392800 March 19 at 14:00 UTC].
'''Updates for editors'''
* The [https://test.wikipedia.org/w/index.php?title=Special:ContentTranslation&filter-type=automatic&filter-id=previous-edits&active-list=suggestions&from=en&to=es improved Content Translation tool dashboard] is now available in [[phab:T387820|10 Wikipedias]] and will be available for all Wikipedias [[phab:T387821|soon]]. With [[mw:Special:MyLanguage/Content translation#Improved translation experience|the unified dashboard]], desktop users can now: Translate new sections of an article; Discover and access topic-based [https://ig.m.wikipedia.org/w/index.php?title=Special:ContentTranslation&active-list=suggestions&from=en&to=ig&filter-type=automatic&filter-id=previous-edits article suggestion filters] (initially available only for mobile device users); Discover and access the [[mw:Special:MyLanguage/Translation suggestions: Topic-based & Community-defined lists|Community-defined lists]] filter, also known as "Collections", from wiki-projects and campaigns.
* On Wikimedia Commons, a [[c:Commons:WMF support for Commons/Upload Wizard Improvements#Improve category selection|new system to select the appropriate file categories]] has been introduced: if a category has one or more subcategories, users will be able to click on an arrow that will open the subcategories directly within the form, and choose the correct one. The parent category name will always be shown on top, and it will always be possible to come back to it. This should decrease the amount of work for volunteers in fixing/creating new categories. The change is also available on mobile. These changes are part of planned improvements to the UploadWizard.
* The Community Tech team is seeking wikis to join a pilot for the [[m:Special:MyLanguage/Community Wishlist Survey 2023/Multiblocks|Multiblocks]] feature and a refreshed Special:Block page in late March. Multiblocks enables administrators to impose multiple different types of blocks on the same user at the same time. If you are an admin or steward and would like us to discuss joining the pilot with your community, please leave a message on the [[m:Talk:Community Wishlist Survey 2023/Multiblocks|project talk page]].
* Starting March 25, the Editing team will test a new feature for Edit Check at [[phab:T384372|12 Wikipedias]]: [[mw:Special:MyLanguage/Help:Edit check#Multi-check|Multi-Check]]. Half of the newcomers on these wikis will see all [[mw:Special:MyLanguage/Help:Edit check#ref|Reference Checks]] during their edit session, while the other half will continue seeing only one. The goal of this test is to see if users are confused or discouraged when shown multiple Reference Checks (when relevant) within a single editing session. At these wikis, the tags used on edits that show References Check will be simplified, as multiple tags could be shown within a single edit. Changes to the tags are documented [[phab:T373949|on Phabricator]]. [https://phabricator.wikimedia.org/T379131]
* The [[m:Special:MyLanguage/Global reminder bot|Global reminder bot]], which is a service for notifying users that their temporary user-rights are about to expire, now supports using the localized name of the user-rights group in the message heading. Translators can see the [[m:Global reminder bot/Translation|listing of existing translations and documentation]] to check if their language needs updating or creation.
* The [[Special:GlobalPreferences|GlobalPreferences]] gender setting, which is used for how the software should refer to you in interface messages, now works as expected by overriding the local defaults. [https://phabricator.wikimedia.org/T386584]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:26}} community-submitted {{PLURAL:26|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the Wikipedia App for Android had a bug fixed for when a user is browsing and searching in multiple languages. [https://phabricator.wikimedia.org/T379777]
'''Updates for technical contributors'''
* Later this week, the way that Codex styles are loaded will be changing. There is a small risk that this may result in unstyled interface message boxes on certain pages. User generated content (e.g. templates) is not impacted. Gadgets may be impacted. If you see any issues [[phab:T388847|please report them]]. See the linked task for details, screenshots, and documentation on how to fix any affected gadgets.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.44/wmf.21|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/12|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W12"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 18 mars 2025 à 00:48 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28412594 -->
== Actualités techniques n° 2025-13 ==
<section begin="technews-2025-W13"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/13|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* La Fondation Wikimédia souhaite recueillir vos commentaires sur les [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Product & Technology OKRs|ébauches des objectifs et des résultats-clés qui façonneront les priorités de la Fondation en matière de produits et de technologies]] pour la prochaine année fiscale (commençant en juillet). Les objectifs sont des domaines généraux et les résultats-clés permettent de mesurer leur réalisation. N'hésitez pas à partager vos commentaires sur la page de discussion, dans n'importe quelle langue, idéalement avant fin avril.
'''Actualités pour la contribution'''
* L'extension [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|CampaignEvents]] sera déployée sur plusieurs wikis (voir le [[m:Special:MyLanguage/CampaignEvents/Deployment status#Global Deployment Plan|plan de déploiement]] pour plus de détails) en avril 2025, et l'équipe a commencé le processus d'engagement des communautés sur les wikis identifiés. L'extension fournit des outils pour organiser, gérer et promouvoir des activités collaboratives (comme des événements, des edit-a-thons et des WikiProjects) sur les wikis. L'extension comporte trois outils : [[m:Special:MyLanguage/Event Center/Registration|Inscription à l'événement]], [[m:Special:MyLanguage/CampaignEvents/Collaboration list|Liste de collaboration]] et [[m:Special:MyLanguage/Campaigns/Foundation Product Team/Invitation list|Liste d'invitation]]. Elle est actuellement présente sur 13 Wikipédias, dont la Wikipédia en anglais, la Wikipédia en français et la Wikipédia en espagnol, ainsi que sur Wikidata. Les questions ou demandes peuvent être adressées sur la [[mw:Help talk:Extension:CampaignEvents|page de discussion de l'extension]] ou sur Phabricator (avec l'étiquette <bdi lang="en" dir="ltr" style="white-space: nowrap;">#campaigns-product-team</bdi>).
* À partir de la semaine du 31 mars, les wikis pourront définir quels groupes d'utilisateurs peuvent voir les inscriptions privées dans [[m:Special:MyLanguage/Event Center/Registration|Inscription à l'événement]], dans le cadre de l'extension [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|CampaignEvents]]. Par défaut, les organisateurs d'événements et les administrateurs du wiki local pourront voir les inscriptions privées. Il s'agit d'un changement par rapport au réglage actuel qui permet seulement aux organisateurs de l'événement de voir les inscriptions privées. Les wikis peuvent modifier la configuration par défaut en demandant un changement de configuration dans Phabricator (et en ajoutant l'étiquette <bdi lang="en" dir="ltr" style="white-space: nowrap;">#campaigns-product-team</bdi>). Les participants aux événements passés peuvent annuler leur inscription à tout moment.
* Les administrateurs des wikis qui disposent d'une barre latérale <bdi lang="en" dir="ltr">[[MediaWiki:Sidebar]]</bdi> personnalisée doivent vérifier si elle contient une entrée pour la liste {{int:specialpages}}. Si ce n'est pas le cas, ils doivent l'ajouter en utilisant <code dir=ltr style="white-space: nowrap;">* specialpages-url|specialpages</code>. Les wikis disposant d'une barre latérale par défaut verront le lien déplacé de la boîte à outils de la page vers le menu de la barre latérale en avril. [https://phabricator.wikimedia.org/T388927]
* L'habillage Minerva (web mobile) combine les notifications d'avis et d'alertes dans l'icône de cloche ([[File:OOjs UI icon bell.svg|16px|link=|class=skin-invert]]). Il existait depuis longtemps un bogue qui faisait qu'une indication de nouvelles notifications n'était affichée que si vous aviez des alertes que vous n'avez pas vues. Ce problème est désormais résolu. À l'avenir, les utilisateurs de Minerva remarqueront un compteur au-dessus de l'icône de la cloche lorsque vous avez un ou plusieurs notifications et/ou alertes non vues. [https://phabricator.wikimedia.org/T344029]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* VisualEditor a introduit un [[mw:VisualEditor/Hooks|nouveau hook côté client]] pour les développeurs à utiliser lors de l'intégration avec le cycle de vie de la cible VisualEditor. Ce hook devrait remplacer les hooks existants liés au cycle de vie et être plus cohérent entre les différentes plateformes. De plus, le nouveau hook s'appliquera aux utilisations de VisualEditor en dehors de l'édition complète d'articles, permettant aux gadgets d'interagir avec l'éditeur dans DiscussionTools également. L'équipe d'édition a l'intention de déprécier et éventuellement de supprimer les hooks de l'ancien cycle de vie, donc tous les cas d'utilisation que ce nouveau hook ne couvre pas seraient intéressants pour l'équipe et peuvent être [[phab:T355555|partagés dans la tâche]].
* Les développeurs qui utilisent la bibliothèque JavaScript <code dir=ltr>mw.Api</code> peuvent désormais identifier l'outil qui l'utilise avec le paramètre <code dir=ltr>userAgent</code> : <code dir=ltr>var api = new mw.Api( { userAgent: 'GadgetNameHere/1.0.1' } );</code>. Si vous gérez un gadget ou un script utilisateur, veuillez définir un agent utilisateur, car cela facilite la maintenance de la bibliothèque et du serveur et permet de différencier le trafic légitime du trafic illégitime. [https://phabricator.wikimedia.org/T373874][https://foundation.wikimedia.org/wiki/Policy:Wikimedia_Foundation_User-Agent_Policy]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.22|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/13|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W13"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 24 mars 2025 à 23:42 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28443127 -->
== Actualités techniques n° 2025-14 ==
<section begin="technews-2025-W14"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/14|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* L'équipe Contribution travaille sur une nouvelle [[mw:Special:MyLanguage/Edit Check|vérification des modifications]] : le [[mw:Special:MyLanguage/Edit check#26 March 2025|contrôle des éloges]]. L'objectif de cette vérification est d’identifier les termes non neutres saisis lors de la modification d’une page Wikipédia, afin d’informer l’auteur ou autrice que son texte devrait peut-être être modifié avant publication. Ce projet n’en est qu’à ses débuts ; l’équipe a besoin de l’avis des communautés. Dans [[phab:T389445|cette tâche Phabricator]], l’équipe rassemble les recommendations internes des wikis, les modèles utilisés pour étiqueter les articles non neutres et les termes (jargon et mots-clés) utilisés dans les résumés de modification pour les langues étudiées actuellement. Vous pouvez participer en modifiant le tableau sur Phabricator, en commentant la tâche ou en envoyant directement un message à [[m:user:Trizek (WMF)|Trizek (WMF)]].
* La [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3|connexion utilisateur unique]] (SUL) a été mise à jour sur tous les wikis afin de déplacer la connexion et la création de compte vers un domaine central. Cela rend la connexion des contributeurs compatible avec les restrictions des navigateurs sur les cookies inter-domaines, qui ont empêché les utilisateurs de certains navigateurs de rester connectés.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:35|la tâche soumise|les {{formatnum:35}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:35||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* À partir du 31 mars, l'équipe MediaWiki Interfaces va lancer une version limitée des spécifications OpenAPI générées et une expérience de bac à sable basée sur SwaggerUI pour [[mw:Special:MyLanguage/API:REST API|MediaWiki REST APIs]]. L'équipe invite les développeurs d'un groupe limité de communautés Wikipédia non anglophones (arabe, allemand, français, hébreu, interlingua, néerlandais, chinois) à consulter la documentation et à expérimenter le bac à sable dans leur langue de choix. En plus de ces projets Wikipédia spécifiques, le bac à sable et la spécification OpenAPI seront disponibles sur la [[testwiki:Special:RestSandbox|page spéciale test wiki REST Sandbox]] pour les développeurs dont l'anglais est la langue préférée. Pendant la période de prévisualisation, l'équipe MediaWiki Interfaces invite également les développeurs à [[mw:MediaWiki Interfaces Team/Feature Feedback/REST Sandbox|partager leur retour d'expérience]]. L'aperçu durera environ 2 semaines, après quoi le bac à sable et les spécifications OpenAPI seront mis à la disposition de tous les projets wiki.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.23|MediaWiki]]
'''En détails'''
* Parfois, un petit changement de code d'une ligne peut avoir une grande importance : dans ce cas, cela signifie que pour la première fois depuis des années, nous sommes en mesure de faire fonctionner toute la pile qui sert <bdi lang="en" dir="ltr">[http://maps.wikimedia.org/ maps.wikimedia.org]</bdi> - un hôte dédié à servir nos wikis et leurs besoins en cartes multilingues - à partir d'un seul centre de données, ce que nous testons à chaque fois que nous effectuons un [[m:Special:MyLanguage/Tech/Server switch|basculement de centre de données]]. C'est important car cela signifie que si l'un de nos centres de données est affecté par une catastrophe, nous serons toujours en mesure de servir le site. Ce changement est le résultat d'un [[phab:T216826|travail intensif]] de deux développeurs sur le portage du dernier composant de la pile de cartes sur [[w:fr:Kubernetes|kubernetes]], où nous pouvons allouer des ressources plus efficacement qu'auparavant, ce qui nous permet de supporter plus de trafic dans un seul centre de données. Ce travail a nécessité beaucoup d'étapes compliquées car ce logiciel et les bibliothèques logicielles qu'il utilise nécessitaient de nombreuses mises à jour attendues depuis longtemps. Ce type de travail rend l'infrastructure de Wikimedia plus durable.
'''Rencontres et évènements'''
* La [[mw:Special:MyLanguage/MediaWiki Users and Developers Workshop Spring 2025|Conférence des utilisateurs et développeurs de MediaWiki printemps 2025]] se déroulera à Sandusky, aux États-Unis, et en ligne, du 14 au 16 mai 2025. La conférence proposera des discussions autour de l'utilisation du logiciel MediaWiki par et au sein d'entreprises de différents secteurs, et inspirera et embarquera de nouveaux utilisateurs. L'inscription et l'enregistrement des présentations sont maintenant disponibles sur le site web de la conférence.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/14|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W14"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 1 avril 2025 à 02:05 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28473566 -->
== Final proposed modifications to the Universal Code of Conduct Enforcement Guidelines and U4C Charter now posted ==
<div lang="en" dir="ltr" class="mw-content-ltr">
The proposed modifications to the [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Universal Code of Conduct Enforcement Guidelines]] and the U4C Charter [[m:Universal_Code_of_Conduct/Annual_review/2025/Proposed_Changes|are now on Meta-wiki for community notice]] in advance of the voting period. This final draft was developed from the previous two rounds of community review. Community members will be able to vote on these modifications starting on 17 April 2025. The vote will close on 1 May 2025, and results will be announced no later than 12 May 2025. The U4C election period, starting with a call for candidates, will open immediately following the announcement of the review results. More information will be posted on [[m:Special:MyLanguage//Universal_Code_of_Conduct/Coordinating_Committee/Election|the wiki page for the election]] soon.
Please be advised that this process will require more messages to be sent here over the next two months.
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|review the U4C Charter]].
Please share this message with members of your community so they can participate as well.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|talk]]) 4 avril 2025 à 04:04 (CEST)
</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28469465 -->
== Actualités techniques n° 2025-15 ==
<section begin="technews-2025-W15"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/15|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Désormais, les [[m:Special:MyLanguage/Interface administrators|admins d’interface]] et [[m:Special:MyLanguage/Central notice administrators|admins des annonces centrales]] sont contraint techniquement d’activer l’[[m:Special:MyLanguage/Help:Two-factor authentication|authentification à deux facteurs]] avant de pouvoir utiliser leurs privilèges. À l’avenir, cela pourrait être étendu à d’autres groupes ayant des droits avancés. [https://phabricator.wikimedia.org/T150898]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:20|la tâche soumise|les {{formatnum:20}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:20||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* L’équipe Système design prépare la sortie de la nouvelle version majeur de Codex (v2.0.0) pour le 29 avril. Les contributeurices et développeurs et développeuses qui utilisent du CSS de Codex devraient consulter la [[mw:Codex/Release Timeline/2.0|documentation sur l’arrivée de la v2]], elle inclut un guidage pour les ruptures introduites dans cette version, par exemple pour <code dir=ltr style="white-space: nowrap;">font-size</code>, <code dir=ltr style="white-space: nowrap;">line-height</code> et <code dir=ltr style="white-space: nowrap;">size-icon</code>.
* Les résultats de [[mw:Developer Satisfaction Survey/2025|l’enquête 2025 sur la satisfaction des développeurs et développeuses]] est désormais disponible. Merci à tous les participants ! Ces résultats aident Wikimedia à décider ce sur quoi orienter le travail et à évaluer le travail récent.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.24|MediaWiki]]
'''Rencontres et évènements'''
* Le [[mw:Special:MyLanguage/Wikimedia Hackathon 2025|Hackathon Wikimedia 2025]] aura lieu à Istanbul en Turquie, du 2 au 4 mai. Les inscriptions pour participer en présentiel ont lieu jusqu’au 13 avril. Avant de vous inscrire, sachez qu’il vous faudra peut-être un [https://www.mfa.gov.tr/turkish-representations.en.mfa visa] ou un [https://www.mfa.gov.tr/visa-information-for-foreigners.en.mfa e-visa] pour entrer dans le pays.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/15|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W15"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 7 avril 2025 à 20:52 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28507470 -->
== Wikidata and Sister Projects: An online community event ==
''(Apologies for posting in English)''
Hello everyone, I am excited to share news of an upcoming online event called '''[[d:Event:Wikidata_and_Sister_Projects|Wikidata and Sister Projects]]''' celebrating the different ways Wikidata can be used to support or enhance with another Wikimedia project. The event takes place over 4 days between '''May 29 - June 1st, 2025'''.
We would like to invite speakers to present at this community event, to hear success stories, challenges, showcase tools or projects you may be working on, where Wikidata has been involved in Wikipedia, Commons, WikiSource and all other WM projects.
If you are interested in attending, please [[d:Special:RegisterForEvent/1291|register here]].
If you would like to speak at the event, please fill out this Session Proposal template on the [[d:Event_talk:Wikidata_and_Sister_Projects|event talk page]], where you can also ask any questions you may have.
I hope to see you at the event, in the audience or as a speaker, - [[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 11 avril 2025 à 11:18 (CEST)
<!-- Message envoyé par User:Danny Benjafield (WMDE)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Danny_Benjafield_(WMDE)/MassMessage_Send_List&oldid=28525705 -->
== <span lang="en" dir="ltr">Tech News: 2025-16</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W16"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/16|Translations]] are available.
'''Weekly highlight'''
* Later this week, the default thumbnail size will be increased from 220px to 250px. This changes how pages are shown in all wikis and has been requested by some communities for many years, but wasn't previously possible due to technical limitations. [https://phabricator.wikimedia.org/T355914]
* File thumbnails are now stored in discrete sizes. If a page specifies a thumbnail size that's not among the standard sizes (20, 40, 60, 120, 250, 330, 500, 960), then MediaWiki will pick the closest larger thumbnail size but will tell the browser to downscale it to the requested size. In these cases, nothing will change visually but users might load slightly larger images. If it doesn't matter which thumbnail size is used in a page, please pick one of the standard sizes to avoid the extra in-browser down-scaling step. [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images#Thumbnail_sizes][https://phabricator.wikimedia.org/T355914]
'''Updates for editors'''
* The Wikimedia Foundation are working on a system called [[m:Edge Uniques|Edge Uniques]] which will enable [[:w:en:A/B testing|A/B testing]], help protect against [[:w:en:Denial-of-service attack|Distributed denial-of-service attacks]] (DDoS attacks), and make it easier to understand how many visitors the Wikimedia sites have. This is so that they can more efficiently build tools which help readers, and make it easier for readers to find what they are looking for.
* To improve security for users, a small percentage of logins will now require that the account owner input a one-time password [[mw:Special:MyLanguage/Help:Extension:EmailAuth|emailed to their account]]. It is recommended that you [[Special:Preferences#mw-prefsection-personal-email|check]] that the email address on your account is set correctly, and that it has been confirmed, and that you have an email set for this purpose. [https://phabricator.wikimedia.org/T390662]
* "Are you interested in taking a short survey to improve tools used for reviewing or reverting edits on your Wiki?" This question will be [[phab:T389401|asked at 7 wikis starting next week]], on Recent Changes and Watchlist pages. The [[mw:Special:MyLanguage/Moderator Tools|Moderator Tools team]] wants to know more about activities that involve looking at new edits made to your Wikimedia project, and determining whether they adhere to your project's policies.
* On April 15, the full Wikidata graph will no longer be supported on <bdi lang="zxx" dir="ltr">[https://query.wikidata.org/ query.wikidata.org]</bdi>. After this date, scholarly articles will be available through <bdi lang="zxx" dir="ltr" style="white-space:nowrap;">[https://query-scholarly.wikidata.org/ query-scholarly.wikidata.org]</bdi>, while the rest of the data hosted on Wikidata will be available through the <bdi lang="zxx" dir="ltr">[https://query.wikidata.org/ query.wikidata.org]</bdi> endpoint. This is part of the scheduled split of the Wikidata Graph, which was [[d:Special:MyLanguage/Wikidata:SPARQL query service/WDQS backend update/September 2024 scaling update|announced in September 2024]]. More information is [[d:Wikidata:SPARQL query service/WDQS graph split|available on Wikidata]].
* The latest quarterly [[m:Special:MyLanguage/Wikimedia Apps/Newsletter/First quarter of 2025|Wikimedia Apps Newsletter]] is now available. It covers updates, experiments, and improvements made to the Wikipedia mobile apps.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:30}} community-submitted {{PLURAL:30|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]].
'''Updates for technical contributors'''
* The latest quarterly [[mw:Technical Community Newsletter/2025/April|Technical Community Newsletter]] is now available. This edition includes: an invitation for tool maintainers to attend the Toolforge UI Community Feedback Session on April 15th; recent community metrics; and recent technical blog posts.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.44/wmf.25|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/16|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W16"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 15 avril 2025 à 02:24 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28540654 -->
== Vote now on the revised UCoC Enforcement Guidelines and U4C Charter ==
<div lang="en" dir="ltr" class="mw-content-ltr">
The voting period for the revisions to the Universal Code of Conduct Enforcement Guidelines ("UCoC EG") and the UCoC's Coordinating Committee Charter is open now through the end of 1 May (UTC) ([https://zonestamp.toolforge.org/1746162000 find in your time zone]). [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/2025/Voter_information|Read the information on how to participate and read over the proposal before voting]] on the UCoC page on Meta-wiki.
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review of the EG and Charter was planned and implemented by the U4C. Further information will be provided in the coming months about the review of the UCoC itself. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|review the U4C Charter]].
Please share this message with members of your community so they can participate as well.
In cooperation with the U4C -- [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|talk]]) 17 avril 2025 à 02:34 (CEST)
</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28469465 -->
== Actualités techniques n° 2025-17 ==
<section begin="technews-2025-W17"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/17|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* [[f:Special:MyLanguage/Wikifunctions:Main Page|Wikifunctions]] est désormais intégré à la [[w:dag:Solɔɣu|Wikipédia en dagbani]] depuis le 15 avril. C'est le premier projet qui pourra appeler des [[f:Special:MyLanguage/Wikifunctions:Introduction|fonctions de Wikifonctions]] et les intégrer dans des articles. Une fonction est quelque chose qui prend une ou plusieurs entrées et les transforme en un résultat souhaité, comme l'addition de deux nombres, la conversion de miles en mètres, le calcul du temps écoulé depuis un événement, ou la déclinaison d'un mot en une casse. Les Wikifonctions permettront aux utilisateurs de faire cela par un simple appel d'[[f:Special:MyLanguage/Wikifunctions:Catalogue|une fonction stable et globale]], plutôt que par l'intermédiaire d'un modèle local. [https://www.wikifunctions.org/wiki/Special:MyLanguage/Wikifunctions:Status_updates/2025-04-16]
* Un nouveau type d'erreur ''lint'' a été créé : [[Special:LintErrors/empty-heading|{{int:linter-category-empty-heading}}]] ([[mw:Special:MyLanguage/Help:Lint errors/empty-heading|documentation]]). L'objectif de l'extension [[mw:Special:MyLanguage/Help:Extension:Linter|Linter]] est d'identifier les éléments de wikitexte qui doivent ou peuvent être corrigés dans les pages et de fournir des conseils sur les problèmes posés par ces éléments et sur la manière de les corriger. [https://phabricator.wikimedia.org/T368722]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:37|la tâche soumise|les {{formatnum:37}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:37||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* À la suite de sa publication sur ''HuggingFace'', l'ensemble de données « ''Structured Contents'' », développé par Wikimedia Enterprise, est [https://enterprise.wikimedia.com/blog/kaggle-dataset/ maintenant également disponible sur Kaggle]. Cette initiative bêta vise à rendre les données de Wikimedia plus lisibles par les machines pour les réutilisateurs de gros volumes. Cette version bêta est publiée à un emplacement déjà utilisé par les communautés de données ouvertes, afin de recueillir des commentaires qui permettront d'améliorer le produit en vue d'une future diffusion à plus grande échelle. Vous pouvez en savoir plus sur le projet ''[https://enterprise.wikimedia.com/blog/structured-contents-snapshot-api/#open-datasets Structured Contents]'', et sur la [https://enterprise.wikimedia.com/blog/structured-contents-wikipedia-infobox/ première version librement utilisable].
* Il n'y a pas de nouvelle version de MediaWiki cette semaine.
'''Rencontres et évènements'''
* Les équipes de rédaction et d'apprentissage automatique (''Editing and Machine Learning Teams'') invitent les bénévoles intéressés à une visioconférence pour discuter de la [[mw:Special:MyLanguage/Edit check/Peacock check|vérification « ''peacock'' »]] <small>(NdT : litt. « paon » mais également une expression idiomatique pour le langage vaniteux, prétentieux)</small>, qui est la dernière [[mw:Special:MyLanguage/Edit check|vérification de modification]] qui détectera le langage « trop promotionnel » ou « non neutre » pendant qu'un rédacteur est en train de taper. Les rédacteurs qui travaillent avec les nouveaux arrivants, ou qui aident à corriger ce type d'écriture, ou qui sont intéressés par la manière dont nous utilisons l'intelligence artificielle dans nos projets, sont invités à participer à cette réunion. La [[mw:Special:MyLanguage/Editing team/Community Conversations#Next Conversation|réunion aura lieu le 28 avril 2025]] à [https://zonestamp.toolforge.org/1745863200 18:00-19:00 UTC] et sera hébergée sur Zoom.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/17|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W17"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 21 avril 2025 à 23:00 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28578245 -->
== Actualités techniques n° 2025-18 ==
<section begin="technews-2025-W18"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/18|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Event organizers who host collaborative activities on [[m:Special:MyLanguage/CampaignEvents/Deployment status#Global Deployment Plan|multiple wikis]], including Bengali, Japanese, and Korean Wikipedias, will have access to the [[mw:Special:MyLanguage/Extension:CampaignEvents|CampaignEvents extension]] this week. Also, admins in the Wikipedia where the extension is enabled will automatically be granted the event organizer right soon. They won't have to manually grant themselves the right before they can manage events as [[phab:T386861|requested by a community]].</span>
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:19|la tâche soumise|les {{formatnum:19}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:19||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* <span lang="en" dir="ltr" class="mw-content-ltr">The release of the next major version of [[mw:Special:MyLanguage/Codex|Codex]], the design system for Wikimedia, is scheduled for 29 April 2025. Technical editors will have access to the release by the week of 5 May 2025. This update will include a number of [[mw:Special:MyLanguage/Codex/Release_Timeline/2.0#Breaking_changes|breaking changes]] and minor [[mw:Special:MyLanguage/Codex/Release_Timeline/2.0#Visual_changes|visual changes]]. Instructions on handling the breaking and visual changes are documented on [[mw:Special:MyLanguage/Codex/Release Timeline/2.0#|this page]]. Pre-release testing is reported in [[phab:T386298|T386298]], with post-release issues tracked in [[phab:T392379|T392379]] and [[phab:T392390|T392390]].</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">Users of [[wikitech:Special:MyLanguage/Help:Wiki_Replicas|Wiki Replicas]] will notice that the database views of <code dir="ltr">ipblocks</code>, <code dir="ltr">ipblocks_ipindex</code>, and <code dir="ltr">ipblocks_compat</code> are [[phab:T390767|now deprecated]]. Users can query the <code dir="ltr">[[mw:Special:MyLanguage/Manual:Block_table|block]]</code> and <code dir="ltr">[[mw:Special:MyLanguage/Manual:Block_target_table|block_target]]</code> new views that mirror the new tables in the production database instead. The deprecated views will be removed entirely from Wiki Replicas in June, 2025.</span>
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.27|MediaWiki]]
'''En détails'''
* <span lang="en" dir="ltr" class="mw-content-ltr">The latest quarterly [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/April|Language and Internationalization Newsletter]] is now available.</span> <span lang="en" dir="ltr" class="mw-content-ltr">This edition includes an overview of the improved [https://test.wikipedia.org/w/index.php?title=Special:ContentTranslation&campaign=contributionsmenu&to=es&filter-type=automatic&filter-id=previous-edits&active-list=suggestions&from=en#/ Content Translation Dashboard Tool], [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/April#Language Support for New and Existing Languages|support for new languages]], [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/April#Wiki Loves Ramadan Articles Made In Content Translation Mobile Workflow|highlights from the Wiki Loves Ramadan campaign]], [[m:Special:MyLanguage/Research:Languages Onboarding Experiment 2024 - Executive Summary|results from the Language Onboarding Experiment]], an analysis of topic diversity in articles, and information on upcoming community meetings and events.</span>
'''Rencontres et évènements'''
* <span lang="en" dir="ltr" class="mw-content-ltr">The [[Special:MyLanguage/Grants:Knowledge_Sharing/Connect/Calendar|Let's Connect Learning Clinic]] will take place on [https://zonestamp.toolforge.org/1745937000 April 29 at 14:30 UTC]. This edition will focus on "Understanding and Navigating Conflict in Wikimedia Projects". You can [[m:Special:MyLanguage/Event:Learning Clinic %E2%80%93 Understanding and Navigating Conflict in Wikimedia Projects (Part_1)|register now]] to attend.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">The [[mw:Special:MyLanguage/Wikimedia Hackathon 2025|2025 Wikimedia Hackathon]], which brings the global technical community together to connect, brainstorm, and hack existing projects, will take place from May 2 to 4th, 2025, at Istanbul, Turkey.</span>
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/18|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W18"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 28 avril 2025 à 21:31 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28585685 -->
== Vote sur les modifications proposées aux lignes directrices de l'UCoC et à la charte de l'U4C ==
<section begin="announcement-content" />
La période de vote pour les révisions des directives d'application du Code de conduite universel et de la Charte U4C se termine le 1er mai 2025 à 23:59 UTC ([https://zonestamp.toolforge.org/1746162000 trouver dans votre fuseau horaire]). [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2025/Voter information|Lisez les informations sur la façon de participer et lisez la proposition avant de voter]] sur la page UCoC de Méta-wiki.
Le [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Comité de coordination du code de conduite universel (U4C)]] est un groupe mondial qui se consacre à la mise en œuvre équitable et cohérente de l'UCoC. Cet examen annuel a été planifié et mis en œuvre par l'U4C. Pour plus d'informations et pour connaître les responsabilités de l'U4C, vous pouvez [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|réviser la charte U4C]].
Veuillez partager ce message avec les membres de votre communauté dans votre langue, le cas échéant, afin qu'ils puissent également y participer.
En coopération avec l'U4C -- <section end="announcement-content" />
<div lang="en" dir="ltr" class="mw-content-ltr">
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 29 avril 2025 à 05:40 (CEST)</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 -->
== Actualités techniques n° 2025-19 ==
<section begin="technews-2025-W19"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/19|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* La Wikimedia Foundation a partagé le dernier projet de mise à jour de son [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026|plan annuel]] pour l'année prochaine (juillet 2025-juin 2026). Cela comprend un [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026|résumé exécutif]] (également sur [[diffblog:2025/04/25/sharing-the-wikimedia-foundations-2025-2026-draft-annual-plan/|Diff]]), des détails sur les trois principaux [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Goals|objectifs]] ([[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Product & Technology OKRs|Infrastructure]], [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Goals/Volunteer Support|Soutien aux bénévoles]] et [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Goals/Effectiveness|Efficacité]]), [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Global Trends|tendances mondiales]], ainsi que le [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Budget Overview|budget]] et le [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Financial Model|modèle financier]]. Les réactions et les questions sont les bienvenues sur la [[m:Talk:Wikimedia Foundation Annual Plan/2025-2026|page de discussion]] jusqu'à la fin du mois de mai.
'''Actualités pour la contribution'''
* Pour les wikis qui ont l'extension [[m:Special:MyLanguage/CampaignEvents/Deployment status|CampaignEvents]] activée, deux nouvelles améliorations de fonctionnalités ont été publiées :
** Les administrateurs peuvent désormais choisir les espaces de noms autorisés pour [[m:Special:MyLanguage/Event Center/Registration|Inscription à un événement]] via [[mw:Special:MyLanguage/Community Configuration|Configuration de la communauté]] ([[mw:Special:MyLanguage/Help:Extension:CampaignEvents/Registration/Permitted namespaces|documentation]]). Par défaut, l'enregistrement d'un événement est autorisé dans l'espace de noms Event, mais d'autres espaces de noms (tels que l'espace de noms du projet ou l'espace de noms WikiProject) peuvent désormais être ajoutés. Grâce à cette modification, les communautés telles que les WikiProjets peuvent désormais utiliser plus facilement l'enregistrement d'événements pour leurs activités collaboratives.
** Les éditeurs peuvent désormais [[mw:Special:MyLanguage/Transclusion|transclure]] la liste de collaboration sur une page wiki ([[mw:Special:MyLanguage/Help:Extension:CampaignEvents/Collaboration list/Transclusion|documentation]]). La liste de collaboration est une liste automatisée d'événements et de wikiprojets sur les wikis, accessible via {{#special:AllEvents}} ([[w:en:Special:AllEvents|exemple]]). Désormais, la liste de collaboration peut être ajoutée à toutes sortes de pages wiki, telles que : une page principale wiki, une page WikiProjet, une page d'affiliation, une page d'événement, ou même une page d'utilisateur.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les développeurs qui utilisent la bibliothèque <code dir=ltr>moment</code> dans les gadgets et les scripts utilisateur doivent réviser leur code pour utiliser des alternatives comme la bibliothèque <code dir=ltr>Intl</code> ou la nouvelle bibliothèque <code dir=ltr>mediawiki.DateFormatter</code>. La bibliothèque <code dir=ltr>moment</code> a été dépréciée et commencera à enregistrer des messages dans la console du développeur. Vous pouvez voir une recherche globale pour les utilisations actuelles, et [[phab:T392532|posez des questions connexes dans cette tâche Phabricator]].
* Les développeurs qui maintiennent un outil qui interroge les tables de stockage de termes de Wikidata (<code dir=ltr style="white-space: nowrap;">wbt_*</code>) doivent mettre à jour leur code pour se connecter à une grappe de base de données séparée. Ces tables sont réparties dans une grappe de base de données distincte. Les outils qui interrogent ces tables via les répliques du wiki doivent être adaptés pour se connecter à la nouvelle grappe. [[wikitech:News/2025 Wikidata term store database split|La documentation et des liens connexes sont à votre disposition]]. [https://phabricator.wikimedia.org/T390954]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.28|MediaWiki]]
'''En détails'''
* La dernière [[mw:Special:MyLanguage/Extension:Chart/Project/Updates|lettre d’information du projet Chart]] est disponible. Il comprend des mises à jour sur la préparation de l'extension du déploiement à d'autres wikis dès cette semaine (à partir du 6 mai) et sur la mise à l'échelle au cours des semaines suivantes, ainsi que sur l'exploration du filtrage et de la transformation des données sources.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/19|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W19"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 6 mai 2025 à 02:14 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28665011 -->
== We will be enabling the new Charts extension on your wiki soon! ==
''(Apologies for posting in English)''
Hi all! We have good news to share regarding the ongoing problem with graphs and charts affecting all wikis that use them.
As you probably know, the [[:mw:Special:MyLanguage/Extension:Graph|old Graph extension]] was disabled in 2023 [[listarchive:list/wikitech-l@lists.wikimedia.org/thread/EWL4AGBEZEDMNNFTM4FRD4MHOU3CVESO/|due to security reasons]]. We’ve worked in these two years to find a solution that could replace the old extension, and provide a safer and better solution to users who wanted to showcase graphs and charts in their articles. We therefore developed the [[:mw:Special:MyLanguage/Extension:Chart|Charts extension]], which will be replacing the old Graph extension and potentially also the [[:mw:Extension:EasyTimeline|EasyTimeline extension]].
After successfully deploying the extension on Italian, Swedish, and Hebrew Wikipedia, as well as on MediaWiki.org, as part of a pilot phase, we are now happy to announce that we are moving forward with the next phase of deployment, which will also include your wiki.
The deployment will happen in batches, and will start from '''May 6'''. Please, consult [[:mw:Special:MyLanguage/Extension:Chart/Project#Deployment Timeline|our page on MediaWiki.org]] to discover when the new Charts extension will be deployed on your wiki. You can also [[:mw:Special:MyLanguage/Extension:Chart|consult the documentation]] about the extension on MediaWiki.org.
If you have questions, need clarifications, or just want to express your opinion about it, please refer to the [[:mw:Special:MyLanguage/Extension_talk:Chart/Project|project’s talk page on Mediawiki.org]], or ping me directly under this thread. If you encounter issues using Charts once it gets enabled on your wiki, please report it on the [[:mw:Extension_talk:Chart/Project|talk page]] or at [[phab:tag/charts|Phabricator]].
Thank you in advance! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 6 mai 2025 à 17:07 (CEST)
<!-- Message envoyé par User:Sannita (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=28663781 -->
== Actualités techniques n° 2025-20 ==
<section begin="technews-2025-W20"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/20|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Le lien [[m:Special:MyLanguage/Wikimedia URL Shortener|« Obtenir une URL raccourcie »]] dans la barre latérale inclut désormais un [[phab:T393309|code QR]]. Les utilisateurs des sites Wikimedia peuvent maintenant l’utiliser en le scannant ou en le téléchargeant pour partager et accéder rapidement au contenu partagé des sites Wikimedia, de manière pratique.
'''Actualités pour la contribution'''
* La Wikimedia Foundation travaille sur un système appelé [[m:Edge Uniques|« ''Edge Uniques'' »]], qui permettra de réaliser des [[w:en:A/B testing|tests A/B]], d’aider à se protéger contre les [[w:en:Denial-of-service attack|attaques par déni de service distribué]] (attaques DDoS), et de mieux comprendre combien de visiteurs les sites Wikimedia reçoivent. Cela vise à construire plus efficacement des outils utiles aux lecteurs, et de les aider à trouver ce qu'ils cherchent. Les Actualités techniques en ont [[m:Special:MyLanguage/Tech/News/2025/16|déjà parlé]]. Le déploiement sera progressif. Certains pourraient voir le cookie « ''Edge Uniques'' » à partir de la semaine du 19 mai. Vous pouvez en discuter sur la [[m:Talk:Edge Uniques|page de discussion]].
* À partir du 19 mai 2025, les organisateurs d’événements sur les wikis disposant de l’[[mw:Special:MyLanguage/Help:Extension:CampaignEvents|extension « CampaignEvents »]] pourront utiliser l’[[m:Special:MyLanguage/Event Center/Registration|inscription aux événements]] dans l’espace de noms du projet (par exemple, l’espace Wikipédia, l’espace Wikidata). Avec ce changement, les communautés n’ont plus besoin d’administrateurs pour utiliser cette fonctionnalité. Toutefois, les wikis qui ne souhaitent pas ce changement peuvent retirer et ajouter les espaces de noms autorisés sur [[Special:CommunityConfiguration/CampaignEvents]].
* Le projet Wikipédia dispose désormais d’un {{int:project-localized-name-group-wikipedia/fr}} en [[d:Q36720|nupe]] ([[w:nup:|<code>w:nup:</code>]]). Il s’agit d’une langue principalement parlée dans la région du centre-nord du Nigeria. Les locuteurs de cette langue sont invités à contribuer à la [[w:nup:Tatacin feregi|nouvelle Wikipédia]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les développeurs peuvent désormais accéder à la Wikipédia néerlandaise pré-analysée, parmi d’autres (anglais, allemand, français, espagnol, italien et portugais), via les [https://enterprise.wikimedia.com/docs/snapshot/#structured-contents-snapshot-bundle-info-beta instantanés « ''Structured Contents'' » (bêta)]. Le contenu comprend des résumés Wikipédia analysés, des descriptions, des images principales, des infoboxes, des sections d’articles et des références.
* Le point de terminaison de l’API REST <code dir="ltr">/page/data-parsoid</code> n’est plus utilisé et sera obsolète. Il est [[phab:T393557|prévu qu’il soit désactivé]] le 7 juin 2025.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.1|MediaWiki]]
'''En détails'''
* Le [https://wikitech.wikimedia.org/wiki/News/2025_Cloud_VPS_VXLAN_IPv6_migration support IPv6] est un nouveau réseau virtuel Cloud qui améliore considérablement l’évolutivité, la sécurité et la préparation des plateformes Wikimedia pour l’avenir. Si vous êtes un contributeur technique curieux d’en savoir plus, consultez [https://techblog.wikimedia.org/2025/05/06/wikimedia-cloud-vps-ipv6-support/ ce billet de blogue] pour un aperçu détaillé de la transition vers l’IPv6.
'''Rencontres et évènements'''
* La deuxième édition de 2025 de [[m:Special:MyLanguage/Afrika Baraza|« Afrika Baraza »]], une plateforme virtuelle permettant aux Wikimédiens africains de se connecter, aura lieu le [https://zonestamp.toolforge.org/1747328400 15 mai à 17 h UTC]. Cette édition sera axée sur des discussions concernant la [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026|planification annuelle et les avancées de Wikimedia]].
* La [[m:Special:MyLanguage/MENA Connect Community Call|« ''MENA Connect Community Call'' »]], une réunion virtuelle permettant aux Wikimédiens de la [[w:fr:MENA|région Moyen-Orient et Afrique du Nord]] (MENA) de se rencontrer, aura lieu le [https://zonestamp.toolforge.org/1747501200 17 mai à 17 h UTC]. Vous pouvez [[m:Event:MENA Connect (Wiki_Diwan) APP Call|vous inscrire dès maintenant]] pour y assister.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/20|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W20"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 13 mai 2025 à 00:37 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28714188 -->
== Appel aux candidatures pour le Comité de Coordination du Code de Conduite Universel (U4C). ==
<section begin="announcement-content" />
Les résultats du vote sur les directives d'application et la charte du Comité de Coordination du Code de Conduite Universel (U4C) sont disponibles [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2025#Results|sur Méta-wiki]].
Vous pouvez désormais [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025/Candidates|soumettre votre candidature pour siéger à l'U4C]] <s>à partir du 29 mai 2025 à 12:00 UTC</s>. Des informations sur [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025|l'éligibilité, le processus, et la chronologie sont disponibles sur Méta-wiki]]. Le vote sur les candidats sera ouvert le 1<sup>er</sup> juin 2025 et durera deux semaines, se finissant donc le 15 juin 2025 à 12:00 UTC.
Si vous avez des questions, vous pouvez les poser sur [[m:Talk:Universal Code of Conduct/Coordinating Committee/Election/2025|la page de discussion pour l'élection]]. -- en coopération avec l'U4C, </div><section end="announcement-content" />
<bdi lang="en" dir="ltr">[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|discussion]])</bdi> 16 mai 2025 à 00:06 (CEST)
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 -->
:Rectification : Vous pouvez désormais [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025/Candidates|soumettre votre candidature pour siéger à l'U4C]] du 14 mai au 28 mai.
:{{BlocCitation|You can submit your candidacy from May 14 until May 28, 2025}}
:-- ◄ [[Utilisateur:DavidL|'''D'''avid '''L''']] • [[Discussion Utilisateur:DavidL|discuter]] ► 16 mai 2025 à 16:36 (CEST)
== <span lang="en" dir="ltr">Tech News: 2025-21</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W21"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/21|Translations]] are available.
'''Weekly highlight'''
* The Editing Team and the Machine Learning Team are working on a new check for newcomers: [[mw:Edit check/Peacock check|Peacock check]]. Using a prediction model, this check will encourage editors to improve the tone of their edits, using artificial intelligence. We invite volunteers to review the first version of the Peacock language model for the following languages: Arabic, Spanish, Portuguese, English, and Japanese. Users from these wikis interested in reviewing this model are [[mw:Edit check/Peacock check/model test|invited to sign up at MediaWiki.org]]. The deadline to sign up is on May 23, which will be the start date of the test.
'''Updates for editors'''
* From May 20, 2025, [[m:Special:MyLanguage/Oversight policy|oversighters]] and [[m:Special:MyLanguage/Meta:CheckUsers|checkusers]] will need to have their accounts secured with two-factor authentication (2FA) to be able to use their advanced rights. All users who belong to these two groups and do not have 2FA enabled have been informed. In the future, this requirement may be extended to other users with advanced rights. [[m:Special:MyLanguage/Mandatory two-factor authentication for users with some extended rights|Learn more]].
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Wishlist item]] [[m:Special:MyLanguage/Community Wishlist Survey 2023/Multiblocks|Multiblocks]] will begin mass deployment by the end of the month: all non-Wikipedia projects plus Catalan Wikipedia will adopt Multiblocks in the week of May 26, while all other Wikipedias will adopt it in the week of June 2. Please [[m:Talk:Community Wishlist Survey 2023/Multiblocks|contact the team]] if you have concerns. Administrators can test the new user interface now on your own wiki by browsing to [{{fullurl:Special:Block|usecodex=1}} {{#special:Block}}?usecodex=1], and can test the full multiblocks functionality [[testwiki:Special:Block|on testwiki]]. Multiblocks is the feature that makes it possible for administrators to impose different types of blocks on the same user at the same time. See the [[mw:Special:MyLanguage/Help:Manage blocks|help page]] for more information. [https://phabricator.wikimedia.org/T377121]
* Later this week, the [[{{#special:SpecialPages}}]] listing of almost all special pages will be updated with a new design. This page has been [[phab:T219543|redesigned]] to improve the user experience in a few ways, including: The ability to search for names and aliases of the special pages, sorting, more visible marking of restricted special pages, and a more mobile-friendly look. The new version can be [https://meta.wikimedia.beta.wmflabs.org/wiki/Special:SpecialPages previewed] at Beta Cluster now, and feedback shared in the task. [https://phabricator.wikimedia.org/T219543]
* The [[mw:Special:MyLanguage/Extension:Chart|Chart extension]] is being enabled on more wikis. For a detailed list of when the extension will be enabled on your wiki, please read the [[mw:Special:MyLanguage/Extension:Chart/Project#Deployment Timeline|deployment timeline]].
* [[f:Special:MyLanguage/Wikifunctions:Main Page|Wikifunctions]] will be deployed on May 27 on five Wiktionaries: [[wikt:ha:|Hausa]], [[wikt:ig:|Igbo]], [[wikt:bn:|Bengali]], [[wikt:ml:|Malayalam]], and [[wikt:dv:|Dhivehi/Maldivian]]. This is the second batch of deployment planned for the project. After deployment, the projects will be able to call [[f:Special:MyLanguage/Wikifunctions:Introduction|functions from Wikifunctions]] and integrate them in their pages. A function is something that takes one or more inputs and transforms them into a desired output, such as adding up two numbers, converting miles into metres, calculating how much time has passed since an event, or declining a word into a case. Wikifunctions will allow users to do that through a simple call of [[f:Special:MyLanguage/Wikifunctions:Catalogue|a stable and global function]], rather than via a local template.
* Later this week, the Wikimedia Foundation will publish a hub for [[diffblog:2024/07/09/on-the-value-of-experimentation/|experiments]]. This is to showcase and get user feedback on product experiments. The experiments help the Wikimedia movement [[diffblog:2023/07/13/exploring-paths-for-the-future-of-free-knowledge-new-wikipedia-chatgpt-plugin-leveraging-rich-media-social-apps-and-other-experiments/|understand new users]], how they interact with the internet and how it could affect the Wikimedia movement. Some examples are [[m:Special:MyLanguage/Future Audiences/Generated Video|generated video]], the [[m:Special:MyLanguage/Future Audiences/Roblox game|Wikipedia Roblox speedrun game]] and [[m:Special:MyLanguage/Future Audiences/Discord bot|the Discord bot]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:29}} community-submitted {{PLURAL:29|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, there was a bug with creating an account using the API, which has now been fixed. [https://phabricator.wikimedia.org/T390751]
'''Updates for technical contributors'''
* Gadgets and user scripts that interact with [[{{#special:Block}}]] may need to be updated to work with the new [[mw:Special:MyLanguage/Help:Manage blocks|manage blocks interface]]. Please review the [[mw:Help:Manage blocks/Developers|developer guide]] for more information. If you need help or are unable to adapt your script to the new interface, please let the team know on the [[mw:Help talk:Manage blocks/Developers|talk page]]. [https://phabricator.wikimedia.org/T377121]
* The <code dir=ltr>mw.title</code> object allows you to get information about a specific wiki page in the [[w:en:Wikipedia:Lua|Lua]] programming language. Starting this week, a new property will be added to the object, named <code dir=ltr>isDisambiguationPage</code>. This property allows you to check if a page is a disambiguation page, without the need to write a custom function. [https://phabricator.wikimedia.org/T71441]
* [[File:Octicons-tools.svg|15px|link=|class=skin-invert|Advanced item]] User script developers can use a [[toolforge:gitlab-content|new reverse proxy tool]] to load javascript and css from [[gitlab:|gitlab.wikimedia.org]] with <code dir=ltr>mw.loader.load</code>. The tool's author hopes this will enable collaborative development workflows for user scripts including linting, unit tests, code generation, and code review on <bdi lang="zxx" dir="ltr">gitlab.wikimedia.org</bdi> without a separate copy-and-paste step to publish scripts to a Wikimedia wiki for integration and acceptance testing. See [[wikitech:Tool:Gitlab-content|Tool:Gitlab-content on Wikitech]] for more information.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.45/wmf.2|MediaWiki]]
'''Meetings and events'''
* The 12th edition of [[m:Special:MyLanguage/Wiki Workshop 2025|Wiki Workshop 2025]], a forum that brings together researchers that explore all aspects of Wikimedia projects, will be held virtually on 21-22 May. Researchers can [https://pretix.eu/wikimedia/wikiworkshop2025/ register now].
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/21|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W21"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 20 mai 2025 à 01:12 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28724712 -->
== RfC ongoing regarding Abstract Wikipedia (and your project) ==
<div lang="en" dir="ltr" class="mw-content-ltr">
''(Apologies for posting in English, if this is not your first language)''
Hello all! We opened a discussion on Meta about a very delicate issue for the development of [[:m:Special:MyLanguage/Abstract Wikipedia|Abstract Wikipedia]]: where to store the abstract content that will be developed through functions from Wikifunctions and data from Wikidata. Since some of the hypothesis involve your project, we wanted to hear your thoughts too.
We want to make the decision process clear: we do not yet know which option we want to use, which is why we are consulting here. We will take the arguments from the Wikimedia communities into account, and we want to consult with the different communities and hear arguments that will help us with the decision. The decision will be made and communicated after the consultation period by the Foundation.
You can read the various hypothesis and have your say at [[:m:Abstract Wikipedia/Location of Abstract Content|Abstract Wikipedia/Location of Abstract Content]]. Thank you in advance! -- [[User:Sannita (WMF)|Sannita (WMF)]] ([[User talk:Sannita (WMF)|<span class="signature-talk">{{int:Talkpagelinktext}}</span>]]) 22 mai 2025 à 17:26 (CEST)
</div>
<!-- Message envoyé par User:Sannita (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=28768453 -->
== Actualités techniques n° 2025-22 ==
<section begin="technews-2025-W22"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/22|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Une discussion communautaire à l’échelle du mouvement est désormais ouverte sur Meta au sujet d’un point très délicat pour le développement de [[m:Special:MyLanguage/Abstract Wikipedia|« Abstract Wikipedia »]] : où stocker le contenu abstrait qui sera développé à partir des fonctions de Wikifunctions et des données de Wikidata. La discussion est ouverte jusqu’au 12 juin sur [[m:Special:MyLanguage/Abstract Wikipedia/Location of Abstract Content|Abstract Wikipedia/Location of Abstract Content]], et tous les avis sont les bienvenus. La décision sera prise et communiquée par la Fondation à l’issue de la période de consultation.
'''Actualités pour la contribution'''
* Depuis la semaine dernière, sur tous les wikis excepté [[phab:T388604|les vingt plus grands]], les utilisateurs de l’éditeur visuel mobile disposent de [[phab:T385851|nouveaux outils dans la barre de menu]], accessibles via le nouveau bouton de la barre d’outils <code>+</code>. Pour commencer, le nouveau menu proposera des options pour ajouter : des références, des hiéroglyphes et des blocs de code. Le déploiement sur les autres wikis est [[phab:T388605|prévu]] pour le mois de juin.
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] La fonction d’analyse <code dir=ltr>[[mw:Special:MyLanguage/Help:Extension:ParserFunctions##ifexist|#ifexist]]</code> ne créera plus de lien vers sa page cible. Cela améliorera l’utilité de [[{{#special:WantedPages}}]], qui ne listera à terme que les pages réellement ciblées par un lien rouge. Ce changement se fera progressivement à mesure que les pages sources seront mises à jour. [https://phabricator.wikimedia.org/T14019]
* Cette semaine, l’équipe des outils de modération va lancer [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|un nouveau filtre dans les modifications récentes]], en commençant par la Wikipédia en indonésien. Ce nouveau filtre met en évidence les modifications susceptibles d’être annulées. L’objectif est d’aider les patrouilleurs des modifications récentes à repérer les contributions potentiellement problématiques. D’autres wikis bénéficieront de ce filtre à l’avenir.
* Lorsqu’ils cliquent sur une barre de recherche vide, les utilisateurs non connectés verront des suggestions d’articles à lire. Cette fonctionnalité sera disponible à la fois sur ordinateur et sur mobile. Les lecteurs des Wikipédias en catalan, hébreu et italien ainsi que de certains projets frères recevront ce changement entre le 21 mai et la mi-juin. Les lecteurs des autres wikis le recevront plus tard. L’objectif est d’encourager les utilisateurs à lire davantage les wikis. [[mw:Special:MyLanguage/Reading/Web/Content Discovery Experiments/Search Suggestions|En savoir plus]].
* Certains utilisateurs de l’application Wikipédia sur Android peuvent utiliser une nouvelle fonctionnalité destinée aux lecteurs : [[mw:Special:MyLanguage/Wikimedia Apps/Team/Android/TrivaGame|« WikiGames »]], un jeu quotidien de quiz basé sur des événements historiques réels. Le déploiement a commencé sous forme de test A/B, disponible pour 50 % des utilisateurs dans les langues suivantes : anglais, français, portugais, russe, espagnol, arabe, chinois et turc.
* L’[[mw:Special:MyLanguage/Extension:Newsletter|extension « Newsletter »]] disponible sur MediaWiki.org permet la création de [[mw:Special:Newsletters|divers bulletins d’information]] à destination des utilisateurs de tous les wikis. L’extension peut désormais publier de nouveaux numéros sous forme de liens vers des sections sur une page existante, au lieu de nécessiter une nouvelle page pour chaque numéro. [https://phabricator.wikimedia.org/T393844]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:32|la tâche soumise|les {{formatnum:32}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:32||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les champs <code dir=ltr>[[mw:Special:MyLanguage/Manual:Ipblocks table|ipblocks]]</code>, précédemment déclarés obsolètes, seront supprimés début juin dans les [[wikitech:Help:Wiki Replicas|Wiki Replicas]]. Il est recommandé aux utilisateurs d’interroger les nouveaux champs <code dir=ltr>[[mw:Special:MyLanguage/Manual:Block table|block]]</code> et <code dir=ltr>[[mw:Special:MyLanguage/Manual:Block target table|block_target]]</code> à la place.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.3|MediaWiki]]
'''Rencontres et évènements'''
* [[d:Special:MyLanguage/Event:Wikidata and Sister Projects|« Wikidata et les projets frères »]] est un événement en ligne de plusieurs jours qui portera sur l’intégration de Wikidata à Wikipédia et aux autres projets Wikimedia. L’événement se déroulera du 29 mai au 1<sup>er</sup> juin. Vous pouvez [[d:Special:MyLanguage/Event:Wikidata and Sister Projects#Sessions|consulter le programme]] et [[d:Special:RegisterForEvent/1291|vous inscrire]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/22|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W22"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 26 mai 2025 à 22:04 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28788673 -->
== Sélection 2025 du conseil d'administration de la fondation Wikimédia et appel à questions ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Announcement/Selection announcement|{{int:interlanguage-link-mul}}]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Wikimedia Foundation elections/2025/Announcement/Selection announcement}}&language=&action=page&filter= {{int:please-translate}}]''
Chers tous et toutes,
Cette année, le mandat de deux administrateurs sélectionnés par la communauté et les affiliés au conseil d'administration de la Fondation Wikimédia prendra fin [1]. Le conseil invite l'ensemble du mouvement à participer au processus de sélection de cette année et à voter pour pourvoir ces sièges.
Le Comité des élections supervisera ce processus avec le soutien du personnel de la Fondation [2]. Le Comité de gouvernance, composé de membres du Conseil d'administration non-candidats au processus de sélection des membres du Conseil d'administration 2025 sélectionnés par la communauté et les affiliés (Raju Narisetti, Shani Evenstein Sigalov, Lorenzo Losa, Kathy Collins, Victoria Doronina et Esra'a Al Shafei) [3], est chargé de superviser le processus de sélection des membres du Conseil d'administration 2025 et de tenir le Conseil d'administration au courant de la situation. Pour plus de détails sur les rôles de la commission des élections, du conseil d'administration et du personnel, cliquez ici [4].
En voici les dates clés :
* 22 mai - 5 juin : Annonce ( la présente communication) et période d'appel à questions. [6]
* 17 juin - 1er juillet 2025 : Appel à candidatures
* Juillet 2025 : Si nécessaire, les affiliés votent pour présélectionner les candidats si 10 d'entre eux ou plus se présentent [5].
* Août 2025 : Période de la campagne
* Août - septembre 2025 : Période de vote communautaire de deux semaines
* Octobre - novembre 2025 : Vérification des antécédents des candidats sélectionnés
* Réunion du conseil d'administration en décembre 2025 : Installation des nouveaux membres du conseil d'administration
Pour en savoir plus sur le processus de sélection de 2025 - y compris le calendrier détaillé, le processus de candidature, les règles de la campagne et les critères d'éligibilité des électeurs -, veuillez consulter cette page Meta-wiki. [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2025|[link]]].
'''Appel à questions'''
Lors de chaque processus de sélection, la communauté a la possibilité de soumettre des questions auxquelles les candidats au conseil d'administration devront répondre. Le comité électoral sélectionne les questions à partir de la liste établie par la communauté pour que les candidats y répondent. Les candidats doivent répondre à toutes les questions posées dans le dossier de candidature pour être éligibles, faute de quoi leur candidature sera rejetée. Cette année, le comité électoral sélectionnera 5 questions auxquelles les candidats devront répondre. Les questions sélectionnées peuvent être une combinaison de celles qui ont été soumises par la communauté, si elles sont similaires ou liées. [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2025/Questions_for_candidates|[link]]]
'''Bénévoles des élections'''
Une autre façon de participer au processus de sélection de 2025 est de devenir bénévole des élections. Les bénévoles électoraux constituent un pont entre le comité électoral et leur communauté respective. Ils veillent à ce que leur communauté soit représentée et les incitent à voter. Pour en savoir plus sur le programme et les modalités d'adhésion, consultez cette page Meta-wiki. [[m:Wikimedia_Foundation_elections/2025/Election_volunteers|[link]]].
Je vous remercie !
[1] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2022/Results
[2] https://foundation.wikimedia.org/wiki/Committee:Elections_Committee_Charter
[3] https://foundation.wikimedia.org/wiki/Resolution:Committee_Membership,_December_2024
[4] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections_committee/Roles
[5] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2025/FAQ
[6] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2025/Questions_for_candidates
Bien à vous,
Victoria Doronina
Liaison du conseil d'administration avec le comité des élections
Comité de gouvernance<section end="announcement-content" />
[[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 28 mai 2025 à 05:07 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 -->
== Actualités techniques n° 2025-23 ==
<section begin="technews-2025-W23"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/23|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* L'[[mw:Special:MyLanguage/Extension:Chart|extension Chart]] est maintenant disponible sur tous les wikis Wikimedia. Les éditeurs peuvent utiliser cette nouvelle extension pour créer des visualisations de données interactives comme des diagrammes à barres, à lignes, avec des zones, et circulaires. Chart a été créée pour remplacer la plupart des utilisations de l'ancienne [[mw:Special:MyLanguage/Extension:Graph|extension Graph]].
'''Actualités pour la contribution'''
* Il est maintenant plus simple de configurer les citations automatiques pour votre wiki dans le [[mw:Special:MyLanguage/Citoid/Enabling Citoid on your wiki|générateur de citations]] de l'éditeur visuel. Les administrateurs peuvent maintenant définir un modèle par défaut en utilisant la clé <code dir=ltr>_default</code> dans la page locale <bdi lang="en" dir="ltr">[[MediaWiki:Citoid-template-type-map.json]]</bdi> ([[mw:Special:Diff/6969653/7646386|exemple de modification]]). Définir ce réglage par défaut permettra aussi de pérenniser vos configurations existantes lorsque de [[phab:T347823|nouveaux types d'objets]] seront ajoutés à l'avenir. Vous pouvez toujours définir des modèles pour des types d'objets individuels et ils seront prioritaires par rapport au modèle par défaut. [https://phabricator.wikimedia.org/T384709]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:20|la tâche soumise|les {{formatnum:20}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:20||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* À partir de la semaine du 2 juin, les robots qui utilisent <code dir=ltr>action=login</code> ou <code dir=ltr>action=clientlogin</code> pour s'authentifier auront un taux d'échec plus fréquent. Cela est dû à des protections plus fortes contre les connexions suspectes. Les robots qui utilisent des [[mw:Special:MyLanguage/Manual:Bot passwords|mots de passe de robots]] ou une authentification sans connexion telle que [[mw:Special:MyLanguage/OAuth/Owner-only consumers|OAuth]] ne seront pas affectés. Si votre bot n'utilise aucun des deux, vous devriez le mettre à jour ; utiliser <code dir=ltr>action=login</code> sans un mot de passe de robot a été rendu désuet [[listarchive:list/wikitech-l@lists.wikimedia.org/message/3EEMN7VQX5G7WMQI5K2GP5JC2336DPTD/|en 2016]]. Pour la plupart des robots, cela nécessite seulement de changer quel mot de passe ce dernier utilise. [https://phabricator.wikimedia.org/T395205]
* À partir de cette semaine, les wikis Wikimedia permettront des fonctionnalités ES2017 dans le code JavaScript pour le code officiel, les gadgets et les scripts utilisateurs. La fonctionnalité la plus visible d'ES2017 est la syntaxe <bdi lang="zxx" dir="ltr"><code>async</code>/<code>await</code></bdi>, ce qui permet un code plus facile à lire. Jusqu'à cette semaine, la plateforme ne permettait que jusqu'à ES2016, et quelques mois plus tôt, jusqu'à ES2015. [https://phabricator.wikimedia.org/T381537]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.4|MediaWiki]]
'''Rencontres et évènements'''
* Les demandes de bourse d'études pour participer à la [[m:Special:MyLanguage/GLAM Wiki 2025|conférence GLAM 2025]] sont maintenant ouvertes. La conférence aura lieu du 30 octobre au 1er novembre, à Lisbonne, au Portugal. Les contributeurs GLAM qui n'ont pas les moyens de financer leur participation peuvent [[m:Special:MyLanguage/GLAM Wiki 2025/Scholarships|faire une demande ici]]. La date limite de candidature est le 7 juin.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/23|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W23"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 3 juin 2025 à 01:54 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28819186 -->
== Actualités techniques n° 2025-24 ==
<section begin="technews-2025-W24"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/24|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* L’[[mw:Special:MyLanguage/Trust and Safety Product|équipe produits Confiance et sûreté]] finalise les travaux nécessaires au déploiement des [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] sur les grandes Wikipédias plus tard ce mois-ci. L’équipe a collaboré avec les stewards et d’autres utilisateurs disposant de droits étendus afin d’anticiper et de traiter de nombreux cas d’usage qui pourraient se présenter sur les wikis de grande taille, afin que les membres des communautés puissent continuer à modérer et à patrouiller efficacement les comptes temporaires. Il s’agira de la deuxième des trois phases de déploiement ; la dernière aura lieu en septembre au plus tôt. Pour plus d’informations sur les développements récents du projet, [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Updates|voir cette mise à jour]]. Si vous avez des commentaires ou des questions, écrivez sur la [[mw:Talk:Trust and Safety Product/Temporary Accounts|page de discussion]] et [[m:Event:CEE Catch up Nr. 10 (June 2025)|rejoignez un « CEE Catch Up »]] ce mardi.
'''Actualités pour la contribution'''
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] La fonctionnalité [[mw:Special:MyLanguage/Help:Watchlist expiry|« expiration de la liste de suivi »]] permet aux contributeurs de suivre des pages pendant une durée limitée. Une fois ce délai écoulé, la page est automatiquement retirée de votre liste de suivi. À partir de cette semaine, vous pouvez définir une préférence pour la durée par défaut pendant laquelle vous souhaitez suivre les pages. Les [[Special:Preferences#mw-prefsection-watchlist-pageswatchlist|préférences]] permettent également de définir différentes durées par défaut selon que vous modifiez une page existante, que vous en créez une nouvelle ou que vous utilisez l’annulation rapide (''rollback''). [https://phabricator.wikimedia.org/T265716]
[[File:Talk pages default look (April 2023).jpg|thumb|alt=Capture d'écran des améliorations visuelles apportées aux pages de discussion|Exemple d'une page de discussion avec les améliorations, en français.]]
* L’apparence des pages de discussion va changer sur la quasi-totalité des Wikipédias ([[m:Special:MyLanguage/Tech/News/2024/19|certaines]] ont déjà reçu ce nouveau design, [[phab:T379264|quelques-unes]] le recevront plus tard). Vous pouvez lire les détails concernant ces changements [[diffblog:2024/05/02/making-talk-pages-better-for-everyone/|sur ''Diff'']]. Il est possible de désactiver ces modifications [[Special:Preferences#mw-prefsection-editing-discussion|dans les préférences utilisateur]] (« {{int:discussiontools-preference-visualenhancements}} »). [https://phabricator.wikimedia.org/T319146][https://phabricator.wikimedia.org/T392121]
* Les utilisateurs disposant de certains droits étendus (y compris les administrateurs, bureaucrates, vérificateurs, masqueurs et stewards) peuvent désormais voir les adresses IP de tous les comptes temporaires [[phab:T358853|révélées automatiquement]] pendant des périodes limitées, lorsqu’ils doivent lutter contre du vandalisme rapide impliquant des changements fréquents de compte. Cette fonctionnalité a été demandée par les stewards. [https://phabricator.wikimedia.org/T386492]
* Cette semaine, les équipes des outils de modération et d’apprentissage automatique poursuivent le déploiement [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|d’un nouveau filtre dans les Modifications récentes]], en l’étendant à plusieurs autres Wikipédias. Ce filtre utilise le modèle « Revert Risk », développé par l’équipe de recherche, pour mettre en évidence les modifications susceptibles d’être annulées et aider les patrouilleurs à repérer les contributions potentiellement problématiques. La fonctionnalité sera déployée sur les Wikipédias suivantes : {{int:project-localized-name-afwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-bewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-bnwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-cywiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-hawwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-iswiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-kkwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-simplewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-trwiki/fr}}. Le déploiement se poursuivra dans les semaines à venir pour inclure [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|le reste des Wikipédias concernées par ce projet]]. [https://phabricator.wikimedia.org/T391964]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les éditeurs de filtres anti-abus actifs sur Meta-Wiki et les grandes Wikipédias sont priés de mettre à jour les filtres pour les rendre compatibles avec les comptes temporaires. Un lien vers les instructions ainsi que vers les listes privées des filtres à vérifier est [[phab:T369611|disponible sur Phabricator]].
* Les modules Lua ont désormais accès au nom de l’image miniature associée à une page, et sur [https://gerrit.wikimedia.org/g/operations/mediawiki-config/+/2e4ab14aa15bb95568f9c07dd777065901eb2126/wmf-config/InitialiseSettings.php#10849 certains wikis], aux informations d’évaluation des WikiProjets. Cela est possible grâce à deux nouvelles propriétés des objets [[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#added-by-extensions|mw.title]], nommées <code dir=ltr>pageImage</code> et <code dir=ltr>pageAssessments</code>. [https://phabricator.wikimedia.org/T131911][https://phabricator.wikimedia.org/T380122]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.5|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/24|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W24"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 10 juin 2025 à 03:16 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28846858 -->
== Vote now in the 2025 U4C Election ==
<div lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English.
{{Int:Please-translate}}
Eligible voters are asked to participate in the 2025 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2025|2025 Election information page]]. The vote closes on 17 June 2025 at [https://zonestamp.toolforge.org/1750161600 12:00 UTC].
Please vote if your account is eligible. Results will be available by 1 July 2025. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 14 juin 2025 à 01:00 (CEST) </div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28848819 -->
== <span lang="en" dir="ltr">Tech News: 2025-25</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W25"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/25|Translations]] are available.
'''Updates for editors'''
* You can [https://wikimediafoundation.limesurvey.net/359761?lang=en nominate your favorite tools] for the sixth edition of the [[m:Special:MyLanguage/Coolest Tool Award|Coolest Tool Award]]. Nominations are anonymous and will be open until June 25. You can re-use the survey to nominate multiple tools.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:33}} community-submitted {{PLURAL:33|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]].
'''Updates for technical contributors'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.45/wmf.6|MediaWiki]]
'''In depth'''
* Foundation staff and technical volunteers use Wikimedia APIs to build the tools, applications, features, and integrations that enhance user experiences. Over the coming years, the MediaWiki Interfaces team will be investing in Wikimedia web (HTTP) APIs to better serve technical volunteer needs and protect Wikimedia infrastructure from potential abuse. You can [https://techblog.wikimedia.org/2025/06/12/apis-as-a-product-investing-in-the-current-and-next-generation-of-technical-contributors/ read more about their plans to evolve the APIs in this Techblog post].
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/25|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W25"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 17 juin 2025 à 01:38 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28870688 -->
== Conseil d'administration de la Fondation Wikimédia 2025 - Appel à candidatures ==
<section begin="announcement-content" />
:''<div class="plainlinks">[[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Announcement/Call for candidates|{{int:interlanguage-link-mul}}]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Wikimedia Foundation elections/2025/Announcement/Call for candidates}}&language=&action=page&filter= {{int:please-translate}}]</div>
Bonjour à toutes et à tous,
L'appel [[m:Special:MyLanguage/Wikimedia Foundation elections/2025|à candidatures pour la sélection du Conseil d'administration de la Fondation Wikimédia 2025]] est désormais ouvert du 17 juin 2025 au 2 juillet 2025 à 11:59 UTC [1]. Le conseil d'administration est chargé de superviser le travail de la Fondation Wikimédia, et chaque membre du conseil d'administration est nommé pour un mandat de trois ans [2]. Il s'agit d'un rôle bénévole.
Cette année, la communauté Wikimédia votera entre la fin du mois d'août et le mois de septembre 2025 pour élire deux (2) membres du Conseil d'administration de la Fondation. Seriez-vous - ou connaissez-vous quelqu'un - susceptible de rejoindre le conseil d'administration de la Fondation Wikimedia ? [3]
Apprenez-en plus sur les exigences pour briguer ces postes de direction et sur la manière de soumettre votre candidature sur [[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Candidate application|cette page Meta-wiki]] ou encouragez quelqu’un d’autre à se présenter à l’élection de cette année.
Bien à vous,
Abhishek Suryawanshi<br />
Président du Comité des élections
Au nom du comité des élections et du comité de gouvernance
[1] https://meta.wikimedia.org/wiki/Special:MyLanguage/Wikimedia_Foundation_elections/2025/Call_for_candidates
[2] https://foundation.wikimedia.org/wiki/Legal:Bylaws#(B)_Term.
[3] https://meta.wikimedia.org/wiki/Special:MyLanguage/Wikimedia_Foundation_elections/2025/Resources_for_candidates<section end="announcement-content" />
[[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 17 juin 2025 à 19:43 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28866958 -->
== Actualités techniques n° 2025-26 ==
<section begin="technews-2025-W26"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/26|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Cette semaine, les équipes des outils de modération et d'apprentissage automatique poursuivront le déploiement d'[[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|un nouveau filtre pour les modifications récentes]], le lançant à la troisième et dernière série de Wikipédia. Ce filtre utilise le modèle de risque de réversion, qui a été créé par l'équipe de recherche, pour mettre en avant les modifications susceptibles d'être annulées et aider les patrouilleurs des modifications récentes à identifier les contributions potentiellement problématiques. La fonctionnalité sera déployée sur les Wikipédias suivantes : {{int:project-localized-name-azwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-lawiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-mkwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-mlwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-mrwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-nnwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-pawiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-swwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-tewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-tlwiki/fr}}. Le déploiement se poursuivra dans les semaines à venir pour inclure [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|le reste des Wikipédias de ce projet]]. [https://phabricator.wikimedia.org/T391964]
'''Actualités pour la contribution'''
* La semaine dernière, des [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] ont été déployés sur les Wikipédias tchèque, coréenne et turque. Cette semaine et la semaine prochaine, des déploiements sur des Wikipédias plus importantes suivront. [[mw:Talk:Trust and Safety Product/Temporary Accounts|Partagez vos pensées]] sur le projet. [https://phabricator.wikimedia.org/T340001]
* Plus tard cette semaine, l'équipe d'Édition publiera ''[[mw:Special:MyLanguage/Help:Edit check#Multi check|Multi Check]]'' sur toutes les Wikipédias (à l'exception de la Wikipédia en anglais). Cette fonctionnalité affiche plusieurs [[mw:Special:MyLanguage/Help:Edit check#Reference check|vérifications de références]] dans l'interface d'édition. Elle encourage les utilisateurs à ajouter des citations lorsqu'ils ajoutent plusieurs nouveaux paragraphes à un article Wikipédia. Cette fonctionnalité était auparavant disponible en tant que test A/B. [https://analytics.wikimedia.org/published/reports/editing/multi_check_ab_test_report_final.html#summary-of-results Le test montre] que les utilisateurs qui voient plusieurs vérifications sont 1,3 fois plus susceptibles d'ajouter une référence à leur modification, et que leur modification est moins susceptible d'être annulée (-34,7 %). [https://phabricator.wikimedia.org/T395519]
* Quelques pages doivent être renommées en raison de mises à jour logicielles et pour correspondre à des normes Unicode plus récentes. Tous ces changements sont liés aux modifications de la capitalisation des titres. Environ 71 pages et 3 fichiers seront renommés, sur 15 wikis ; la liste complète se trouve dans [[phab:T396903|la tâche]]. Les développeurs renommeront ces pages la semaine prochaine, et ils corrigeront les redirections et les liens de fichiers intégrés quelques minutes plus tard via une mise à jour des paramètres système.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:24|la tâche soumise|les {{formatnum:24}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:24||s}} la semaine dernière]]. Par exemple, un bug qui avait fait défiler les pages vers le haut lorsque du texte près du haut était sélectionné a été corrigé. [https://phabricator.wikimedia.org/T364023]
'''Actualités pour la contribution technique'''
* Les éditeurs peuvent désormais utiliser des modules Lua pour filtrer et transformer des données tabulaires à utiliser avec [[mw:Special:MyLanguage/Extension:Chart|Extension:Chart]]. Cela peut être utilisé pour des choses comme sélectionner un sous-ensemble de lignes ou de colonnes à partir des données sources, convertir entre des unités, le traitement statistique et de nombreuses autres transformations utiles. [[mw:Special:MyLanguage/Extension:Chart/Transforms|Des informations sur la façon d'utiliser les transformations sont disponibles]]. [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:Chart/Project/Updates]
* La variable <code dir=ltr>all_links</code> dans [[Special:AbuseFilter|AbuseFilter]] a été renommée <code dir=ltr>new_links</code> pour plus de cohérence avec les autres variables. Les anciennes utilisations continueront de fonctionner. [https://phabricator.wikimedia.org/T391811]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.7|MediaWiki]]
'''En détails'''
* Le dernier [[mw:Special:MyLanguage/Growth/Newsletters/34|bulletin de croissance]] trimestriel est disponible. Il contient : les mises à jour récentes pour la tâche "Ajouter un lien", deux nouvelles fonctionnalités d'engagement des nouveaux arrivants et des mises à jour de la configuration de la communauté.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/26|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W26"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 24 juin 2025 à 01:20 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28870688 -->
== Commentaires sur les projets de Wikimédia ==
<section begin="message"/>
Chère communauté Wikimédia,
[[m:Wikimedia Foundation Community Affairs Committee|Le Comité des affaires communautaires (CAC)]] du conseil d'administration de la Fondation Wikimedia a chargé [[m:Wikimedia Foundation Community Affairs Committee/Sister Projects Task Force|le groupe de travail sur les projets frères (SPTF)]] de mettre à jour et d’implémenter une procédure d'évaluation du cycle de vie des projets frères, c'est-à-dire [[m:Wikimedia projects|des projets wiki soutenus par la Fondation Wikimédia (WMF)]].
La vision d'une connaissance libre, pertinente, accessible et percutante a toujours guidé le mouvement Wikimédia. Alors que l’environnement des projets Wikimédia continue d'évoluer, il est crucial de réévaluer régulièrement les projets existants afin de nous assurer qu'ils correspondent toujours à nos objectifs et aux capacités de la communauté.
Malgré leurs nobles intentions, certains projets peuvent ne plus remplir efficacement leur objectif initial. '''Examiner de tels projets ne signifie pas les abandonner, mais plutôt gérer de manière responsable les ressources partagées'''. Le temps des bénévoles, le soutien du personnel, les infrastructures et l'attention de la communauté sont limités, et les coûts non techniques ont tendance à augmenter considérablement à mesure que notre écosystème entre dans une ère d'Internet différente de celle de nos débuts. Soutenir des projets inactifs ou qui ne répondent pas à nos ambitions peut involontairement détourner ces ressources de domaines à plus fort impact potentiel.
De plus, maintenir des projets qui ne reflètent plus la qualité et la fiabilité que Wikimédia représente comporte un risque pour la réputation. Un projet abandonné ou moins fiable affecte la confiance dans le mouvement Wikimédia.
Enfin, '''ne pas abandonner ou repenser les projets qui ne fonctionnent plus peut rendre beaucoup plus difficile le lancement de nouveaux projets'''. Lorsque la communauté se sent liée à toutes les décisions passées, aussi obsolètes soient-elles, nous risquons la stagnation. Un environnement sain doit permettre l'évolution, l'adaptation et, si nécessaire, de lâcher-prise. Si nous créons l'attente que chaque projet doit exister indéfiniment, nous limitons notre capacité d'expérimentation et d'innovation.
C'est pourquoi le SPTF a examiné des demandes concernant le cycle de vie de deux projets frères afin d'analyser et de démontrer le processus d'évaluation. Nous avons choisi Wikispore comme étude de cas pour l'ouverture éventuelle d'un nouveau projet frère, et Wikinews comme étude de cas pour l'évaluation d'un projet existant. Les conclusions préliminaires ont été discutées lors de la réunion du CAC du 11 septembre 2024, et le CAC a recommandé une consultation communautaire sur les deux propositions.
=== Wikispore ===
La [[m:Wikispore|demande d'admission de Wikispore]] comme nouveau projet frère a été soumise en 2019. Le SPTF a décidé d'examiner cette demande plus en détail, car, plutôt que de se concentrer sur un sujet spécifique, comme le sont la plupart des propositions de nouveaux projets frères, Wikispore a le potentiel de soutenir plusieurs projets frères en cours de démarrage.
Après mûre réflexion, le SPTF a décidé de '''ne pas recommander''' Wikispore comme projet frère Wikimedia. Compte tenu du niveau d'activité actuel, la structure en cours offre une '''plus grande flexibilité''' et plus d’expérimentation, tandis que la WMF fournit le soutien infrastructurel de base.
Nous reconnaissons le potentiel de l'initiative et sollicitons l'avis de la communauté sur ce qui constituerait un niveau d'activité et d'engagement suffisant pour reconsidérer son statut à l'avenir.
Dans le cadre de ce processus, nous avons partagé la décision avec la communauté Wikispore et invité l'un de ses responsables, Pharos, à une réunion du SPTF.
En ce moment, nous sollicitons particulièrement vos commentaires sur des critères mesurables indiquant l'état de préparation du projet, tels que le nombre de contributeurs, le volume de contenu et le soutien durable de la communauté. Cela clarifiera les critères nécessaires à l'ouverture d'un nouveau projet frère, y compris une éventuelle nouvelle candidature de Wikispore. Cependant, les chiffres restent seulement indicatifs, car ils peuvent toujours être manipulés.
=== Wikinews ===
Nous avons choisi d'évaluer Wikinews parmi les projets frères existants, car c'est celui pour lequel nous avons observé le plus grand niveau d'inquiétude à plusieurs égards.
Depuis la création du SPTF en 2023, ses membres ont sollicité l'avis de la communauté lors de conférences et d'appels communautaires concernant les projets frères qui n'avaient pas tenu leurs promesses au sein du mouvement Wikimédia.[https://commons.wikimedia.org/wiki/File:WCNA_2024._Sister_Projects_-_opening%3F_closing%3F_merging%3F_splitting%3F.pdf <nowiki>[1]</nowiki>][https://meta.wikimedia.org/wiki/Wikimedia_Foundation_Community_Affairs_Committee/Sister_Projects_Task_Force#Wikimania_2023_session_%22Sister_Projects:_past,_present_and_the_glorious_future%22 <nowiki>[2]</nowiki>][https://meta.wikimedia.org/wiki/WikiConvention_francophone/2024/Programme/Quelle_proc%C3%A9dure_pour_ouvrir_ou_fermer_un_projet_%3F <nowiki>[3]</nowiki>] Wikinews était le principal candidat à une évaluation, car il avait été proposé par des personnes issues de plusieurs communautés linguistiques. De plus, selon la plupart des indicateurs, il s'agit du projet frère le moins actif, affichant la plus forte baisse d'activité au fil des ans.
Bien que le comité des langues ouvre et ferme régulièrement les versions linguistiques des projets frères dans des « petites » langues, aucune proposition valable n'a jamais été présentée pour fermer Wikipédia dans les langues majeures, ni aucun projet en anglais. Ce n'est pas le cas de Wikinews, où une proposition de fermeture de Wikinews en anglais a été formulée, laquelle a suscité un certain intérêt, mais n'a donné lieu à aucune action,[https://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_English_Wikinews <nowiki>[4]</nowiki>][https://meta.wikimedia.org/wiki/WikiConvention_francophone/2024/Programme/Quelle_proc%C3%A9dure_pour_ouvrir_ou_fermer_un_projet_%3F voir également la section 5 de <nowiki>[5]</nowiki>], ainsi qu'un projet de proposition visant à fermer Wikinews dans toutes les langues[https://meta.wikimedia.org/wiki/Talk:Proposals_for_closing_projects/Archive_2#Close_Wikinews_completely,_all_languages? <nowiki>[6]</nowiki>].
[[:c:File:Sister Projects Taskforce Wikinews review 2024.pdf|Les indicateurs initiaux]] compilés par l'équipe de la WMF soutiennent également les inquiétudes de la communauté du mouvement concernant Wikinews.
Sur la base de ce rapport, la SPTF recommande une réévaluation communautaire de Wikinews. Nous concluons que sa structure actuelle et son niveau d'activité sont les plus faibles parmi les projets frères existants. La SPTF recommande également de suspendre l'ouverture de nouvelles éditions linguistiques pendant la durée de la consultation.
La SPTF soumet cette analyse à la discussion et encourage les discussions sur d'autres solutions, notamment d'éventuelles restructurations ou une intégration avec d'autres initiatives de Wikimédia.
Les options évoquées jusqu'à présent (peuvent être appliquées uniquement aux langues peu actives ou à toutes les langues) incluent, sans s'y limiter:
*Restructurer le fonctionnement de Wikinews et l'associer aux autres projets d'actualité;
*Fusionner le contenu de Wikinews dans les Wikipédias des langues concernées, éventuellement dans un nouvel espace de noms,
*Fusionner le contenu dans des projets externes sous licence compatible,
*Archiver les projets Wikinews.
Vos idées et points de vue sont précieux pour façonner l'avenir de ces projets. Nous encourageons tous les membres de la communauté intéressés à partager leurs réflexions sur les pages de discussion correspondantes ou via les autres canaux de commentaires dédiés.
<span id="Feedback_and_next_steps"></span>
=== Commentaires et prochaines étapes ===
Nous vous serons reconnaissants si vous pouvez participer à une discussion sur l'avenir de ces projets et sur le processus d'évaluation. Nous créons actuellement deux pages de projets: [[m:Public consultation about Wikispore|Public consultation about Wikispore]] et [[m:Public consultation about Wikinews|Public consultation about Wikinews]]. Merci de participer entre le 27 juin et le 27 juillet, date à laquelle nous résumerons la discussion pour la suite. Vous pouvez écrire dans votre langue.
J'animerai également une discussion communautaire le mercredi 16 juillet à 11 h UTC et le jeudi 17 juillet à 17 h UTC (les liens pour se connecter seront communiqués prochainement) et je serai présente à Wikimania pour poursuivre les discussions.
<section end="message"/>
-- [[User:Victoria|Victoria]] on behalf of the Sister Project Task Force, 27 juin 2025 à 22:56 (CEST)
<!-- Message envoyé par User:Johan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Johan_(WMF)/Sister_project_MassMassage_on_behalf_of_Victoria/Target_list&oldid=28911188 -->
== Actualités techniques n° 2025-27 ==
<section begin="technews-2025-W27"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/27|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* La [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|extension CampaignEvents]] a été activée sur toutes les Wikipédias. L'extension facilite l'organisation et la participation à des activités collaboratives, telles que les edit-a-thons et les WikiProjects, sur les wikis. L'extension a trois fonctionnalités : [[m:Special:MyLanguage/Event Center/Registration|Inscription à un événement]], [[m:Special:MyLanguage/CampaignEvents/Collaboration list|Liste de collaboration]], et [[m:Campaigns/Foundation Product Team/Invitation list|Liste d'invitation]]. Pour demander l'extension pour votre wiki, visitez la [[m:Special:MyLanguage/CampaignEvents/Deployment status#How to Request the CampaignEvents Extension for your wiki|page d'information sur le déploiement]].
'''Actualités pour la contribution'''
* Les mainteneurs des filtres de abus peuvent maintenant [[mw:Special:MyLanguage/Extension:IPReputation/AbuseFilter variables|faire correspondre avec les données de réputation IP]] dans [[mw:Special:MyLanguage/Extension:AbuseFilter|Filtres d'abus]]. Les données de réputation IP sont des informations sur les proxys et les VPN associés à l'adresse IP de l'utilisateur. Ces données ne sont pas affichées publiquement et ne sont pas générées pour les actions effectuées par des comptes enregistrés. [https://phabricator.wikimedia.org/T354599]
* Le contenu caché qui se trouve dans [[mw:Special:MyLanguage/Manual:Collapsible elements|des parties réductibles des pages wiki]] sera désormais révélé lorsqu'une personne recherche la page en utilisant la fonction « Rechercher dans la page » du navigateur Web (Ctrl+F ou ⌘F) dans les navigateurs compatibles. [https://phabricator.wikimedia.org/T327893][https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/hidden#browser_compatibility]
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] Une nouvelle fonctionnalité, appelée [[mw:Special:MyLanguage/Help:TemplateData/Template discovery|Modèles favoris]], sera déployée plus tard cette semaine sur tous les projets (sauf la Wikipédia en anglais, qui recevra la fonctionnalité la semaine prochaine), suite à une phase de test sur les Wikipédias polonaise et arabe, et sur les Wikisource italien et anglais. Cette fonctionnalité offrira une meilleure façon pour les contributeurs nouveaux et expérimentés de se souvenir et de découvrir des modèles via la boîte de dialogue des modèles, en permettant aux utilisateurs de mettre des modèles sur une "liste de favoris" spéciale. La fonctionnalité fonctionne à la fois avec l'éditeur visuel et l'éditeur wikitext. Cette fonctionnalité est un [[m:Special:MyLanguage/Community Wishlist/Focus areas/Template recall and discovery|domaine d'intérêt de la wishlist de la communauté]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:31|la tâche soumise|les {{formatnum:31}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:31||s}} la semaine dernière]]. Par exemple, un bug a été corrigé qui avait causé l'envoi de certaines notifications plusieurs fois. [https://phabricator.wikimedia.org/T397103]
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.8|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/27|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W27"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 1 juillet 2025 à 01:40 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28917415 -->
== Actualités techniques n° 2025-28 ==
<section begin="technews-2025-W28"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/28|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Des [[mw:Special:MyLanguage/Help:Temporary accounts|comptes temporaires]] ont été déployés sur 18 Wikipédias larges et de taille moyenne, y compris l'allemand, japonais, français et chinois. Maintenant, environ un tiers de toute l'activité des utilisateurs non connectés sur les wikis provient des comptes temporaires. Les utilisateurs impliqués dans la surveillance peuvent être intéressés par deux nouvelles pages de documentation : [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Access to IP|Accès à IP]], expliquant tout ce qui concerne l'accès aux adresses IP des comptes temporaires, et [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Repository|Dépôt]] avec une liste de nouveaux gadgets et scripts utilisateur.
'''Actualités pour la contribution'''
* Tout le monde peut jouer à un nouveau jeu expérimental, [[mw:Special:MyLanguage/New Engagement Experiments/WikiRun|WikiRun]], qui vous permet de courir à travers Wikipédia en cliquant d'un article à l'autre, dans le but d'atteindre une page cible en aussi peu d'étapes et en aussi peu de temps que possible. L'objectif du projet est d'explorer de nouvelles façons d'engager les lecteurs. [https://wikirun-game.toolforge.org/ Essayez de jouer au jeu] et faites savoir à l'équipe ce que vous en pensez [[mw:Talk:New Engagement Experiments/WikiRun|sur la page de discussion]].
* Les utilisateurs de l'application Android de Wikipédia dans certaines langues peuvent désormais jouer au nouveau [[mw:Special:MyLanguage/Wikimedia Apps/Team/Android/TrivaGame|jeu de trivia]]. ''Lequel est arrivé en premier ?'' est un simple jeu d'histoire où vous devinez lequel de deux événements s'est produit plus tôt à la date du jour. Il était auparavant disponible en tant que test A/B. Il est maintenant disponible pour tous les utilisateurs en anglais, allemand, français, espagnol, portugais, russe, arabe, turc et chinois. L'objectif de cette fonctionnalité est d'aider à engager de nouvelles générations de lecteurs. [https://meta.wikimedia.org/wiki/Special:MyLanguage/Tech/News/2025/22]
* Les utilisateurs de l'application Wikipédia iOS dans certaines langues peuvent voir une nouvelle fonctionnalité de navigation par onglets qui vous permet d'ouvrir plusieurs onglets pendant que vous lisez. Cette fonctionnalité facilite l'exploration de sujets connexes et le passage d'un article à l'autre. Le test A/B est actuellement en cours en arabe, en anglais et en japonais dans des régions sélectionnées. Plus de détails sont disponibles sur la [[mw:Special:MyLanguage/Wikimedia Apps/Team/iOS/Tabbed Browsing (Tabs)|page du projet Navigation par onglets]].
* Les bureaucrates sur les wikis Wikimédia peuvent maintenant utiliser [[{{#special:VerifyOATHForUser}}]] pour vérifier si les utilisateurs ont activé [[mw:Special:MyLanguage/Help:Two-factor authentication|l'authentification à deux facteurs]]. [https://phabricator.wikimedia.org/T265726]
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] Une nouvelle fonctionnalité liée à [[m:Special:MyLanguage/Community Wishlist/Focus areas/Template recall and discovery|Rappel et découverte de modèle]] sera déployée plus tard cette semaine sur tous les projets Wikimédia : un [[mw:Special:MyLanguage/Help:TemplateData/Template discovery#Template categories|navigateur de catégories de modèles]] sera introduit pour aider les utilisateurs à trouver des modèles à ajouter à leur liste de « favoris ». Le navigateur permettra aux utilisateurs de parcourir une liste de modèles qui ont été organisés dans un arbre de catégories donné. La fonctionnalité a été demandée par la communauté [[m:Special:MyLanguage/Community Wishlist/Wishes/Select templates by categories|à travers la liste de souhaits de la communauté]].
* Il est désormais possible d'accéder aux préférences de la liste de surveillance depuis la page de la liste de surveillance. De plus, le bouton redondant pour modifier la liste de surveillance a été retiré. [https://www.mediawiki.org/wiki/Moderator_Tools/Watchlist]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Dans le cadre de [[mw:MediaWiki_1.44|MediaWiki 1.44]], il existe désormais un système de notifications intégré et unifié qui facilite l'envoi, la gestion et la personnalisation des notifications pour les développeurs. Consultez la documentation mise à jour à [[mw:Manual:Notifications|Manuel:Notifications]], des informations sur la migration dans [[phab:T388663|T388663]] et des détails sur les hooks obsolètes dans [[phab:T389624|T389624]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.9|MediaWiki]]
'''Rencontres et évènements'''
* [[d:Special:MyLanguage/Event:WikidataCon 2025|WikidataCon 2025]], la conférence dédiée à Wikidata est désormais ouverte pour [https://pretalx.com/wikidatacon-2025/cfp propositions de sessions] et pour [[d:Special:RegisterForEvent/1340|l'inscription]]. L'événement de cette année se tiendra en ligne du 31 octobre au 02 novembre et explorera le thème « Connecter les gens grâce aux données ouvertes liées ».
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/28|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W28"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 8 juillet 2025 à 02:05 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28930584 -->
== Wikidata Item and Property labels soon displayed in Wiki Watchlist/Recent Changes ==
''(Apologies for posting in English, you can help by translating into your language)''
Hello everyone, the [[m:Wikidata_For_Wikimedia_Projects/Clearer_Wikidata_Edit_Summaries/Resolve_Labels|Wikidata For Wikimedia Projects]] team is excited to announce an upcoming change in how Wikidata edit changelogs are displayed in your [[Special:Watchlist|Watchlists]] and [[Special:RecentChanges|Recent Changes]] lists. If an edit is made on Wikidata that affects a page in another Wikimedia Project, the changelog will contain some information about the nature of the edit. This can include a QID (or Q-number), a PID (or P-number) and a value (which can be text, numbers, dates, or also QID or PID’s). Confused by these terms? See the [[d:Special:MyLanguage/Wikidata:Glossary|Wikidata:Glossary]] for further explanations.
The upcoming change is scheduled for '''17.07.2025''', between '''1300 - 1500 UTC'''.
The change will display the label (item name) alongside any QID or PIDs, as seen in the image below:
[[File:Apr10 edit summary on Wikidata.png|An edit sum entry on Wikidata, labels display alongside their P- and Q-no.'s]]
These changes will only be visible if you have Wikidata edits enabled in your User Preferences for Watchlists and Recent Changes, or have the active filter ‘Wikidata edits’ checkbox toggled on, directly on the Watchlist and Recent Changes pages.
Your bot and gadget may be affected! There are thousands of bots, gadgets and user-scripts and whilst we have researched potential effects to many of them, we cannot guarantee there won’t be some that are broken or affected by this change.
Further information and context about this change, including how your bot may be affected can be found on this [[m:Wikidata_For_Wikimedia_Projects/Clearer_Wikidata_Edit_Summaries/Resolve_Labels|project task page]]. We welcome your questions and feedback, please write to us on this dedicated [[m:Talk:Wikidata_For_Wikimedia_Projects/Clearer_Wikidata_Edit_Summaries/Resolve_Labels|Talk page]].
Thank you, - [[m:User:Danny_Benjafield_(WMDE)|Danny Benjafield (WMDE)]] on behalf of the Wikidata For Wikimedia Projects Team. [[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 14 juillet 2025 à 14:46 (CEST)
<!-- Message envoyé par User:Danny Benjafield (WMDE)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Danny_Benjafield_(WMDE)/MassMessage_Test_List&oldid=28981877 -->
== Actualités techniques n° 2025-29 ==
<section begin="technews-2025-W29"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/29|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* [[mw:Special:MyLanguage/Help:TemplateData/Template discovery#Featured templates|Modèles en vedette]], une nouvelle fonctionnalité liée à [[m:Special:MyLanguage/Community Wishlist/Focus areas/Template recall and discovery|Rappel et Découverte de Modèles]] sera déployée cette semaine sur tous les projets Wikimédia : Avec cette fonctionnalité, les éditeurs pourront rapidement accéder à une liste de modèles qui sont susceptibles d'être utiles. Ces modèles seront affichés dans une liste, sous l'onglet "mis en avant" de l'interface de découverte des modèles. La fonctionnalité répond à une demande de la communauté [[m:Special:MyLanguage/Community Wishlist/Wishes/Easy access Templates|via la Liste de souhaits de la Communauté]]. [https://phabricator.wikimedia.org/T367428][https://phabricator.wikimedia.org/T392896]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:31|la tâche soumise|les {{formatnum:31}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:31||s}} la semaine dernière]]. Par exemple, la demande d'ajouter des polices malayalam dans l'[[oldWikisource:Special:MyLanguage/Wikisource:WS Export|Outil d'exportation de livres Wikisource]] a été résolue et maintenant, le rendu des lettres malayalam dans les livres Wikisource exportés est précis. [https://phabricator.wikimedia.org/T374457]
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.10|MediaWiki]]
'''En détails'''
* Les développeurs, créateurs et tous les Wikimédiens sont invités à [https://phabricator.wikimedia.org/project/board/7953/ soumettre une idée de projet] pour le Hackathon Wikimania 2025. Lisez [https://diff.wikimedia.org/2025/06/30/call-for-projects-wikimania-hackathon-2025-is-coming-to-nairobi/ cet article de blog Diff] pour plus de détails.
'''Rencontres et évènements'''
* La candidature pour la bourse [[m:WikiIndaba conference 2025|WikiIndaba 2025]] et la soumission de programme sont ouvertes jusqu'à 23h59 GMT le 20 juillet. WikiIndaba est une conférence régionale pour les Wikimédiens africains à la fois sur le continent et dans la diaspora, visant à s'unir et à grandir ensemble. Soumettez dès maintenant [https://docs.google.com/forms/d/e/1FAIpQLSdJTv68R1OPASXXDfpIl8EWiMLTM-TDwh6_5gNVvFuWccFZ2Q/viewform votre candidature à la bourse] et [https://ee.kobotoolbox.org/x/BI3omIfH votre proposition de programme]!
* [https://br.wikimedia.org/wiki/WikiCon_Brasil_2025 WikiCon Brésil 2025] aura lieu du 19 au 20 juillet à Salvador, Bahia, Brésil. Les membres de la communauté brésilienne sont encouragés à s'inscrire et à participer !
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/29|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W29"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 14 juillet 2025 à 22:09 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28980963 -->
== <span lang="en" dir="ltr">Tech News: 2025-30</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W30"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/30|Translations]] are available.
'''Updates for editors'''
* The Translation Suggestions feature in the [[mw:Special:MyLanguage/Content translation|Content Translation tool]] now has another level of article filters added to the "[https://en.wikipedia.org/w/index.php?title=Special:ContentTranslation&filter-type=automatic&filter-id=previous-edits&active-list=suggestions&from=en&to=fi#/ ... More]" category. Translators who use the Suggestions feature can now select and receive article suggestions that are customized to geographical locations of their interest using the new "{{int:Cx-sx-suggestions-filters-tab-regions}}" filter. [https://phabricator.wikimedia.org/T113257]
* Administrators can now limit "Add a Link" to newcomers. The [[mw:Special:MyLanguage/Help:Growth/Tools/Add a link|"Add a Link"]] Structured Task [[mw:Special:MyLanguage/Growth/Constructive activation experimentation#Enwiki A/B test & "Add a Link" Improvements (Wiki Experiences 1.2.11 & 1.2.16)|helps new account holders start editing]], but some communities have requested the ability to restrict it to its intended audience: newcomers. Administrators can configure this setting within the [[Special:CommunityConfiguration/GrowthSuggestedEdits|Community Configuration]] feature.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:29}} community-submitted {{PLURAL:29|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]].
'''Updates for technical contributors'''
* For AbuseFilter editors on [[phab:T392144|some wikis]], it is now possible to filter edits based on the RevertRisk score of the edit being attempted. It is only populated if the action being evaluated is an edit. For more information, please see the [[mw:Special:MyLanguage/Extension:ORES/AbuseFilter variables#What variables are available for use|ORES/AbuseFilter variables]] documentation.
* The [[mw:Special:MyLanguage/Beta Cluster|Beta Cluster]] wikis have [[listarchive:list/wikitech-l@lists.wikimedia.org/thread/YDABPV75LADRQCXMJAFWUP256N4EQ25B/|been moved]] from <code dir=ltr>beta.wmflabs.org</code> to <code dir=ltr>beta.wmcloud.org</code>. Users may need to update URLs in any tools, or in their password managers. Any related issues can be [[phab:T289318|reported in the task]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.45/wmf.11|MediaWiki]]
'''Meetings and events'''
* [[m:Special:MyLanguage/WikiCite 2025|WikiCite 2025]] will take place from 29–31 August, both online and in-person in Bern, Switzerland. The event's goals are to reconnect communities, institutions, and individuals working with open citations, bibliographic data, and the Wikidata/Wikibase ecosystem. Registration is open and the call for proposals will be announced soon. [https://lists.wikimedia.org/hyperkitty/list/wikidata@lists.wikimedia.org/message/KQZUG3ETKLBWPBYSB2YAWZIRPWHS24TG/]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/30|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W30"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 22 juillet 2025 à 01:42 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29005283 -->
== Actualités techniques n° 2025-31 ==
<section begin="technews-2025-W31"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/31|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Au cours des prochains mois, l’équipe Community Tech se concentrera sur les souhaits liés aux listes de suivi et aux pages des Modifications récentes. Elle est à la recherche de retours. Merci de [[m:Special:MyLanguage/Community Wishlist/Updates#July 24, 2025: Watchlists and Recent Changes pages|lire la dernière mise à jour]] et, si vous avez des idées, de [[m:Special:MyLanguage/Community Wishlist|soumettre un souhait]] sur ce sujet.
'''Actualités pour la contribution'''
* La communauté de Wikimedia Commons a décidé de bloquer les [[:mw:Special:MyLanguage/Upload dialog|téléversements interwiki]] vers Wikimedia Commons pour tous les utilisateurs ne disposant pas des droits d’utilisateur autoconfirmé sur ce wiki, à partir du 16 août. Cette décision fait suite à de [[:c:Commons:Cross-wiki media upload tool/History|nombreux problèmes]] liés aux fichiers téléversés par de nouveaux arrivants. Les utilisateurs concernés verront un message d’erreur contenant un lien vers l’Assistant de téléversement, moins restrictif, sur Commons. Merci d’aider à traduire le [[:c:Special:MyLanguage/MediaWiki:Abusefilter-disallowed-cross-wiki-upload|message]] ou de donner votre avis sur son contenu. Pensez également à mettre à jour vos pages d’aide locales pour expliquer cette restriction. [https://phabricator.wikimedia.org/T370598]
* Sur les wikis où les comptes temporaires sont activés, ainsi que sur Meta-Wiki, les administrateurs peuvent désormais configurer un pied de page pour les pages « Spécial:Contributions » des comptes temporaires, similaire à ceux pouvant être affichés sur les pages d’IP et de comptes utilisateur. Ils peuvent le faire en créant la page nommée <code dir=ltr>MediaWiki:Sp-contributions-footer-temp</code>. [https://phabricator.wikimedia.org/T398347]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:21|la tâche soumise|les {{formatnum:21}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:21||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.12|MediaWiki]]
'''Rencontres et évènements'''
* [[wmania:Special:MyLanguage/2025:Wikimania|Wikimania 2025]] se déroulera du 6 au 9 août. Le [https://wikimedia.eventyay.com/talk/wikimania2025/schedule/ programme est disponible] pour vous permettre de planifier les sessions auxquelles vous souhaitez assister. La plupart des sessions seront diffusées en direct, sauf celles affichant l’icône « pas de caméra ». Si vous participez en ligne pour regarder les diffusions en direct et utiliser les fonctionnalités interactives, veuillez [[wmania:Special:MyLanguage/2025:Registration|vous inscrire]] pour obtenir un billet virtuel gratuit. Par exemple, vous pourriez être intéressé par des sessions techniques telles que :
** [https://wikimedia.eventyay.com/talk/wikimania2025/talk/KFEFVG/ Comptes temporaires : améliorer la confidentialité de nos contributeurs non enregistrés]
** [https://wikimedia.eventyay.com/talk/wikimania2025/talk/TVCVAB/ Construire un avenir durable pour les contributeurs de Wikimedia]
** [https://wikimedia.eventyay.com/talk/wikimania2025/talk/WTRQCJ/ Une douzaine de visions pour le wikitexte !]
** [https://wikimedia.eventyay.com/talk/wikimania2025/talk/8YKKP9/ Coordonner les parties prenantes avec le Conseil consultatif Produit et Technologie]
* La [[mw:Special:MyLanguage/MediaWiki Users and Developers Conference Fall 2025|conférence des utilisateurs et développeurs de MediaWiki, automne 2025]] se tiendra du 28 au 30 octobre 2025 à Hanovre, en Allemagne. Cet événement est organisé par et pour la communauté MediaWiki. Vous pouvez proposer des sessions et vous inscrire pour y participer.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/31|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W31"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 29 juillet 2025 à 02:26 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29051727 -->
== Actualités techniques n° 2025-32 ==
<section begin="technews-2025-W32"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/32|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les contributeurs peuvent désormais activer les [[mw:Special:MyLanguage/Product Safety and Integrity/Anti-abuse signals/User Info|cartouches d’infos utilisateur]]. Cette fonctionnalité ajoute une icône à côté des noms utilisateur sur les pages d’historique et autres pages listant des contributions. Lorsque vous appuyez ou cliquez sur l’icône, elle affiche les données concernant ce compte utilisateur telles que le nombre de modifications, de modifications annulées, de blocages, etc. Cela s’inscrit dans un projet plus large visant à faciliter, pour la modération, l’évaluation du sérieux des comptes. La fonctionnalité peut déjà être activée dans [[testwiki:Special:GlobalPreferences#mw-prefsection-rendering|vos préférences globales]] et pourra cette semaine être activée dans vos préférences locales. [https://phabricator.wikimedia.org/T386439]
* Tout le monde est invité à faire part de ses commentaires sur les [[m:Special:MyLanguage/CampaignEvents/Collaborative contributions|Contributions collaboratives]], un projet récemment lancé par l’[[m:Special:MyLanguage/Connection Team|équipe Connexion]]. Le projet vise à créer de nouvelles façons d’afficher l’impact des activités de contribution collaborative (telles que les contribuathons, les listes de travaux à faire et les wikiprojets) sur les wikis. Écrivez votre avis sur [[m:Talk:CampaignEvents/Collaborative contributions|la page de discussion du projet]]. [https://phabricator.wikimedia.org/T378035]
* Les admins peuvent désormais définir la durée de blocage par défaut pour les comptes temporaires. Pour cela, il faut créer une page intitulée <code dir=ltr>MediaWiki:Ipb-default-expiry-temporary-account</code> et utiliser une valeur définie dans <code dir=ltr>MediaWiki:Ipboptions</code>. Cela permet de bloquer facilement des comptes temporaires pour 90 jours, ce qui équivaut à un blocage indéfini. L’avantage de cette solution est que cela n’encombre pas Spécial:Liste_des_blocages. [[mw:Special:MyLanguage/Manual:Block and unblock#Default block duration options|Une documentation]] est disponible. [https://phabricator.wikimedia.org/T398626]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les gadgets peuvent désormais inclure des fichiers <code dir=ltr>.vue</code>. Cela facilite le développement d’interfaces utilisateur modernes avec [[mw:Vue.js|Vue.js]] et notamment [[mw:Special:MyLanguage/Codex|Codex]], le système de mise en forme officiel de Wikimedia. Les [[wmdoc:codex/latest/icons/overview.html|icônes Codex]] peuvent être chargées depuis la définition du gadget. [[mw:Special:MyLanguage/Extension:Gadgets#Pages|La documentation]] contient des exemples. Pour les scripts utilisateur qui utilisent Vue.js, un [[mw:API:CodexIcons|module de l’API]] existe désormais pour charger des icônes Codex. [https://phabricator.wikimedia.org/T340460][https://phabricator.wikimedia.org/T311099]
* Les personnes développant des modules peuvent désormais utiliser une [[mw:Help:Extension:Translate/Message Bundles/Lua reference|interface Lua]] pour simplifier la préparation des modules Lua pour la traduction sur Méta-Wiki. Cette amélioration rend plus facile pour les traducteurs et traductrices de trouver et de modifier des messages à traduire au sein des modules, sans avoir à s’occuper du code Lua. Cela évite les erreurs qui peuvent casser le module pendant la traduction. Les personnes développant ou traduisant des modules sont invitées à [[commons:File:Translatable modules video demo July 2025.webm|regarder la vidéo de démonstration]] pour comprendre comment cela fonctionne, à utiliser [[m:Module:User Wikimedia project|Module:User Wikimedia project]] de Méta-Wiki comme exemple et à [[mw:Talk:Translatable modules|expliquer si cela répond aux problématiques]] de leur processus de travail. L’interface rencontre encore des problèmes de performance, il faut éviter de l’utiliser sur des modules fortement utilisés pour le moment. [https://phabricator.wikimedia.org/T359918]
* Les personnes développant des outils externes qui se connectent aux pages Wikimedia doivent définir un agent utilisateur conforme au [[foundation:Special:MyLanguage/Policy:Wikimedia Foundation User-Agent Policy|règlement sur les agents utilisateur]]. Ce règlement sera appliqué plus strictement en aout en raison de robots externes qui [[diffblog:2025/04/01/how-crawlers-impact-the-operations-of-the-wikimedia-projects/|abusent]] des ressources de Wikimedia. Les outils qui sont hébergés sur Toolforge de Wikimedia ou sur Cloud VPS ne seront pas affectés pour le moment, mais devraient quand même définir un agent utilisateur. [[phab:T400119|D’autres détails techniques sont disponibles]] ; les questions afférentes sont les bienvenues sur la tâche.
* L’utilisation de Parsoid pour l’affichage des pages va être déployée sur certaines petites Wikipédia dans les prochaines semaines, à la suite du passage réussi des Wikivoyage et Wikitionnaires à Parsoid pour l’affichage des pages. Pour plus d’informations, consultez la page du projet [[mw:Special:MyLanguage/Parsoid/Parser Unification|Parsoid/Unification des analyseurs syntaxiques]]. [https://phabricator.wikimedia.org/project/profile/7694/]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.13|MediaWiki]]
'''Rencontres et évènements'''
* [[wmania:Special:MyLanguage/2025:Wikimania|Wikimania 2025]] aura lieu du 6 au 9 aout. [https://wikimedia.eventyay.com/talk/wikimania2025/schedule/ Le programme] est disponible, afin de vous permettre de choisir à quelles séances vous voulez participer. Les séances seront retransmises en direct, à l’exception de celles affichant l’icône « caméra barrée ». Si vous participerez en ligne, pour regarder la retransmission et utiliser les fonctionnalités interactives, [[wmania:Special:MyLanguage/2025:Registration|inscrivez-vous]] pour recevoir un ticket virtuel gratuit. Par exemple, vous serez peut-être intéressé{{GENDER:||e}} par des séances techniques telles que :
** [https://wikimedia.eventyay.com/talk/wikimania2025/talk/GEH9DH/ L’infrastructure de la connaissance de Wikimedia dans un Internet en changement : établir des chemins durables pour la réutilisation du contenu]
** [https://wikimedia.eventyay.com/talk/wikimania2025/talk/7ELN9Q/ Wikifunctions va bientôt arriver pour un wiki dont vous êtes proches !]
** [https://wikimedia.eventyay.com/talk/wikimania2025/talk/ZMGVJV/ Dessiner le futur de l’expérience de lecture de Wikipédia]
** [https://wikimedia.eventyay.com/talk/wikimania2025/talk/KCKTFZ/ Rendre Wikipédia plus lisible : quelle est la prochaine étape ?]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/32|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W32"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 5 août 2025 à 05:40 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29083927 -->
== Actualités techniques n° 2025-33 ==
<section begin="technews-2025-W33"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/33|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* La barre d'outils WikiEditor inclut désormais [[mw:Special:MyLanguage/Help:Extension:WikiEditor#Keyboard shortcuts|ses raccourcis clavier]] dans les infobulles de ses boutons. Cela contribuera à améliorer la découvrabilité de cette fonctionnalité. [https://phabricator.wikimedia.org/T400583]
* Le [[m:Special:MyLanguage/Product and Technology Advisory Council|Conseil consultatif sur les produits et les technologies]] a publié une série de [[m:Special:MyLanguage/Product and Technology Advisory Council/August 2025 draft PTAC proposals for feedback|propositions d'expériences]] que la Fondation Wikimédia pourrait mettre en œuvre pour améliorer la communication avec la communauté. Les commentaires sur ces propositions sont les bienvenus jusqu'au 22 août sur [[m:Talk:Product and Technology Advisory Council/August 2025 draft PTAC proposals for feedback|cette page de discussion]].
* La barre de recherche sur l’habillage Minerva (mobile) a été mise à jour pour utiliser le même composant de recherche assistée que celui de l’habillage Vector 2022. La fonctionnalité de recherche reste inchangée, mais des modifications visuelles mineures ont été apportées. En particulier, le bouton pour fermer la recherche a changé : ce n’est plus une croix « X », mais une flèche vers l’arrière. Cela permet de le distinguer de l’autre bouton « X » permettant d’effacer le texte. [https://phabricator.wikimedia.org/T393944]
* Les contributeurs à certains wikis verront une nouvelle option pour « Grouper les résultats par page » sur la liste de suivi, le suivi des pages liées à une page et les pages des modifications récentes. Il s'agit d’un [[mw:Special:MyLanguage/Moderator Tools/Watchlist/Experiment|test A/B]] qui débutera le 11 août et durera de 3 à 6 semaines sur les Wikipédia en bengali, chinois, tchèque, français, grec, portugais et ourdou. L’expérience étudiera comment le fait de rendre cette fonctionnalité plus détectable pourrait affecter la capacité des contributeurs à trouver les modifications qu’ils recherchent. [https://phabricator.wikimedia.org/T396789]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:31|la tâche soumise|les {{formatnum:31}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:31||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les jeux de données multiwiki de [[:wikt:en:Module:Unicode data|données Unicode]] ont été déplacés vers [[c:Category:Unicode Module Datasets|<i lang="en">Category:Unicode Module Datasets</i>]] (la catégorie des jeux de données Unicode des modules) sur Wikimedia Commons, conformément au principe « Une source de données commune, plusieurs wikis locaux ». La plupart des wikis ont été mis à jour pour utiliser la version Commons. Vous pouvez poser vos questions sur la [[c:Category talk:Unicode Module Datasets|page de discussion]]. [https://en.wiktionary.org/wiki/Module_talk:Unicode_data#Data_from_commons]
* Le code Lua peut ajouter des avertissements en cas d'anomalie grâce à la fonction <code dir=ltr>mw.addWarning()</code>. Il est désormais possible d'ajouter plusieurs avertissements, au lieu de remplacer les anciens par de nouveaux. Si vous maintenez un module Lua qui utilise des avertissements, vérifiez qu’il fonctionne toujours correctement. [https://phabricator.wikimedia.org/T398390]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.14|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/33|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W33"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 12 août 2025 à 01:29 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29106516 -->
== Actualités techniques n° 2025-34 ==
<section begin="technews-2025-W34"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/34|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Plus tard cette semaine, les personnes connectées et ayant activé la « [[mw:Special:MyLanguage/Talk pages project/Feature summary|Outils de discussion]] » [[Special:Preferences#mw-prefsection-betafeatures|Fonctionnalité bêta]] auront la possibilité de « Remercier » des commentaires individuels directement depuis les pages de discussion, plutôt que de devoir naviguer vers l'historique des pages. [[mw:Special:MyLanguage/Talk pages project/Feature summary#Comment actions|En savoir plus sur cette fonctionnalité]]. [https://phabricator.wikimedia.org/T400849]
* Un test A/B comparant deux versions du lien de don sur ordinateur, lancé sur testwiki le 12 août et sur Wikipédia en anglais le 14 août pour 0,1 % des utilisateurs déconnectés sur le site de bureau. L'expérience durera trois semaines et se terminera le 12 septembre. [https://phabricator.wikimedia.org/T395716]
* Un test A/A visant à mesurer la rétention des lecteurs a été lancé le 12 août à l'aide du [[wikitech:Experimentation Lab|Laboratoire d'expérimentation]]. Ce test mesure le pourcentage d'utilisateurs qui revisitent un wiki après leur première visite sur une période de 14 jours. Aucun changement visuel n'est attendu. L'expérience se déroulera jusqu'au 31 août. [https://phabricator.wikimedia.org/T399227]
* Cinq nouveaux wikis ont été créés :
** un {{int:project-localized-name-group-wikisource/fr}} en [[d:Q34057|tagalog]] ([[s:tl:|<code>s:tl:</code>]]) [https://phabricator.wikimedia.org/T388639]
** un {{int:project-localized-name-group-wikisource/fr}} en [[d:Q36213|Maduraise]] ([[s:mad:|<code>s:mad:</code>]]) [https://phabricator.wikimedia.org/T391747]
** un {{int:project-localized-name-group-wikipedia/fr}} en [[d:Q3450749|Rakhine]] ([[w:rki:|<code>w:rki:</code>]]) [https://phabricator.wikimedia.org/T392490]
** un {{int:project-localized-name-group-wikibooks/fr}} en [[d:Q13324|Minangkabau]] ([[b:min:|<code>b:min:</code>]]) [https://phabricator.wikimedia.org/T395452]
** un {{int:project-localized-name-group-wiktionary/fr}} en [[d:Q7598268|Amazigh marocain standard]] ([[wikt:zgh:|<code>wikt:zgh:</code>]]) [https://phabricator.wikimedia.org/T399684]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:46|la tâche soumise|les {{formatnum:46}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:46||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.15|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/34|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W34"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 19 août 2025 à 02:37 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29127690 -->
== Actualités techniques n° 2025-35 ==
<section begin="technews-2025-W35"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/35|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Les auteurs de modèles peuvent désormais utiliser davantage de propriétés CSS, depuis que le mécanisme de filtrage CSS utilisé par [[mw:Special:MyLanguage/Help:TemplateStyles|TemplateStyles]] a été mis à jour. Par exemple : <code>width: fit-content</code> ; <code>ruby-align</code> ; des unités relatives comme <code>lh</code> ; et des chaînes de caractères personnalisées dans <code>list-style-type</code>. Ces améliorations répondent à un [[m:Special:MyLanguage/Community Wishlist/Wishes/Allow use of modern CSS in templates by updating the TemplateStyles CSS sanitizer|souhait exprimé dans la liste des souhaits de la communauté]]. [https://phabricator.wikimedia.org/T271958][https://phabricator.wikimedia.org/T277755][https://phabricator.wikimedia.org/T293633][https://phabricator.wikimedia.org/T295088][https://phabricator.wikimedia.org/T326906][https://phabricator.wikimedia.org/T340057][https://phabricator.wikimedia.org/T360725][https://phabricator.wikimedia.org/T371809][https://phabricator.wikimedia.org/T375344][https://phabricator.wikimedia.org/T394619]
* Sur les grands wikis, la période par défaut pour l’affichage des modifications dans la page « Special:RecentChanges » est passée de 7 jours à 1 jour. Cela fait partie d’un projet d’amélioration des performances. Ce changement ne devrait avoir aucun impact visible pour les utilisateurs, en raison du volume élevé de modifications sur ces wikis. [https://phabricator.wikimedia.org/T399455]
* Les administrateurs peuvent désormais accéder à la page [[{{#special:BlockedExternalDomains}}]] depuis la page de liste [[{{#special:CommunityConfiguration}}]]. Cela permet de la trouver plus facilement. [https://phabricator.wikimedia.org/T393240]
* Les vidéos de Wikimedia Commons n’apparaissaient pas dans l’onglet « Vidéos » de la recherche Google. Le problème a été analysé et signalé à Google, qui l’a désormais corrigé. [https://phabricator.wikimedia.org/T396168][https://meta.wikimedia.org/wiki/Community_Wishlist/Wishes/Do_something_about_Google_%26_DuckDuckGo_search_not_indexing_media_files_and_categories_on_Commons]
* Un nouveau wiki a été créé : Un {{int:project-localized-name-group-wiktionary/fr}} en [[d:Q33014|betawi]] ([[wikt:bew:|<code>wikt:bew:</code>]]) [https://phabricator.wikimedia.org/T402130]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:39|la tâche soumise|les {{formatnum:39}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:39||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Deux champs de la [[mw:Special:MyLanguage/Manual:Recentchanges table|table de base de données « recentchanges »]] vont être supprimés. <code>rc_new</code> et <code>rc_type</code> sont retirés au profit de <code>rc_source</code>. Les requêtes utilisant ces anciens champs commenceront à échouer dès cette semaine, et les développeurs doivent utiliser <code>rc_source</code> à la place. Ces champs obsolètes avaient été dépréciés il y a plus de 10 ans et ne devraient plus être utilisés. Cette modification s’inscrit dans le cadre d’un travail visant à améliorer les performances et la stabilité des requêtes sur la table « recentchanges ». [https://phabricator.wikimedia.org/T400696]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.16|MediaWiki]]
'''En détails'''
* Le dernier numéro trimestriel de la [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/July|Lettre d’information sur les langues et l’internationalisation]] est désormais disponible. Cette édition inclut : la prise en charge de nouvelles langues dans MediaWiki et translatewiki ; le lancement du projet « Language Onboarding and Development » visant à soutenir la croissance des wikis nouveaux et de petite taille ; des mises à jour sur des projets de recherche ; et bien plus encore.
'''Rencontres et évènements'''
* La prochaine [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Community meetings#29 August 2025|réunion de la communauté des langues]] aura lieu bientôt, le 29 août à [https://zonestamp.toolforge.org/1756479600 15 h UTC]. La réunion de cette semaine portera notamment sur les développeurs du clavier Avro de Wikimedia Bangladesh, qui ont récemment reçu une récompense nationale pour leur contribution à ce clavier, ainsi que sur d’autres sujets.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/35|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W35"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 26 août 2025 à 02:12 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29175124 -->
== Bientôt, les comptes temporaires seront déployés ==
<section begin="body"/>
Bonjour, nous sommes l'équipe [[mw:Special:MyLanguage/Product Safety and Integrity|Sécurité et intégrité des produits]] de la Fondation Wikimedia. Nous souhaitons annoncer que '''nous prévoyons d'activer les [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] pour ce wiki dès la semaine du 1er septembre.'''
Les comptes temporaires sont en ligne sur 30 wikis, dont de nombreux wikis de tailles importantes comme les wikis allemand, japonais et français. Ce changement est particulièrement pertinent pour les contributeurs non connectés, que cette fonctionnalité vise à protéger. Mais il est également pertinent pour les membres de la communauté comme les mentors, les patrouilleurs et les administrateurs – toute personne qui annule les modifications, bloque les utilisateurs ou interagit de toute autre manière avec les contributeurs non connectés afin de garantir la sécurité et l'exactitude des wikis.
'''Pourquoi nous développons les comptes temporaires'''
Nos wikis devraient être plus sûrs à modifier par défaut pour les contributeurs non connectés. Les comptes temporaires permettent aux gens de continuer à modifier les wikis sans créer de compte, tout en évitant d'associer publiquement leurs modifications à leur adresse IP. Nous pensons que c'est dans l'intérêt de nos contributeurs non connectés, qui apportent des contributions précieuses aux wikis et qui peuvent plus tard créer des comptes et enrichir notre communauté de contributeurs, d'administrateurs et d'autres rôles. Même si les wikis avertissent les contributeurs non connectés que leur adresse IP sera associée à leur modification, beaucoup de personnes peuvent ne pas comprendre ce qu'est une adresse IP, ou qu'elle pourrait être utilisée pour les connecter à d'autres informations les concernant de manières qu'ils n'attendent peut-être pas.
De plus, nos logiciels et outils de modération s'appuient trop lourdement sur l'origine réseau (adresses IP) pour identifier les utilisateurs et les modèles d'activité, surtout alors que les adresses IP elles-mêmes deviennent des identifiants moins stables. Les comptes temporaires permettent des interactions plus précises avec les contributeurs non connectés, y compris des blocages plus précis, et peuvent aider à limiter la fréquence à laquelle nous finissons par bloquer involontairement des utilisateurs de bonne foi qui utilisent les mêmes adresses IP que des utilisateurs de mauvaise foi.
'''Comment fonctionnent les comptes temporaires'''
[[File:Temporary account banner and empty talk page.png|thumb]]
Chaque fois qu'un utilisateur non connecté publie une modification sur ce wiki, un cookie sera défini dans le navigateur de cet utilisateur, et un compte temporaire lié à ce cookie sera automatiquement créé. Le nom de ce compte suivra le modèle : <code dir=ltr>~2025-12345-67</code> (un tilde, l'année actuelle, un numéro) Sur des pages comme les Modifications récentes ou l'historique des pages, ce nom sera affiché. Le cookie expirera 90 jours après sa création. Tant qu'il existe, toutes les modifications effectuées depuis cet appareil seront attribuées à ce compte temporaire. Ce sera le même compte même si l'adresse IP change, sauf si l'utilisateur efface ses cookies ou utilise un appareil ou un navigateur web différent. Un enregistrement de l'adresse IP utilisée au moment de chaque modification sera stocké pendant 90 jours après la modification. Cependant, seuls certains utilisateurs connectés pourront la voir.
'''Qu'est-ce que cela signifie pour différents groupes d'utilisateurs ?'''
''' Pour les contributeurs non connectés'''
* Cela augmente la confidentialité : actuellement, si vous n'utilisez pas un compte enregistré pour modifier, alors tout le monde peut voir l'adresse IP des modifications que vous avez faites, même après 90 jours. Cela ne sera plus possible sur ce wiki.
* Si vous utilisez un compte temporaire pour modifier depuis différents endroits dans les 90 derniers jours (par exemple chez vous et dans un café), l'historique des modifications et les adresses IP de tous ces endroits seront maintenant enregistrés ensemble, pour le même compte temporaire. Les utilisateurs qui [[foundation:Special:MyLanguage/Policy:Access_to_temporary_account_IP_addresses| satisfont les conditions requises]] pourront voir ces données. Si cela crée des préoccupations de sécurité personnelle pour vous, veuillez contacter talktohumanrights at wikimedia.org pour des conseils.
'''Pour les membres de la communauté interagissant avec les contributeurs non connectés'''
* Un compte temporaire est uniquement lié à un appareil. En comparaison, une adresse IP peut être partagée avec différents appareils et personnes (par exemple, différentes personnes à l'école ou au travail peuvent avoir la même adresse IP).
* Par rapport à la situation actuelle, il sera plus sûr de supposer que la page de discussion d'un utilisateur temporaire appartient à une seule personne, et que les messages laissés là seront lus par elle. Comme vous pouvez le voir dans la capture d'écran, les utilisateurs de comptes temporaires recevront des notifications. Il sera aussi possible de les remercier pour leurs modifications, de les mentionner dans les discussions, et de les inviter à s'impliquer davantage dans la communauté.
'''Pour les utilisateurs qui utilisent les données d'adresse IP pour modérer et maintenir le wiki'''
'''Pour les patrouilleurs''' qui traquent les abuseurs persistants, enquêtent sur les violations de politiques, etc. : Les utilisateurs qui [[foundation:Special:MyLanguage/Policy:Access_to_temporary_account_IP_addresses|satisfont les conditions]] pourront révéler les adresses IP des utilisateurs temporaires et toutes les contributions faites par les comptes temporaires depuis une adresse IP ou une plage spécifique ([[Special:IPContributions]]). Ils auront aussi accès à des informations utiles sur les adresses IP grâce à la fonctionnalité [[mw:Special:MyLanguage/Trust and Safety Product/IP Info|IP Info]]. Beaucoup d'autres logiciels ont été construits ou ajustés pour fonctionner avec les comptes temporaires, incluant AbuseFilter, les blocages globaux, les contributions globales des utilisateurs, et plus encore. (Pour des informations destinées aux développeurs bénévoles sur comment mettre à jour le code de leurs outils – voir la dernière partie du message.)
* '''Pour les administrateurs bloquant les contributeurs non connectés''':
** Il sera possible de bloquer de nombreux agresseurs en bloquant simplement leurs comptes temporaires. Une personne bloquée ne pourra pas créer rapidement de nouveaux comptes temporaires si l'administrateur sélectionne l'option de [[mw:Special:MyLanguage/Autoblock|blocage automatique]].
** Il sera toujours possible de bloquer une adresse IP ou une plage d'IP.
* Les comptes temporaires ne seront pas appliqués rétroactivement aux contributions faites avant le déploiement. Sur Special:Contributions, vous pourrez voir les contributions d'utilisateurs IP existantes, mais pas les nouvelles contributions faites par les comptes temporaires sur cette adresse IP. Vous devriez plutôt utiliser Special:IPContributions pour cela.
'''Notre demande et les prochaines étapes'''
* Si vous connaissez des outils, bots, gadgets, etc. utilisant des données sur les adresses IP ou étant disponibles pour les utilisateurs non connectés, vous voudrez peut-être tester s'ils fonctionnent sur [[testwiki:Main_Page|testwiki]] ou [[test2wiki:Main_Page|test2wiki]]. Si vous êtes un développeur bénévole, lisez notre [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/For developers|documentation pour les développeurs]], et en particulier, la section sur [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/For developers#How should I update my code?|comment votre code pourrait avoir besoin d'être mis à jour]].
* Si vous voulez tester l'expérience des comptes temporaires, par exemple juste pour vérifier ce que cela fait, allez sur testwiki ou test2wiki et modifiez sans vous connecter.
* Dites-nous si vous pensez à des difficultés qui doivent être mentionnées. Nous essaierons d'aider, et si nous ne le pouvons pas, nous considérerons les options disponibles.
* Regardez notre [[m:Meta:Babel#Temporary_Accounts:_access_to_IP_addresses_and_next_steps| message précédent]] concernant les exigences pour les utilisateurs sans droits étendus qui peuvent avoir besoin d'accès aux adresses IP.
Pour en savoir plus sur le projet, consultez [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/FAQ|notre foire aux questions]] – vous y trouverez beaucoup de réponses utiles. Vous pouvez aussi regarder les [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Updates|mises à jour]] (nous venons d'en publier une) et vous abonner à notre [[mw:Newsletter:Product Safety and Integrity|nouvelle infolettre]]. Si vous souhaitez me parler (Szymon) hors wiki, vous me trouverez sur Discord et Telegram. Merci!<section end="body" />
<bdi lang="en" dir="ltr">[[m:user:NKohli (WMF)|NKohli (WMF)]], [[m:user:SGrabarczuk (WMF)|SGrabarczuk (WMF)]]</bdi> 26 août 2025 à 23:35 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Quiddity_(WMF)/sandbox6&oldid=29181713 -->
== Actualités techniques n° 2025-36 ==
<section begin="technews-2025-W36"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/36|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* L'équipe de rédaction souhaite compiler une liste de modèles, de termes techniques et de politiques utilisés dans les résumés de modification lors de la suppression d'une violation de droits d'auteur. Cela les aidera à identifier le nombre de modifications annulées en raison de problèmes de droits d'auteur. Nous invitons les membres de la communauté des wikis suivants à lister ces termes dans [[Phab:T402601|T402601]], ou à partager leur liste avec [[User:Trizek (WMF)|Trizek_(WMF)]] : {{int:project-localized-name-arwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-cswiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-dewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-enwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-eswiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-fawiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-frwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-hewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-idwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-itwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-jawiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-kowiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-nlwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-plwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-ptwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-trwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-ukwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-viwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-zhwiki/fr}}. Ce projet est ouvert jusqu'au 9 septembre 2025.
'''Actualités pour la contribution'''
* L'[[mw:Special:MyLanguage/Help:Extension:CampaignEvents|extension CampaignEvents]] a été activée pour toutes les Wikisources. L'extension facilite l'organisation et la participation à des activités collaboratives, comme les edit-a-thons et les WikiProjects, sur les wikis. L'extension dispose de trois fonctionnalités : [[m:Special:MyLanguage/Event Center/Registration|Inscription à l'événement]], [[m:Special:MyLanguage/CampaignEvents/Collaboration list|Liste de Collaboration]], et [[m:Special:MyLanguage/Connection Team/Invitation list|Liste d'invitation]]. Pour demander l'extension pour votre wiki, visitez la page d'information sur le déploiement. [https://meta.wikimedia.org/wiki/CampaignEvents/Deployment_status#How_to_Request_the_CampaignEvents_Extension_for_your_wiki]
* Les listes dans le pied de page de l'interface d'édition, telles que des « Modèles utilisés sur cette page », seront désormais organisées en colonnes lorsqu'il y a suffisamment d'espace. Cette amélioration minimise le défilement lors de la modification d'articles longs sur Wikipédia. [https://phabricator.wikimedia.org/T401066]
* Le 3 septembre 2025, nous augmenterons les pourcentages d'échantillonnage de notre [[mw:Special:MyLanguage/Moderator Tools/Watchlist/Experiment#Scope of the experiment|expérience de groupe par bascule]] des pages <code>Special:RecentChanges</code>, <code>Special:Watchlist</code> et <code>Special:RelatedChanges</code> sur les Wikipédias chinoise, française et portugaise à 100 %, permettant à davantage d'éditeurs de participer à cette expérience. Cet ajustement vise à garantir que nous disposons de données suffisantes pour prendre des décisions éclairées lors de l'évaluation des résultats de l'expérience. [https://phabricator.wikimedia.org/T402958][https://phabricator.wikimedia.org/T396789]
* En cliquant sur une barre de recherche vide, les utilisateurs déconnectés verront des suggestions d'articles à lire sur Wikipedia en anglais à partir de la semaine du 22 septembre. La fonctionnalité sera disponible sur ordinateur et mobile. Tous les wikis non anglophones ont reçu ce changement en juin et juillet. L'objectif est de faciliter la recherche d'articles pour les utilisateurs. [[mw:Special:MyLanguage/Reading/Web/Content Discovery Experiments/Search Suggestions|En savoir plus]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:37|la tâche soumise|les {{formatnum:37}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:37||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.17|MediaWiki]]
'''En détails'''
* Wikifunctions a désormais une nouvelle capacité appelée « types d'énumération légers », un type d'énumération est simplement un ensemble fixe de valeurs qui se trouve dans la définition du type. Cette capacité permet de définir rapidement et facilement un tel type, et permet la réutilisation de valeurs déjà présentes dans Wikidata. Voici [[f:Special:MyLanguage/Wikifunctions:Status updates/2025-07-19|une infolettre]] pour en savoir plus.
* Le dernier [[mw:Special:MyLanguage/Readers/Newsletter updates#August 2025: Newsletter #1|Bulletin des lecteurs]] est désormais disponible. Cette édition comprend : la création de deux nouvelles équipes — Croissance des Lecteurs et Expérience des Lecteurs ; des aperçus sur la baisse des pages vues et des créations de comptes ; des points forts du panel de Wikimania Nairobi sur l'amélioration de l'expérience de lecture ; des expériences à venir pour engager de nouveaux lecteurs et les lecteurs existants ; et plus encore.
'''Rencontres et évènements'''
* Résumé de certaines sessions de Wikimania 2025 :
** Identifier le texte généré par l'IA en recherchant des ISBN dont les sommes de contrôle échouent : Mathias Schindler de WMDE [https://www.youtube.com/watch?v=Dw9o8Lsl974&t=15910s a partagé des outils pour aider les communautés à les rechercher].
** [https://wikimedia.eventyay.com/talk/wikimania2025/talk/TCHZKH/ La durabilité du mouvement Wikimedia face aux défis actuels et futurs] : Cette session a exploré comment Wikimedia peut rester une source de connaissances fiable à l'ère de l'IA générative, de la surcharge d'information et de la désinformation.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/36|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W36"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 1 septembre 2025 à 22:50 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29196010 -->
== Actualités techniques n° 2025-37 ==
<section begin="technews-2025-W37"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/37|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* L'équipe Édition travaille sur une nouvelle vérification : la [[mw:Special:MyLanguage/Paste check|vérification de copier-coller]]. Cette vérification informe les nouveaux venus qui copient du texte dans Wikipédia que le contenu est susceptible d'être refusé. Elle vise à s'assurer que le contenu ajouter à Wikipédia est en accord avec l'engagement du mouvement pour offrir une information sous licence libre. Cette vérification sera prochainement à l'essai sur quelques wikis. Si votre communauté est intéressée par cet essai, merci de nous en informer dans [[phab:T403680|cette tâche]] ou de [[mw:Talk:Edit check|contacter l'équipe]].
'''Actualités pour la contribution'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Plus tard cette semaine, les utilisateurs et utilisatrices de la [[Special:Preferences#mw-prefsection-betafeatures|fonctionnalité beta]] « {{int:codemirror-beta-feature-title}} » recevront des [[w:fr:Lint (logiciel)|outils de lint]] permettant de détecter les erreurs et autres problèmes potentiels dans le wikicode en temps réel. Voir la [[mw:Special:MyLanguage/Help:Extension:CodeMirror#Linting|page d'aide]] pour plus d'informations. [https://phabricator.wikimedia.org/T381577]
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] En naviguant sur un wiki (par exemple, <code dir=ltr>en.wikipedia.org</code>), le logiciel répond de deux manières : avec une page bureau ou une redirection vers une version mobile au nom de domaine en « m » (comme <code dir=ltr>en.m.wikipedia.org</code>). Durant les trois prochaines semaines, MediaWiki commencera à afficher la version mobile aux appareils mobiles directement sur le domaine standard, sans la redirection. Ce changement n'affecte pas les URLs en m. existantes, ni les personnes ayant choisi d'utiliser la « version de bureau » sur mobile. [[mw:Requests for comment/Mobile domain sunsetting/2025 Announcement|En savoir plus]]. [https://phabricator.wikimedia.org/T214998]
* Le nombre d'éléments d'une catégorie est désormais mis à jour de manière asynchrone lorsque la modification d'une page entraîne un changement de ses catégories. Cela améliore la vitesse d'enregistrement des modifications, en particulier lorsque vous déplacez de nombreuses pages vers ou depuis une même catégorie, et réduit le risque de panne de site, mais cela signifie que le nombre d'éléments affiché d'une catégorie peut être incorrect pendant quelques minutes. [https://phabricator.wikimedia.org/T365303]
* Sur Wikidata, les modifications aux qualificatifs (propriétés et valeurs) et aux références dans une déclaration d'élément Wikidata n'ajouteront temporairement plus d'entrées aux pages « Modifications récentes » et « Liste de suivi » sur tous les autres wikis. Il s'agit d'un changement temporaire afin d'améliorer les performances en attendant que d'autres solutions soient créées. Les pages dans Wikidata resteront inchangées. [[m:Wikidata For Wikimedia Projects/Reduce change propagation noise#Phase 1: Turn off (temporarily) Qualifiers and References Wikidata edits to the Recent Changes tables|En savoir plus]]. [https://phabricator.wikimedia.org/T401286][https://phabricator.wikimedia.org/T400698]
* Les wikis en langue japonaise ont reçu une amélioration majeure dans la manière dont la recherche fonctionne. La nouvelle recherche devrait généralement donner des résultats de recherche plus pertinents et plus précis. [https://phabricator.wikimedia.org/T318269]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:31|la tâche soumise|les {{formatnum:31}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:31||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.18|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/37|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W37"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 9 septembre 2025 à 03:14 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29238161 -->
== Actualités techniques n° 2025-38 ==
<section begin="technews-2025-W38"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/38|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les listes de références qui sont faites en utilisant le [[mw:Special:MyLanguage/Help:Cite#references-tag|tag]] <code dir=ltr><nowiki><references/></nowiki></code> seront maintenant affichées automatiquement avec colonnes en Vector 2022 si les lecteurs utilisent les réglages par défaut pour la taille du texte et la largeur de la page. [https://phabricator.wikimedia.org/T334941]
* À partir de la semaine du 6 octobre, sur les [[gitiles:operations/mediawiki-config/+/a2d2aaab9ace84280dd2f4c70a33bb69cd73850f/dblists/small.dblist|petits wikis]] et [[gitiles:operations/mediawiki-config/+/a2d2aaab9ace84280dd2f4c70a33bb69cd73850f/dblists/medium.dblist|wikis de taille moyenne]] qui ont [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|l'extension CampaignEvents]] activée, tous les utilisateurs autoconfirmés pourront utiliser [[m:Special:MyLanguage/Event Center/Registration|l'inscription aux événements]] en tant qu'organisateur. Aucun changement ne sera fait pour les [[gitiles:operations/mediawiki-config/+/a2d2aaab9ace84280dd2f4c70a33bb69cd73850f/dblists/large.dblist|grands wikis]] sauf demande sur Phabricator. Ce changement est fait pour rendre l'utilisation de l'inscription aux événements plus facile pour plus de personnes, surtout sur des wikis qui n'ont probablement pas de règles en rapport au droit d'organisateur d'évènements. [[m:Special:MyLanguage/CampaignEvents/Proposal to grant autoconfirmed users on small and medium wikis the organizer access to the event registration tool|Plus d'informations]].
* Les utilisateurs qui recherchent en utilisant des expressions rationnelles (''regex'') peuvent désormais utiliser des fonctionnalités supplémentaires, notamment :
** pour le mot-clé <code dir=ltr>intitle:</code> : des [[mw:Special:MyLanguage/Help:CirrusSearch#Metacharacters|métacaractères]] pour le début de ligne (<code dir=ltr>^</code>) et la fin de ligne (<code dir=ltr>$</code>) [https://phabricator.wikimedia.org/T317599]
** pour les mots-clés <code dir=ltr>intitle:</code> et <code dir=ltr>insource:</code> : des raccourcis pour les [[mw:Special:MyLanguage/Help:CirrusSearch#Character_Classes|classes de caractères]] des numéros (<code dir=ltr>\d</code>), des caractères vides (<code dir=ltr>\s</code>) ou de mots (<code dir=ltr>\w</code>) et des [[mw:Special:MyLanguage/Help:CirrusSearch#Escape codes|séquence d'échappement]] pour le retour chariot (<code dir=ltr>\r</code>), nouvelle ligne (<code dir=ltr>\n</code>), tabulation (<code dir=ltr>\t</code>) et les caractères Unicode (<code dir=ltr>\uHHHH</code>). [https://phabricator.wikimedia.org/T403212]
* Lorsque vous recherchez un texte qui ressemble à une adresse IP, le système affiche maintenant les résultats de recherche. Par le passé, vous étiez automatiquement redirigé vers les contributions de cette adresse IP. [https://phabricator.wikimedia.org/T306325]
* [[m:Special:MyLanguage/Tech/Server switch|Tous les wikis seront mis en lecture seule]] pendant quelques minutes le 24 septembre, à [https://zonestamp.toolforge.org/1758726000 15:00 UTC]. Ceci est dû aux tests de changement de serveur de centre de données qui ont lieu deux fois par an. Vous pouvez [[diffblog:2025/03/12/hear-that-the-wikis-go-silent-twice-a-year/|en apprendre plus sur le pourquoi et les détails de ce processus sur le blog Diff]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:24|la tâche soumise|les {{formatnum:24}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:24||s}} la semaine dernière]]. Par exemple, un bug a été corrigé qui affectait les utilisateurs qui utilisent les onglets de page pour passer de la modification du wikitexte d'une section vers l'éditeur visuel. [https://phabricator.wikimedia.org/T401043]
'''Actualités pour la contribution technique'''
* L'équipe de l'interface Mediawiki est en train de remanier le bac à sable de l'API REST Wikimedia avec Codex. Si vous avez des retours sur des améliorations pour la documentation de l'API ou ce qui rend les expériences de développeurs faciles (ou difficiles), vous êtes invités à [https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ2aZzbXeQvjOF7gB1fJXiwAYemQjKf4sXNaRODPA7_obFyNBwkzNkoVCoTF-aeov89kIjXHbCQm rejoindre une future interview de découverte], ou [[mw:MediaWiki Interfaces Team/Developer Feedback/Wikimedia Web APIs|laisser ce retour sur le wiki]]. [[listarchive:list/wikitech-l@lists.wikimedia.org/thread/C4FBAOA57PH6G5ORVMAUF5TGYBLZDU5Q/|En apprendre plus]].
* Les modifications des alias de Wikidata (un nom alternatif pour un élément ou une propriété) seront désormais affichées moins souvent dans les entrées des changements récents et de la liste de suivi sur d'autres wikis, réduisant ainsi les notifications inutiles. Cela réduira la quantité globale d'entrées « bruyantes ». Les propres pages de Wikidata restent inchangées. [[m:Wikidata For Wikimedia Projects/Reduce change propagation noise#Phase 1: More granular Alias tracking|En savoir plus]]. [https://phabricator.wikimedia.org/T401288]
* La nouvelle version [https://www.unicode.org/versions/Unicode17.0.0/ Unicode 17.0] a été publiée. Les [[:c:Category:Unicode Module Datasets|ensembles de données sur Commons]] pour le [[:d:Q39301585|Module:Unicode data]] ont été mises à jour. Les Wikipédias qui n'utilisent pas les ensembles de données Commons devraient soit mettre à jour leurs propres données, soit passer aux ensembles de données Commons.
* Les utilisateurs des points de terminaison des Contenus Structurés de [[m:Special:MyLanguage/Wikimedia Enterprise|Wikimedia Enterprise]] peuvent désormais accéder aux [https://enterprise.wikimedia.com/blog/parsed-wikipedia-tables/ Tables analysées]. La nouvelle fonctionnalité des Tables analysées extrait et représente les tableaux Wikipédia en JSON structuré. Ceci améliore l'accessibilité machine dans le cadre de l'initiative [https://enterprise.wikimedia.com/api/structured-contents/ Contenus Structurés]. Les sorties des Contenus Structurés sont disponibles gratuitement via l'[https://enterprise.wikimedia.com/docs/on-demand/#article-structured-contents-beta API à la demande] ou par le biais des Services Cloud Wikimedia.
* Un [https://www.kaggle.com/datasets/wikimedia-foundation/english-wikipedia-people-dataset jeu de données d'informations biographiques de Wikipédia en anglais] provenant de [[m:Special:MyLanguage/Wikimedia Enterprise|Wikimedia Enterprise]] a été publié sur Kaggle, pour évaluation et recherche. Cela fournit des données structurées issus de plus de 1,5 million de biographies, y compris les dates de naissance et de décès, l'éducation, les affiliations, les carrières, les récompenses, et plus encore (à partir d'une capture instantanée de juin 2024).
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.19|MediaWiki]]
'''Rencontres et évènements'''
* Les [[wmania:Special:MyLanguage/2026:Scholarships|demandes de bourse]] pour Wikimania 2026 à Paris, France, sont ouvertes jusqu'au 31 octobre.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/38|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W38"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 15 septembre 2025 à 19:06 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29263921 -->
== Bascule de serveur : votre wiki sera bientôt en lecture seule durant un court instant ==
<section begin="server-switch"/><div class="plainlinks">
[[:m:Special:MyLanguage/Tech/Server switch|Lire ce message dans une autre langue]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-Tech%2FServer+switch&language=&action=page&filter= {{int:please-translate}}]
La [[foundation:|Fondation Wikimédia]] va basculer le trafic entre ses centres de données. Cela permettra de s’assurer que Wikipédia et les autres wikis de Wikimédia peuvent rester en ligne même après une catastrophe.
Le trafic sera basculé le '''{{#time:j xg|2025-09-24|fr}}'''. La bascule débutera à '''[https://zonestamp.toolforge.org/{{#time:U|2025-09-24T15:00|en}} {{#time:H:i e|2025-09-24T15:00}}]'''.
Malheureusement, en raison de certaines limites de [[mw:Special:MyLanguage/Manual:What is MediaWiki?|MediaWiki]], toutes les modifications de pages devront être arrêtées durant le passage d’un centre de données à l’autre. Nous nous excusons pour ce dérangement, que nous nous efforçons de réduire pour le futur.
Une bannière sera affichée sur tous les wikis 30 minutes avant le début de l’opération. Cette bannière restera visible jusqu’à la fin de l’opération.
Vous pouvez contribuer à la [https://meta.wikimedia.org/w/index.php?title=Special%3ATranslate&group=Centralnotice-tgroup-read_only_banner&task=view&language=&filter=&action=translate traduction ou relecture] du texte de cette bannière.
'''Pendant une courte période, vous pourrez lire les wikis mais pas les modifier.'''
*Vous ne pourrez pas effectuer de modification pendant une durée pouvant aller jusqu’à une heure, le {{#time:l j xg Y|2025-09-24|fr}}.
*Si vous essayez de faire une modification ou de sauvegarder pendant cette période, vous verrez un message d’erreur. Nous espérons qu’aucune modification ne sera perdue durant ce temps, mais nous ne pouvons le garantir. Si vous voyez un message d’erreur, merci de patienter jusqu’au retour à la normale. Vous pourrez alors enregistrer votre modification. Nous vous conseillons cependant de faire une copie de votre modification avant, au cas où.
''Autres conséquences :''
*Les tâches de fond seront ralenties et certaines pourraient être stoppées. Les liens rouges ne seront pas mis à jour aussi vite que d’habitude. Si vous créez un article qui est déjà lié depuis une autre page, le lien rouge pourrait rester rouge plus longtemps que d’habitude. Certains scripts ayant un long temps d’exécution devront être stoppés.
* Le déploiement de code devrait se dérouler comme chaque semaine. Cependant, certains codes particuliers pourraient être gelés si l’opération le nécessitait.
* [[mw:Special:MyLanguage/GitLab|GitLab]] sera indisponible durant environ 90 minutes.
Ce projet pourra être reporté si nécessaire. Vous pouvez [[wikitech:Switch_Datacenter|consulter le calendrier sur wikitech.wikimedia.org]]. Tout changement sera annoncé dans le calendrier.
'''Merci de partager ces informations avec votre communauté.'''</div><section end="server-switch"/>
<span dir=ltr>[[m:User:Trizek (WMF)|Trizek (WMF)]] ([[m:User talk:Trizek (WMF)|{{int:talk}}]])</span> 18 septembre 2025 à 17:40 (CEST)
<!-- Message envoyé par User:Trizek (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=29170715 -->
== Actualités techniques n° 2025-39 ==
<section begin="technews-2025-W39"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/39|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* [https://zonestamp.toolforge.org/1758726000 En date du 24 septembre à 15h00 UTC], tous les utilisateurs des sites Wikimedia seront brièvement en lecture seule en raison d'un [[m:Special:MyLanguage/Tech/Server switch|basculement du serveur du centre de données]]. L'équipe d'ingénierie de la fiabilité des sites (SRE) de la Fondation Wikimedia redirigera tout le trafic d'un serveur principal vers son serveur de secours. Vous pouvez écouter le basculement grâce à l'outil [http://listen.hatnote.com/ « Écouter Wikipédia »], où vous entendrez les modifications s'arrêter quelques minutes pendant la phase de lecture seule, puis reprendre. Ce basculement biannuel du serveur du centre de données garantit la fiabilité en testant le centre de données de secours, afin que nos sites puissent rester en ligne même en cas de panne du centre de données principal. Vous pouvez [[diffblog:2025/03/12/hear-that-the-wikis-go-silent-twice-a-year/|en savoir plus sur le processus sur le blog Diff]].
'''Actualités pour la contribution'''
* Les éditeurs de [[f:Special:Mylanguage/Wikifunctions:Status updates/2025-09-12#Next round of Wiktionaries to receive embedded Wikifunctions calls|60 autres Wiktionnaires]] pourront bientôt appeler des [[f:Special:MyLanguage/Wikifunctions:Introduction|fonctions de Wikifunctions]] et les intégrer à leurs pages. Une fonction prend une ou plusieurs entrées et les transforme en une sortie souhaitée, comme additionner des nombres, convertir des milles en mètres, calculer le temps écoulé ou décliner un mot en cas. Elles rejoindront les [[f:Special:MyLanguage/Wikifunctions:Status updates/2025-08-29#Wikifunctions available on 65 Wiktionaries|65 autres éditions linguistiques du Wiktionnaire]], qui ont déjà accès aux appels Wikifunctions intégrés. Plus tard cette année, il est prévu d'étendre cette fonctionnalité à d'autres Wiktionnaires et à l'Incubateur.
* Une nouvelle [[mw:Special:MyLanguage/Help:Magic words#Technical metadata of another page|fonction d'analyse]] a été ajoutée : <code><nowiki>{{#contentmodel}}</nowiki></code>. Les éditeurs de modèles et les administrateurs peuvent l'utiliser pour obtenir le nom localisé ou canonique du [[mw:Special:MyLanguage/Help:ChangeContentModel|modèle de contenu]] d'une page spécifique. Cette fonction facilite la création et la modification des messages système, comme ''MediaWiki:editinginterface'', même lorsque vous changez de type de page, comme wiki, JavaScript, CSS ou JSON. [https://phabricator.wikimedia.org/T328254]
* L'ajout ou la modification d'un <code>DISPLAYTITLE</code> pour un article avec l'Éditeur Visuel ne sera plus problématique. Les rédacteurs utilisant l'Éditeur Visuel pour modifier le <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> ne verront plus le texte littéral « DISPLAYTITLE » ni sa variante localisée ajoutés à leurs articles. La liste des pages potentiellement affectées et nécessitant un nettoyage est documentée dans [[phab:P83438|ce ticket]].
* Les utilisateurs beta de l'application Wikipédia pour Android peuvent désormais essayer l'[[mw:Special:MyLanguage/Wikimedia Apps/Team/Android/Activity Tab Experiment|onglet Activité]] restructuré, qui remplace l'onglet Modifications. Ce nouvel onglet offre des analyses personnalisées sur les activités de lecture, de modification et de don, tout en simplifiant la navigation et en rendant l'utilisation de l'application plus agréable.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:12|la tâche soumise|les {{formatnum:12}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:12||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.20|MediaWiki]]
'''En détails'''
* Les utilisateurs de Wikifunctions peuvent désormais importer de nombreuses données essentielles concernant les [[f:Special:MyLanguage/Z6011|géo-coordonnées]], les [[f:Special:MyLanguage/Z6010|quantités]] et les [[f:Special:MyLanguage/Z6064|temps]] depuis Wikidata. Ceci est rendu possible grâce à la création de types Wikifunctions pour ces valeurs, ce qui les rend utilisables par les fonctions de Wikifunctions. Pour en savoir plus, consultez [[c:File:ImportingWikidataDatatypesIntoWikifunctions.webm|cette vidéo]] et le [[f:Special:MyLanguage/Wikifunctions:Status updates/2025-08-01#News in Types I: Wikidata quantity|bulletin d'information du 1er août]] (pour les quantités) et le [[f:Special:MyLanguage/Wikifunctions:Status updates/2025-08-22#News in Types: Wikidata geo-coordinate|bulletin d'information du 22 août]] (pour les géo-coordonnées).
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/39|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W39"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 23 septembre 2025 à 00:55 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29305556 -->
== Actualités techniques n° 2025-40 ==
<section begin="technews-2025-W40"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/40|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Une mise à jour majeure du logiciel a été effectuée sur [[phab:|Phabricator]]. La mise à jour introduit des améliorations de performance, un rafraichissement de l’interface de recherche, des améliorations de la recherche de tâches Maniphest, des mises à jour des pages de profil utilisateur et des tableaux de projets, de nouvelles fonctionnalités d’automatisation Herald, ainsi que des améliorations générales de saisie de texte, de l'expérience mobile et bien plus encore. [https://phabricator.wikimedia.org/phame/post/view/321/iterative_improvements_september_2025/]
'''Actualités pour la contribution'''
* L’équipe Technologies communautaires lancera la nouvelle extension <i lang="en">Community Wishlist</i> (les « Souhaits de la communauté ») le 1<sup>er</sup> octobre, qui améliorera la façon dont les souhaits seront soumis. La nouvelle extension permettra aux utilisateurs d’ajouter des étiquettes à leurs souhaits afin de mieux les catégoriser, et (dans une prochaine version) de les filtrer par statut, étiquette et domaines d’intérêt. Il sera également à nouveau possible de soutenir des souhaits individuellement, comme demandé par la communauté à de nombreuses reprises. L’ancien système sera retiré. Il y aura une brève période d’indisponibilité pendant le déploiement de l’extension et la migration des souhaits vers le nouveau système. Vous pouvez en savoir plus à ce sujet [[:m:Special:MyLanguage/Community Wishlist/Updates|dans la dernière note d’actualités]] ou consulter la [[:mw:Special:MyLanguage/Help:Extension:CommunityRequests|documentation actuelle sur MediaWiki.org]].
* Comme annoncé [[diffblog:2025/09/02/better-detecting-bots-and-replacing-our-captcha/|sur le blog Diff]], l’essai en production du service [[mw:Special:MyLanguage/Product Safety and Integrity/Anti-abuse signals/hCaptcha|hCaptcha]] pour la détection des robots a commencé. L’essai utilise actuellement hCaptcha pour protéger la création de compte sur les Wikipédia en chinois, perse, portugais, indonésien, japonais et turc, où il remplacera notre [[mw:Special:MyLanguage/Extension:ConfirmEdit#FancyCaptcha|CAPTCHA]] existant (FancyCaptcha). L’objectif de cet essai est de mieux bloquer les robots tout en améliorant la convivialité et l’accessibilité pour les utilisateurs qui rencontrent des difficultés avec les CAPTCHA.
* L’extension [[mw:Special:MyLanguage/Extension:CampaignEvents|CampaignEvents]] a [[m:Special:MyLanguage/CampaignEvents/Deployment status|été déployée]] sur Wikimedia Commons. Cette extension facilite l’organisation et la participation à des activités collaboratives, comme des contribuathons et des projets, sur les wikis. Sur Commons, n’importe quelle personne enregistrée peut l’utiliser comme participante à un événement. Pour l’utiliser pour organiser un événement, il faut [[c:Special:MyLanguage/Commons:Event organizers|demander un droit spécifique]].
* Les [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing|sous-références]], une nouvelle fonctionnalité permettant de réutiliser des références avec des détails différents, a été lancée sur Wikipédia en allemand. Vous pouvez [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing#test|tester cette fonctionnalité]] sur Testwiki ou [https://en.wikipedia.beta.wmcloud.org/wiki/Sub-referencing sur Betawiki]. N’hésitez pas à partager vos remarques sur [[:m:Talk:WMDE Technical Wishes/Sub-referencing#Templates used in sub-references|l’utilisation des modèles dans les sous-références]] ou [[:m:Talk:WMDE Technical Wishes/Sub-referencing#Pilot wikis|à vous porter volontaire pour devenir un wiki pilote]].
* Sur les wikis utilisant le système de [[mw:Special:MyLanguage/Help:Growth/Mentorship|Mentorat]], les communautés peuvent à présent exclure les personnes expérimentées du mentorat en utilisant [[{{#special:CommunityConfiguration/Mentorship}}]]. Dans cette configuration, les communautés peuvent définir des niveaux, basés sur le compteur de contributions et l’âge du compte, pour décider si une personne est considérée comme suffisamment expérimentée pour ne plus recevoir de soutien par le mentorat. [https://phabricator.wikimedia.org/T403563]
* L'équipe Contribution et l’équipe Apprentissage automatique travaillent actuellement sur un nouveau contrôle destiné aux novices : [[mw:Special:MyLanguage/Edit check/Tone Check|la vérification du style]]. À l’aide d'un modèle prédictif, ce contrôle encouragera les contributeurs à améliorer la tonalité de leurs modifications grâce à l’intelligence artificielle. Nous invitons les bénévoles à examiner la première version du modèle linguistique de ton pour les langues suivantes : arabe, tchèque, allemand, hébreu, indonésien, néerlandais, polonais, russe, turc, chinois, farsi, italien, norvégien, roumain et letton. Les utilisateurs de ces wikis intéressés par l’examen de ce modèle [[mw:Special:MyLanguage/Edit_check/Tone_Check/Model_evaluation|sont invités à s’inscrire sur MediaWiki.org]]. La date limite d’inscription est fixée au 3 octobre, date à laquelle débutera le test.
* La sortie des [[:mw:Special:MyLanguage/Help:Manage blocks|multiblocages]] a entrainé l’affichage inopiné de certains journais de blocage inactifs sur {{#special:Contributions}} et sur les pages utilisateur et de discussion des utilisateurs bloqués. Ce problème sera complètement résolu d’ici quelques jours. Avec ce correctif, [{{fullurl:Special:Allmessages|prefix=sp-contributions-blocked-notice}} les messages préfixées par <code>sp-contributions-blocked-notice</code>] seront retirés et remplacés par [{{fullurl:Special:Allmessages|prefix=blocked-notice-logextract}} ceux préfixés par <code>blocked-notice-logextract</code>] dans quelques semaines. Vous pouvez aider à traduire les nouveaux messages et à mettre à jour les écrasements locaux si besoin.
* Il y a eu un beugue avec les liens ajoutés depuis l’éditeur visuel quand ils contenaient des caractères tel que <code dir=ltr><nowiki>[ ] |</nowiki></code> après le marqueur de fragment (<code><nowiki>#</nowiki></code>). Ils n’étaient pas encodés correctement, ce qui créait des liens erronés. Cela a été corrigé. [https://phabricator.wikimedia.org/T404823]
* Un nouveau wiki a été créé : une {{int:project-localized-name-group-wikiquote/fr}} en [[d:Q9237|malais]] ([[q:ms:|<code>q:ms:</code>]]) [https://phabricator.wikimedia.org/T404698]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:21|la tâche soumise|les {{formatnum:21}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:21||s}} la semaine dernière]]. Par exemple, la [[mw:Special:MyLanguage/Product Safety and Integrity/Anti-abuse signals/User Info|fiche d’informations utilisateur]] affiche désormais les verrouillages et blocages globaux actuellement actifs. [https://phabricator.wikimedia.org/T401128]
'''Actualités pour la contribution technique'''
* À partir de cette semaine, les contributeurs utilisant des modules Lua pourront utiliser la fonction <code>[[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#mw.title.newBatch|mw.title.newBatch]]</code> pour rechercher l’existence de 25 pages en une seule fois, d’une manière qui ne comptera que pour un seul appel de [[mw:Special:MyLanguage/Manual:Parser functions#Expensive parser functions|fonction couteuse]].
* Un nouveau [[m:Special:MyLanguage/Product and Technology Advisory Council/Unsupported Tools Working Group|groupe de travail sur les outils non pris en charge]] s’est créé dans le cadre du travail en cours pour déterminer collectivement les priorités techniques, similaire au [[m:Special:MyLanguage/Product and Technology Advisory Council|conseil consultatif des Produits et Technologies]] (PTAC). Le groupe de travail aidera à prioriser et à examiner les demandes de maintenance pour les extensions, gadgets, robots et outils non maintenus. Pour le premier cycle, le groupe priorisera un outil non pris en charge de Wikimedia Commons.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.21|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/40|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W40"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 29 septembre 2025 à 22:52 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29355230 -->
== Upcoming Dark Mode user interface rollout for anonymous Wikimedia sites users ==
<div lang="en" dir="ltr">
{{int:Hello}} Wikimedians,
Apologies if this message is not in your language. {{int:please-translate}}.
The [[mw:Special:MyLanguage/Reading/Web|Reader Experience team]] will launch the Dark mode feature for anonymous users on all Wikimedia sites, including yours, on October 29, 2025.
[[:en:Special:MyLanguage/Light-on-dark color scheme|Dark mode]] is an option that allows users to view pages in light-coloured text, and icons on a dark background. Once it is available for anonymous users, they can enable it when using various devices. More information on ways to enable it can be found on [[:en:Special:MyLanguage/Wikipedia:Dark mode#Options for anyone|this page]].
Given many pages are still not compatible with dark mode this will be an opt-in feature and not automatically apply to pages.
Dark mode requires modifications to content pages and templates, and since our initial launch [https://diff.wikimedia.org/2024/07/17/dark-modes-bright-future-how-dark-mode-will-transform-wikipedias-accessibility/ in July 2024], we have been working with communities and helping them prepare for dark mode. Before the rollout, it is essential that template authors and technical contributors test dark mode and read [[mw:Special:MyLanguage/Reading/Web/Accessibility for reading/Updates/2024-04|this page]] to learn how to make pages Dark mode-ready and address any compatibility issues found in templates.
We will fix most color compatibility issues only on the most-viewed pages on projects with over 5 million monthly page views. Technical contributors with an account should opt into dark mode currently using preferences or settings and test pages and seek help before the release to ensure everything complies before the enablement.
If you have any questions or need help, please [[mw:Special:MyLanguage/Talk:Reading/Web/Accessibility for reading#|contact the Reader Experience team]] for support.
Thank you!
</div>
<bdi lang="en" dir="ltr">[[User:UOzurumba (WMF)|UOzurumba (WMF)]]</bdi> 30 septembre 2025 à 04:08 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:UOzurumba_(WMF)/sandbox_Dark_mode_deployment_mass_message_list_(October_2025)&oldid=29358561 -->
== Actualités techniques n° 2025-41 ==
<section begin="technews-2025-W41"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/41|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* [[mw:Special:MyLanguage/Help:Edit check#paste|Paste Check]] est une nouvelle fonctionnalité de vérification des modifications destinée à prévenir et à lutter contre les violations de droits d'auteur. Lorsque les contributeurs collent du texte dans un article, Paste Check les invite à confirmer l'origine et la licence du contenu. À partir du mercredi 8 octobre, [[phab:T403680|22 wikis testeront Paste Check]]. Paste Check aidera les nouveaux bénévoles à comprendre et à respecter les politiques et les directives nécessaires pour contribuer de manière constructive aux projets Wikipédia.
'''Actualités pour la contribution'''
* Les appareils mobiles recevront les articles mobiles directement sur le domaine standard (par exemple, <code>en.wikipedia.org</code>), au lieu d'une redirection vers un domaine "m" (par exemple, <code>en.m.wikipedia.org</code>). Ce changement améliore les performances. Il sera activé cette semaine sur Wikipédia. Les URL mobiles existantes et la désactivation de l'affichage "Bureau" restent disponibles. [[mw:Requests for comment/Mobile domain sunsetting/2025 Announcement|En savoir plus]]. [https://phabricator.wikimedia.org/T214998]
* Les nouveaux [[mw:Special:MyLanguage/Help:CirrusSearch#creationdate and lasteditdate|filtres de date]], <code dir=ltr>creationdate:</code> et <code dir=ltr>lasteditdate:</code> sont désormais disponibles dans le moteur de recherche du wiki. Ils permettent aux utilisateurs de filtrer les résultats de recherche par date de première ou de dernière révision d'une page. Ces filtres prennent en charge les opérateurs de comparaison (par exemple, <code dir=ltr>>2024</code>) et les dates relatives (par exemple, <code dir=ltr>today-1d</code>), facilitant ainsi la recherche de contenu ou de pages récemment mis à jour dans des tranches d'âge spécifiques. [https://phabricator.wikimedia.org/T403593]
* [[f:|Wikifunctions]] prend désormais en charge le texte enrichi dans les appels intégrés sur les 150 wikis où il est activé. Pour illustrer cela, l'équipe a créé un [[f:Z26333|tableau de déclinaison latine]] que les éditeurs du Wiktionnaire peuvent utiliser pour générer automatiquement les formes nominales, produisant ainsi des résultats clairs et formatés ; voir un [[f:Wikifunctions:Embedded function calls/Wiktionary tables demonstration|exemple de résultat]]. Pour toute aide ou commentaire, veuillez [[f:Wikifunctions:Project chat|contacter l'équipe Wikifunctions]]. [https://phabricator.wikimedia.org/T397402]
* Un lien d'édition apparaîtra désormais à l'intérieur de la zone de catégories sur les pages d'articles pour les utilisateurs connectés, ce qui lancera directement la boîte de dialogue de catégorie VisualEditor. [https://phabricator.wikimedia.org/T291691]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:34|la tâche soumise|les {{formatnum:34}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:34||s}} la semaine dernière]]. Par exemple, il y a eu un problème lors du téléchargement des fichiers PDF la semaine dernière et cela a été résolu. [https://phabricator.wikimedia.org/T405957]
'''Actualités pour la contribution technique'''
* Le champ <code dir=ltr>rev_sha1</code> dans la table de révision de la base de données est supprimé au profit du champ <code dir=ltr>content_sha1</code> dans la table de contenu de la base de données. Voir [https://lists.wikimedia.org/hyperkitty/list/cloud@lists.wikimedia.org/thread/2D2M3SP4WHR6BXXKTZ2PBLZQYR3EGQVR/ l'annonce] pour plus d'informations.
* L'[[mw:Special:MyLanguage/Reading/Web|équipe Expérience de lecture]] déploiera l'interface utilisateur du [[w:en:Light-on-dark color scheme|Mode sombre]] sur tous les sites Wikimedia le 29 octobre 2025. Tous les utilisateurs anonymes des sites Wikimedia auront la possibilité d'activer un jeu de couleurs avec du texte clair sur fond sombre. Ce système vise à améliorer le confort de lecture, notamment en basse lumière. Les auteurs de modèles et les contributeurs techniques sont encouragés à [[mw:Special:MyLanguage/Reading/Web/Accessibility for reading/Updates/2024-04|apprendre à préparer leurs pages pour le mode sombre]] et à résoudre tout problème de compatibilité rencontré avec les modèles de leur wiki avant l'activation. Pour toute question ou assistance, veuillez contacter l'équipe Web sur [[mw:Talk:Reading/Web/Accessibility for reading#|cette page de discussion]] avant l'activation. [https://phabricator.wikimedia.org/T395628]
* À compter du lundi 6 octobre, les points de terminaison d'API du chemin <code>rest.php</code> seront redirigés vers une nouvelle passerelle d'API interne. Les wikis individuels seront mis à jour en fonction des groupes de versions standard, avec un trafic total en constante augmentation. Ce changement devrait être sans rupture ni perturbation. Si des problèmes sont observés, veuillez déposer un ticket Phabricator auprès du [[phab:tag/serviceops/|tableau de bord de l'équipe Service Ops]]. [https://phabricator.wikimedia.org/T400130]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.22|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/41|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W41"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 6 octobre 2025 à 19:23 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29400897 -->
== Donnez votre avis : votez pour le conseil d'administration 2025 ==
<section begin="announcement-content" />
Bonjour à tous,
La période de vote pour les [[m:Special:MyLanguage/Wikimedia Foundation elections/2025|élections 2025 du conseil d'administration]] est désormais ouverte. Les candidats se présentent pour deux (2) sièges au conseil.
Pour vérifier votre éligibilité en tant qu'électeur, veuillez consulter la [[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Voter eligibility guidelines|page relative à l'éligibilité des électeurs]].
Pour en savoir plus à leur sujet, [[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Candidates|lisez leurs déclarations de candidature et regardez leurs vidéos de présentation]].
Lorsque vous serez prêt, rendez-vous sur la [[m:Special:SecurePoll/vote/405|page de vote SecurePoll pour voter]].
'''Le vote est ouvert du 8 octobre à 00h00 UTC au 22 octobre à 23h59 UTC.'''
Bien à vous,
Abhishek Suryawanshi<br />Président, Comité des élections<section end="announcement-content" />
[[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 9 octobre 2025 à 06:47 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29360896 -->
== Actualités techniques n° 2025-42 ==
<section begin="technews-2025-W42"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/42|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* La semaine dernière, des améliorations dans les fonctionnalités de sécurité des comptes et d’authentification à deux facteurs (A2F) ont été activées sur tous les wikis. Ces changements comprenaient des améliorations dans l’interface utilisateur de [https://auth.wikimedia.org/metawiki/wiki/Special:AccountSecurity Spécial:AccountSecurity], la prise en charge de plusieurs méthodes d’A2F ''via'' des applis d’authentification et des clés de sécurité portables (auparavant, les utilisateurs et utilisatrices ne pouvaient activer qu’une seule méthode) et un nouveau module de codes de récupération qui limite le blocage de comptes en raison de la perte des applis ou appareils d’authentification. Dans le cadre du projet [[mw:Special:MyLanguage/Product Safety and Integrity/Account Security|Sécurité des comptes]], le travail continue durant cette fin d’année 2025 sur des d’autres améliorations de l’exéprience utilisateur et la prise en charge de clés de passe comme solution d’authentification alternative.
'''Actualités pour la contribution'''
* Une autre partie du projet Sécurité des comptes est de rendre l’A2F disponible pour tous les utilisateurs et utilisatrices. Comme les personnes ayant des privilèges avancés (tels que les admins et bureaucrates), 40 % des contributeurs et contributrices ont désormais accès à l’A2F. Vous pouvez vérifier si vous y avez accès sur [https://auth.wikimedia.org/metawiki/wiki/Special:AccountSecurity Spécial:AccountSecurity]. Les instructions pour l’activer sont sur cette page. Le projet est de continuer à augmenter sa disponibilité s’il est déterminé que les capacités d’assistance aux utilisateurs sont suffisantes pour assister l’intégralité de la communauté. [https://phabricator.wikimedia.org/T400579]
* Cette semaine, les utilisateurs et utilisatrices sur les wikis où les [[mw:Special:MyLanguage/Talk pages project/Usability|améliorations d’ergonomie]] sont activées par défaut sur les pages de discussions (c’est-à-dire partout sauf les 12 wikis listés [[phab:T379264|sur la tâche T379 264 de Phabricator]]) pourront remercier quelqu’un pour son message directement depuis la page de discussion où celui-ci apparait. Auparavant, il fallait visiter l’historique de la page de discussion pour remercier quelqu’un. [[diffblog:2025/10/13/revolutionizing-gratitude-a-new-era-of-thanking-comments/|En savoir plus sur ce changement]]. [https://phabricator.wikimedia.org/T366095]
* Les utilisateurs et utilisatrices qui n’ont pas [[Special:Preferences#mw-prefsection-personal-email|vérifié leur adresse électronique]] recevront bientôt une notification mensuelle pour leur rappeler. En effet, avoir vérifié son adresse de courriel permet de récupérer son compte plus facilement. Ces rappels ne seront pas envoyés si la personne est inactive ou si elle retire l’adresse non vérifiée de son compte. [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Email_confirmation][https://phabricator.wikimedia.org/T58074]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:21|la tâche soumise|les {{formatnum:21}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:21||s}} la semaine dernière]]. Par exemple, un correctif d’une erreur occasionnel lors de l’enregistrement d’un paragraphe traduit dans l’outil de traduction de contenu permet désormais de voir plus facilement les messages d’erreurs liés. [https://phabricator.wikimedia.org/T376531]
'''Actualités pour la contribution technique'''
* Le groupe de travail sur les Outils non pris en charge a choisi [[c:Special:MyLanguage/Commons:Video2commons|Video2Commons]] comme premier outil pour son cycle pilote. Le groupe étudiera des façons d’améliorer et d’entretenir l’outil durant les prochains mois. [[m:Special:MyLanguage/Product and Technology Advisory Council/Unsupported Tools Working Group|En savoir plus sur Méta-Wiki]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.23|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/42|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W42"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 13 octobre 2025 à 20:59 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29434481 -->
== Aidez-nous à décider du nom du nouveau projet ''Abstract Wikipedia'' ==
<section begin="function1"/>
Bonjour !
Participez à choisir un nom pour le nouveau projet de wiki ''Abstract Wikipedia'' (« Wikipédia abstraite ») ! Ce projet sera un wiki qui permettra aux utilisateurs et utilisatrices de combiner les fonctions des [[:f:|Wikifonctions]] et les données de Wikidata afin de générer des phrases naturelles dans les langues prises en charges. Ces phrases pourront ensuite être réutilisées sur les Wikipédia (ou ailleurs).
Le scrutin se déroulera en deux tours, chacun étant suivi par un examen juridique des candidats ; ils commenceront respectivement le 20 octobre et le 17 novembre. Notre objectif est d’avoir un nom de projet final choisi à la mi-décembre 2025. Si vous souhaitez participer, '''[[m:Special:MyLanguage/Abstract Wikipedia/Abstract Wikipedia naming contest|lisez le détail du scrutin et votez maintenant]]''' sur Meta-Wiki.
{{Int:Feedback-thanks-title}}
<section end="function1"/>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 20 octobre 2025 à 13:42 (CEST)
<!-- Message envoyé par User:Sannita (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29432175 -->
== Actualités techniques n° 2025-43 ==
<section begin="technews-2025-W43"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/43|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Pour optimiser la manière dont les données des utilisateurs sont stockées dans nos bases de données, les préférences enregistrées des utilisateurs qui ne se sont pas connectés depuis plus de cinq ans et qui ont moins de 100 modifications seront supprimées. Lorsque ces utilisateurs reviendront, les paramètres par défaut s’appliqueront. [https://phabricator.wikimedia.org/T406724]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:20|la tâche soumise|les {{formatnum:20}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:20||s}} la semaine dernière]]. Par exemple, il y avait un lien brisé depuis le message d’interface de GlobalContributions vers la page GlobalContributions de XTools qui a maintenant été corrigé. [https://phabricator.wikimedia.org/T406415]
'''Actualités pour la contribution technique'''
* Le travail de routage de tous le trafic vers les points de terminaison d’API sous la route <code dir=ltr><nowiki>rest.php</nowiki></code> à travers une passerelle commune d’API est désormais terminé. Si vous constatez un problème, n’hésitez pas à ouvrir une tâche Phabricator dans le [[phab:tag/serviceops/|tableau de bord de l’équipe Opé’s de service]].
* Les modifications aux références et qualificateurs de Wikidata seront désormais moins souvent affichés dans les Modifications récentes et listes de suivi des autres wikis, réduisant les notifications non-nécessaires. Cela réduira la quantité globale d’éléments disparates. Les pages directement sur Wikidata restent inchangées. [https://phabricator.wikimedia.org/T401290]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.24|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/43|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W43"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 20 octobre 2025 à 21:36 (CEST)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29478670 -->
== Actualités techniques n° 2025-44 ==
<section begin="technews-2025-W44"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/44|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Un test A/B/C a été lancé sur l’appli Wikipédia pour iOS concernant les améliorations de navigation par onglet, pour certaines langues et régions. Intitulé « Onglets plus dynamiques », ce test explores de nouvelles expériences d’onglets et inclut les recommandations d’articles « Le saviez-vous » et « Parce que vous avez lu ». Vous trouverez [[mw:Special:MyLanguage/Wikimedia Apps/Team/iOS/Tabbed Browsing (Tabs)/New Tab Experience and Recommendations Experiment|plus d’informations sur la page du projet]].
* Les utilisateurs autoconfirmés sur les [[gitiles:operations/mediawiki-config/+/a2d2aaab9ace84280dd2f4c70a33bb69cd73850f/dblists/small.dblist|petits]] et [[gitiles:operations/mediawiki-config/+/a2d2aaab9ace84280dd2f4c70a33bb69cd73850f/dblists/medium.dblist|moyens wikis]] avec l’extension CampaignEvents peuvent désormais utiliser le [[m:Special:MyLanguage/Event Center/Registration|système d’inscription aux évènements]] sans avoir le droit d’organisation d’évènement. Cette fonctionnalité laisse les équipes d’organisation activer l’inscription, gérer les participants et permet aux utilisateurs de s’inscrire en un clic au lieu de signer sur les pages des évènements.
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:31|la tâche soumise|les {{formatnum:31}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:31||s}} la semaine dernière]]. Par exemple, le problème des clignotements lors de l’appui sur les flèches du clavier dans les paramètres du mode sombre de Vector 2022 a été corrigé. [https://phabricator.wikimedia.org/T402285]
'''Actualités pour la contribution technique'''
* L’extension CampaignEvents sera déployé sur tous les wikis restants durant la semaine du 17 novembre 2025. Elle inclut trois fonctionnalités : le système d’inscription aux évènements, la liste de collaboration et la liste d’invitation. Pour ce déploiement, la liste d’invitation ne sera pas activée sur les Wikifonctions et MediaWiki sauf demande contraire de ces communautés. Consultez [[m:Special:MyLanguage/CampaignEvents/Deployment status|la page de déploiement pour en savoir plus]].
* L’expérience de bac à sable REST basée sur SwaggerUI est désormais en ligne sur tous les projets wiki. Le bac à sable est accessible sur la page [[{{#special:RestSandbox}}]]. N’hésitez pas à signaler tout problème sur le tableau de bord de l’équipe des Interfaces de MediaWiki, et à participer aux discussions sur la [[mw:Special:MyLanguage/MediaWiki Interfaces Team/Feature Feedback/REST Sandbox|page de lancement du projet]]. [https://phabricator.wikimedia.org/project/board/6931/]
* Les points de terminaison de transformation avec une barre oblique finale (/) dans l’API REST de MediaWiki sont désormais dépréciés. Ils continueront de fonctionner pour le moment, mais devraient être supprimés d’ici à la fin janvier 206. Tous les utilisateurs et utilisatrices de l’API qui les appellent sont encouragés à transitionner vers les versions sans barre oblique finale. Les deux variantes de ces points de terminaison peuvent être trouvés et testés à l’aide du [https://test.wikipedia.org/w/index.php?api=mw-extra&title=Special%3ARestSandbox bac à sable REST]. Consultez la page [[mw:API/Deprecation|Dépréciation de l’API REST de MediaWiki]] pour plus de détails sur les règles et procédures de dépréciation dans l’API.
* Un [[mw:API:REST API/Changelog|journal des changements existe désormais pour l’API REST de MediaWiki]]. Il fournit un aperçu des changements, facilitant le suivi des améliorations et versions par les développeurs et développeuses. Les annonces continueront sur les canaux classiques de communication, dont les ''Actualités techniques'' et les listes de distribution, mais peuvent désormais être plus facilement référencées depuis un endroit centralisateur. Si vous avez des retours à faire sur le style, la structure ou le contenu de ce journal, vous pouvez [[mw:API talk:REST API/Changelog|rejoindre la discussion]].
* Les admins peuvent supprimer la catégorie de suivi précédemment ajoutée par l’extension JsonConfig, puisqu’elles n’est plus utilisée. Ces catégories sont liées à l’élément [[d:Q130635582#sitelinks-wikipedia|Q130 635 582]]. Il est normal qu’il reste des pages dans cette catégorie en raison du cache : elles seront automatiquement retirées lors de la prochaine modification de chaque page. [https://phabricator.wikimedia.org/T378352]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.25|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/44|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W44"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 27 octobre 2025 à 20:31 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29513638 -->
== À la recherche de bénévoles pour rejoindre plusieurs comités du mouvement ==
<section begin="announcement-content" />
Chaque année, généralement d’octobre à décembre, plusieurs comités du mouvement cherchent de nouveaux bénévoles.
Pour en savoir plus sur les comités, consultez leurs pages Meta-wiki :
* [[m:Special:MyLanguage/Affiliations Committee|Comité des affiliations (AffCom)]]
* [[m:Special:MyLanguage/Ombuds commission|Commission de médiation (OC)]]
* [[m:Special:MyLanguage/Wikimedia Foundation/Legal/Community Resilience and Sustainability/Trust and Safety/Case Review Committee|Comité d'examen des cas (CRC)]]
Les candidatures aux comités sont ouvertes à partir du 30 octobre 2025. Les candidatures au Comité des affiliations sont closes le 11 décembre 2025, et les candidatures à la commission de médiation et au Comité d'examen des cas sont closes le 11 décembre 2025. Pour savoir comment postuler, [[m:Special:MyLanguage/Wikimedia Foundation/Legal/Committee appointments|consultez la page de nomination sur Meta-wiki]]. Publiez sur la page de discussion ou envoyez un e-mail à cst[[File:At sign.svg|16x16px|link=|(_AT_)]]wikimedia.org pour toute question que vous pourriez avoir.
Pour l'équipe de soutien du comité,
<section end="announcement-content" />
-[[m:User:MKaur (WMF)| MKaur (WMF)]] 30 octobre 2025 à 15:12 (CET)
<!-- Message envoyé par User:MKaur (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29517125 -->
== <span lang="en" dir="ltr">Tech News: 2025-45</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W45"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/45|Translations]] are available.
'''Updates for editors'''
* Administrators will now find that [[{{#special:MergeHistory}}]] is now significantly more flexible about what it can merge. It can now merge sections taken from the middle of the history of the source (rather than only the start) and insert revisions anywhere in the history of the destination page (rather than only the start). [https://phabricator.wikimedia.org/T382958]
* For users with "{{int:discussiontools-preference-autotopicsub}}" [[Special:Preferences#mw-prefsection-editing|enabled in their preferences]], starting a new topic or adding a reply to an existing topic will now subscribe them to replies to that topic. Previously, this would only happen if the DiscussionTools "{{int:Skin-action-addsection}}" or "{{int:Discussiontools-replybutton}}" widgets were used. When DiscussionTools was originally launched existing accounts were not opted in to automatic topic subscriptions, so this change should primarily affect newer accounts and users who have deliberately changed their preferences since that time. [https://phabricator.wikimedia.org/T290778]
* Scribunto modules can now be used to [[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#SVG library|generate SVG images]]. This can be used to build charts, graphics and other visualizations dynamically through Lua, reducing the need to compose them externally and upload them as files. [https://phabricator.wikimedia.org/T405861]
* Wikimedia sites now provide all anonymous users with the option to enable a dark mode color scheme, featuring light-colored text on a dark background. This enhancement aims to deliver a more enjoyable reading experience, especially in dimly lit environments. [https://phabricator.wikimedia.org/T395628]
* Users with large watchlists have long faced timeouts when editing [[Special:EditWatchlist|Special:EditWatchlist]]. The page now loads entries in smaller sections instead of all at once due to a paging update, allowing everyone to edit their watchlists smoothly. As part of the database update, sorting by expiry has been removed because it was over 100× slower than sorting by title. A [https://meta.wikimedia.org/wiki/Community_Wishlist/W454 community wish] has been created to explore alternative ways to restore sort-by-expiry. If this feature is important to you, please support the wish! [https://phabricator.wikimedia.org/T41510]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:31}} community-submitted {{PLURAL:31|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the fixing of the persisting highlighting when using VisualEditor find and replace during a query. [https://phabricator.wikimedia.org/T407318]
'''Updates for technical contributors'''
* Since 2019 the [[m:Special:MyLanguage/Wikimedia URL Shortener|Wikimedia URL Shortener]] at https://w.wiki is available for all Wikimedia wikis to create short links to articles, permalinks, diffs, etc. It is available in the sidebar as "Get shortened URL". There are 30 wikis that also install an older "ShortUrl" extension. The old extension will soon be removed. This means <code>/s/</code> URLs will not be advertised under article titles via HTML <code dir=ltr>class="title-shortlink"</code>. The <code>/s/</code> URLs will keep working. [https://phabricator.wikimedia.org/T107188]
* On Thursday, October 30, the [[:mw:Special:MyLanguage/MediaWiki Interfaces Team|MediaWiki Interfaces]] and [[:mw:Special:MyLanguage/Wikimedia Site Reliability Engineering|SRE Service Operations]] teams began rerouting Action API traffic through a common API gateway. Individual wikis will be updated based on the standard release groups, with total traffic increased over time. This change is expected to be non-breaking and non-disruptive. If any issues are observed, please file a Phabricator ticket to the [https://phabricator.wikimedia.org/tag/serviceops/ Service Ops team] board.
* MediaWiki Train deployments will pause for the final two weeks of 2025: 22 December and 29 December. Backport windows will also pause between Monday, 22 December 2025 and Thursday, 2 January 2026. A backport window is a scheduled time to add things like bug fixes and configuration changes. There are seven deployment trains remaining for 2025. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/SMWTEAES4SDLDUSK4HMWNBSKNCXZAWYN/]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.45/wmf.26|MediaWiki]]
'''In depth'''
* In 2025, the Wikimedia Foundation reported that AI systems and search engines increasingly use Wikipedia content without driving users to the site, contributing to an 8% drop in human pageviews compared to 2024. After detecting bots disguised as humans, Wikimedia updated its traffic data to reflect this shift. Read more about current user trends on Wikipedia in [[diffblog:2025/10/17/new-user-trends-on-wikipedia/|a Diff blog post]].
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/45|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W45"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 3 novembre 2025 à 20:34 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29552512 -->
== Actualités techniques n° 2025-46 ==
<section begin="technews-2025-W46"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/46|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
[[File:Talk pages default look (April 2023).jpg|thumb|alt=Capture d'écran des améliorations visuelles apportées aux pages de discussion|Exemple d’une page de discussion avec le nouveau design, en français.]]
* À partir du 12 novembre, les utilisateurs verront un changement dans [[m:Special:MyLanguage/Talk pages project/Feature summary#Usability improvements|l’apparence des pages de discussion]] sur [[Phab:T379264|certaines Wikipédia]]. Ce changement de design a déjà été appliqué sur presque [[phab:T392121|tous les wikis]] ; mais il arrivera plus tard pour [[phab:T409297|la Wikipédia en anglais]]. Vous pouvez en savoir plus [[diffblog:2024/05/02/making-talk-pages-better-for-everyone/|sur ''Diff'']]. Les utilisateurs peuvent choisir de ne pas appliquer ces changements [[Special:Preferences#mw-prefsection-editing|dans leurs préférences utilisateur]] en décochant « {{int:discussiontools-preference-visualenhancements}} ». [https://phabricator.wikimedia.org/T379264]
* MediaWiki peut désormais afficher automatiquement un [[mw:Special:MyLanguage/Help:Protection indicators|indicateur d’état]] lorsqu’une page est protégée. Cette fonctionnalité est désactivée par défaut. Elle peut être activée sur [[m:Special:MyLanguage/Requesting wiki configuration changes|demande de la communauté]]. [https://phabricator.wikimedia.org/T12347]
* L’utilisation des boutons « {{int:showpreview}} » ou « {{int:showdiff}} » dans l’éditeur de wikicode transmettra désormais certains paramètres d’URL tels que ''[[mw:Special:MyLanguage/Manual:Parameters to index.php#useskin|useskin]]'', ''[[mw:Special:MyLanguage/Manual:Parameters to index.php#uselang|uselang]]'' et ''[[mw:Special:MyLanguage/Help:Section#Editing sections|section]]''. Cette mise à jour corrige également un problème où, si le navigateur plantait lors de l’aperçu des modifications d’une section, enregistrer ces modifications pouvait écraser la page entière avec le contenu de cette seule section. [https://phabricator.wikimedia.org/T62744][https://phabricator.wikimedia.org/T24029][https://phabricator.wikimedia.org/T155097]
* Les wikis Wikivoyage peuvent utiliser [[mw:Special:MyLanguage/Help:Extension:Kartographer#Markers and counters|des marqueurs de carte colorés dans le texte de l'article]]. Le texte de ces marqueurs sera désormais affiché en noir ou blanc contrastant, au lieu d'être toujours blanc. Les solutions locales de contournement du problème peuvent être supprimées. [https://phabricator.wikimedia.org/T369454]
* L'onglet Activité de l'application Wikipedia pour Android est désormais disponible pour tous les utilisateurs. Le nouvel onglet offre des informations personnalisées sur la lecture, la modification et les dons, tout en simplifiant la navigation et en rendant l'utilisation de l'application plus attrayante. [https://www.mediawiki.org/wiki/Wikimedia_Apps/Team/Android/Activity_Tab_Experiment]
* L’équipe de Croissance du lectorat lance une expérimentation appelée « Exploration des images » afin de tester comment faciliter la navigation et la découverte d’images dans les articles de Wikipédia. Cette expérimentation prendra la forme d’un test A/B uniquement sur mobile, sur Wikipédia en anglais la semaine du 17 novembre pendant quatre semaines, affectant 0,05 % des utilisateurs de ce wiki. Le test a été lancé le 3 novembre sur les wikis en arabe, chinois, français, indonésien et vietnamien, affectant jusqu’à 10 % des utilisateurs de ces wikis. [https://www.mediawiki.org/wiki/Readers/Reader_Growth/WE3.1.3_Image_Browsing]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]]. Par exemple, l’impossibilité de verrouiller des comptes depuis les sites mobiles a été corrigée. [https://phabricator.wikimedia.org/T256185]
'''Actualités pour la contribution technique'''
* Les [[wikitech:Help talk:Toolforge/Toolforge standards committee#November 2025 committee nominations|mises en candidatures sont ouvertes sur Wikitech]] pour désigner de nouveaux membres au [[wikitech:Help:Toolforge/Toolforge standards committee|comité des normes de Toolforge]]. Le comité supervise notamment le [[wikitech:Help:Toolforge/Right to fork policy|règlement du droit à la création de fork]] et le [[wikitech:Help:Toolforge/Abandoned tool policy|règlement des outils abandonnés]] de Toolforge. Les mises en candidatures sont possibles jusqu’au 28 novembre 2025.
* Le [[w:JSON Web Token#Standard fields|champ émetteur JWT]] dans les [[mw:Special:MyLanguage/OAuth/For Developers#OAuth 2|jetons d’accès OAuth 2]] pour les [[m:Special:MyLanguage/Help:Unified login|wikis SUL]] a été changé, il contient maintenant <code><nowiki>https://meta.wikimedia.org</nowiki></code>. Les anciens jetons d’accès continueront de fonctionner. [https://phabricator.wikimedia.org/T399199]
* Le [[w:JSON Web Token#Standard fields|champ sujet du JWT]] dans les [[mw:Special:MyLanguage/OAuth/For Developers#OAuth 2|jetons d'accès OAuth 2]] changera bientôt de <code><user id></code> à <code dir=ltr style="white-space:nowrap">mw:<identity type>:<user id></code>, où <code><identity type></code> est généralement <code dir=ltr>CentralAuth:</code><!-- not a typo --> (pour les [[m:Special:MyLanguage/Help:Unified login|wikis SUL]]) ou <code dir=ltr style="white-space:nowrap">local:<wiki id></code> (pour les autres wikis). Cela vise à éviter les conflits entre différents types d’identifiants utilisateur et à rendre les jetons d’accès OAuth 2 plus similaires au cookie <code>sessionJwt</code>. Les anciens jetons d’accès continueront de fonctionner. [https://phabricator.wikimedia.org/T399199]
* Les messages de blocage de MediaWiki ([[MediaWiki:Blockedtext|blockedtext]], [[MediaWiki:Blockedtext-partial|blockedtext-partial]], [[MediaWiki:Autoblockedtext|autoblockedtext]], [[MediaWiki:Systemblockedtext|systemblockedtext]], [[MediaWiki:Blockedtext-tempuser|blockedtext-tempuser]], [[MediaWiki:Autoblockedtext-tempuser|autoblockedtext-tempuser]]) prennent désormais en charge des paramètres supplémentaires pour indiquer si l’utilisateur est bloqué pour modifier sa propre page de discussion (<code><nowiki>$9</nowiki></code>) ou pour envoyer des courriels à d’autres utilisateurs (<code><nowiki>$</nowiki><nowiki>10</nowiki></code>). [https://phabricator.wikimedia.org/T285612]
* Une branche <code>REL1_45</code> pour le noyau MediaWiki et chacune des extensions et habillages dans le git de Wikimedia a été créée. Il s’agit de la première étape du processus de publication de MediaWiki 1.45.0, prévue pour fin novembre 2025. Si vous travaillez sur une correction de bogue critique ou sur une nouvelle fonctionnalité, vous devrez peut-être prendre note de cette information. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/ZUY7TY3Z6XPZWZVAZV63OPO5OW52Q6GE/]
* Le processus de génération des copies mémoire (<i lang="en">dumps</i>) de CirrusSearch a été mis à jour en raison d’une baisse de performance. Si vous rencontrez des problèmes lors de la migration vers les copies de remplacement, vous pouvez contacter l’équipe Plateforme de recherche pour obtenir de l’aide. [https://phabricator.wikimedia.org/T366248][https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/3KQPOR6ACVN6OVLMLZPIBXQSWQKW4E3K/]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.2|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/46|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W46"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 10 novembre 2025 à 21:37 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29606150 -->
== Actualités techniques n° 2025-47 ==
<section begin="technews-2025-W47"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/47|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* L’[[mw:Special:MyLanguage/Readers/Reader Experience|équipe Reader Experience]] expérimente les [[mw:Special:MyLanguage/Readers/Reader Experience/WE3.3.4_Reading lists|listes de lecture sur le web mobile]], permettant aux lecteurs connectés n’ayant jamais effectué de modifications de sauvegarder des listes privées d’articles à lire plus tard. L’expérience est en cours sur les Wikipédias en arabe, chinois, français, indonésien et vietnamien depuis la semaine du 10 novembre, et commencera sur la Wikipédia en anglais la semaine du 17 novembre.
* Les utilisateurs qui ne reçoivent pas leur code de vérification par e-mail lors de la connexion peuvent désormais obtenir de l’aide en remplissant un formulaire sur une nouvelle page spéciale. Cette mise à jour fait partie de l’initiative [[mw:Special:MyLanguage/Product Safety and Integrity/Account Security|« Sécurité des comptes »]]. Si votre compte est associé à une adresse électronique, veuillez vous assurer que vous y avez toujours accès. Lors d’une connexion depuis un nouvel appareil ou un nouvel emplacement, sans authentification à deux facteurs (2FA), il peut vous être demandé de saisir un code à 6 chiffres envoyé par courrier électronique pour terminer la connexion. [[mw:Special:MyLanguage/Product Safety and Integrity/Account Security#Why are you requiring me to enter a code from my email to log in? Can I opt out of this?|En savoir plus]].
* Un nouveau wiki a été créé : un {{int:project-localized-name-group-wikisource}} en [[d:Q13324|minangkabau]] ([[s:min:|<code>s:min:</code>]]) [https://phabricator.wikimedia.org/T408317]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Dans le cadre du projet [[mw:Special:MyLanguage/Parsoid/Parser Unification|« Unification de l’analyseur »]], l’équipe Content Transform a déployé Parsoid comme analyseur par défaut sur de nombreuses Wikipédias à faible trafic et prépare la prochaine étape pour les wikis à fort trafic. Ce message vous invite à activer Parsoid comme indiqué dans la documentation [[mw:Special:MyLanguage/Help:Extension:ParserMigration|Extension:ParserMigration]], et à identifier tout problème que vous pourriez rencontrer dans vos propres flux de travail (robots, gadgets ou scripts utilisateur). Merci de nous en informer via le lien « Signaler un bug d’affichage » dans la barre latérale des outils, ou en créant un ticket sur Phabricator en mentionnant l’[[phab:project/view/5846|équipe Content Transform dans Phabricator]].
* Outils non pris en charge : plusieurs problèmes liés à [[:c:Special:MyLanguage/Commons:Video2commons|Video2Commons]] ont été corrigés, notamment les échecs de téléversement liés aux noms de fichiers, les vidéos noires à l’importation, et la gestion des tentatives de reprise. La prise en charge du format AV1 a également été ajoutée. Le travail en cours porte sur la stabilité de l’arrière-plan, les erreurs ffmpeg, l’importation de sous-titres, la gestion des métadonnées et le téléversement de listes de lecture. Pour suivre les tâches spécifiques, consultez le [[phab:tag/video2commons/|tableau Phabricator]].
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.3|MediaWiki]]
'''Rencontres et évènements'''
* Marquez la date pour le prochain Hackathon Wikimedia, qui se tiendra à Milan, en Italie, du 1<sup>er</sup> au 3 mai 2026. Les inscriptions ouvriront en janvier 2026. [https://pretix.eu/wikimedia/Hackathon-2026/ Les candidatures pour les bourses sont actuellement ouvertes] et se clôtureront le 28 novembre 2025. Si vous avez des questions, veuillez envoyer un courriel à <bdi lang="en" dir="ltr">hackathon@wikimedia.org</bdi>.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/47|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W47"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 17 novembre 2025 à 18:26 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29627455 -->
== Rappel : aidez-nous à décider du nom du nouveau projet ''Abstract Wikipedia'' ==
<section begin="function2"/>
Bonjour !
Rappel : aidez-nous à choisir un nom pour le nouveau projet de wiki ''Abstract Wikipedia'' (« Wikipédia abstraite »). Le vote final commence aujourd’hui. Les noms finalistes sont : <span lang="en" dir="ltr" class="mw-content-ltr">Abstract Wikipedia, Multilingual Wikipedia, Wikiabstracts, Wikigenerator, Proto-Wiki</span>. Si vous souhaitez participer, '''[[m:Special:MyLanguage/Abstract Wikipedia/Abstract Wikipedia naming contest|lisez la présentation du scrutin et votez maintenant]]''' sur Méta-Wiki.
{{Int:Feedback-thanks-title}}
<section end="function2"/>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 20 novembre 2025 à 15:21 (CET)
<!-- Message envoyé par User:Sannita (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29583860 -->
== Actualités techniques n° 2025-48 ==
<section begin="technews-2025-W48"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/48|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* La semaine dernière, l'équipe de recherche de Wikimedia a recréé côté serveur la fonctionnalité « ''DWIM'' » (''Do What I Mean'' - Faites ce que je pense) pour les Wikipedias russe et hébraïque. Cette fonctionnalité ajoute des suggestions de saisie au clavier aux suggestions du champ de recherche standard. Par exemple, la recherche de « ''<span lang="und" dir="ltr">cxfcnmt</span>'' » sur Wikipédia russe suggérera désormais « <span lang="ru" dir="ltr">счастье</span> » (« bonheur »), ce que l'utilisateur avait probablement en tête. L'équipe prévoit d'activer cette fonctionnalité pour les autres wikis russes et hébraïques cette semaine. [https://phabricator.wikimedia.org/T408734]
* Plus tard cette semaine, les utilisateurs de la [[Special:Preferences#mw-prefsection-betafeatures|fonctionnalité bêta]] ''{{int:codemirror-beta-feature-title}}'' bénéficieront de la coloration syntaxique dans ''[[mw:Special:MyLanguage/Help:DiscussionTools|DiscussionTools]]''. Pour cela, la préférence ''{{int:discussiontools-preference-sourcemodetoolbar}}'' doit être activée. [https://phabricator.wikimedia.org/T407918]
* L'extension [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|Événements de campagne]], un ensemble d'outils permettant de coordonner des événements et autres collaborations sur le wiki, est désormais disponible sur tous les wikis Wikimedia. Une nouvelle fonctionnalité, ''[[m:Special:MyLanguage/CampaignEvents/Collaborative contributions|Contributions collaboratives]]'', a également été ajoutée pour aider les organisateurs et les participants à visualiser l'impact des activités. Participez à la prochaine [[m:Special:MyLanguage/Event:Connection learning session 3|Session d'apprentissage]] pour découvrir cette nouvelle fonctionnalité et nous faire part de vos commentaires.
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:24|la tâche soumise|les {{formatnum:24}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:24||s}} la semaine dernière]]. Par exemple, le bug qui empêchait ''CodeReviewBot'' de fonctionner a maintenant été corrigé. [https://phabricator.wikimedia.org/T410417]
'''Actualités pour la contribution technique'''
* Les utilisateurs de l'API Wikimedia peuvent participer à une étude d'utilisabilité afin de valider la nouvelle conception des environnements de test de l'API REST de Wikimedia. Les participants intéressés sont invités à remplir le [https://wikimediafoundation.limesurvey.net/487662 questionnaire de recrutement] (en anglais). [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/IREJRRWTZTGCYWQHDMSNJFTQAEPOOAE3/]
* L'équipe Interfaces de MediaWiki abandonne la prise en charge des feuilles de style XSLT dans l'''Action API''. La prise en charge de <code dir=ltr>format=xml'''&xlst={stylesheet}'''</code> sera supprimée des projets Wikimedia d'ici fin novembre 2025. De plus, elle sera bientôt désactivée par défaut dans les versions 1.43 (LTS), 1.44 et 1.45 de MediaWiki. La prise en charge des feuilles de style XSLT sera entièrement supprimée de la version 1.46 de MediaWiki (dont la sortie est prévue entre avril et mai 2026). [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/5AX7UWAVVUNUSBOIRHMNOKWOZ5EZI3JX/]
* Le point d'accès ''WDQS'' hérité ([https://query-legacy-full.wikidata.org/ query-legacy-full.wikidata.org]) sera mis hors service fin décembre 2025, puis définitivement fermé le 7 janvier 2026. Après cette date, les requêtes adressées à query.wikidata.org nécessitant le graphe complet échoueront ou renverront des résultats invalides si elles ne sont pas réécrites pour utiliser la fédération SPARQL. L'équipe encourage les utilisateurs à s'assurer que leurs outils et flux de travail utilisent les points d'accès ''WDQS'' compatibles (<span dir=ltr><nowiki>https://query.wikidata.org/</nowiki></span> - graph principal ou <span dir=ltr><nowiki>https://query-scholarly.wikidata.org/</nowiki></span> - graph scientifique). Pour obtenir de l'aide concernant la migration des cas d'utilisation, veuillez consulter les pages [[d:Special:MyLanguage/Wikidata:Data_access|Accès aux données]] et ''[[d:Wikidata:Request_a_query|Request a query]]'' pour plus de détails et d'assistance sur les méthodes d'accès alternatives.
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.4|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/48|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W48"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 24 novembre 2025 à 16:56 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29702226 -->
== <span lang="en" dir="ltr">Tech News: 2025-49</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W49"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/49|Translations]] are available.
'''Updates for editors'''
* The Wikipedia Year in Review 2025 will be available on December 2 for users of iOS and Android Wikipedia apps, featuring new personalized insights, updated reading highlights, and refreshed designs. Learn more on the review's [[mw:Special:MyLanguage/Wikimedia Apps/Team/Wikipedia Year in Review/Updates|project page]].
* The Growth team is working on improving the text and presentation of the Verification Email sent to new users to make them more welcoming, useful and informative. Some new text have been drafted for A/B testing and you can help by translating them. See [[phab:T396155|Phabricator]].
* [[mw:Special:MyLanguage/Help:Growth/Tools/Add a link|Add a link]] will now be deployed at Japanese, Urdu and Chinese Wikipedias on December 2. Add a link is based on a prediction model that suggests links to be added to articles. While this feature has already been available on most Wikipedias, the prediction model could not support certain languages. A new model has now been developed to handle these languages, and it will be gradually rolled out to other Wikipedias over time. If you would like to know more, please contact [[mw:user:Trizek (WMF)|Trizek (WMF)]].
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:34}} community-submitted {{PLURAL:34|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the issue where search boxes on some Commons pages showed no results due to switch from SpecialSearch to MediaSearch, has now been fixed. [https://phabricator.wikimedia.org/T399476]
* Two new wikis have been created:
** a {{int:project-localized-name-group-wikipedia}} in [[d:Q36846|Toki Pona]] ([[w:tok:|<code>w:tok:</code>]]) [https://phabricator.wikimedia.org/T404457]
** a {{int:project-localized-name-group-wikiquote}} in [[d:Q33655|Nigerian Pidgin]] ([[q:pcm:|<code>q:pcm:</code>]]) [https://phabricator.wikimedia.org/T408318]
'''Updates for technical contributors'''
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.5|MediaWiki]]
'''In depth'''
* The Wikimedia Foundation is in the early stages of exploring approaches to '''Article guidance'''. The initiative aims to identify interventions that could help new editors easily understand and apply existing Wikipedia practices and policies when creating an article. The project is in the exploration and early experimental design phase. All community members are encouraged to [[mw:Special:MyLanguage/Article guidance|learn more]] about the project, and share their thoughts on [[mw:Special:MyLanguage/Talk:Article guidance|the talk page]].
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/49|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W49"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 1 décembre 2025 à 19:57 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29732328 -->
== Actualités techniques n° 2025-50 ==
<section begin="technews-2025-W50"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/50|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Anybody who wishes to secure their user account can now use [[m:Special:MyLanguage/Help:Two-factor authentication|two-factor authentication]] (2FA). This is available to all registered users of all Wikimedia projects. This is part of the [[mw:Special:MyLanguage/Product Safety and Integrity/Account Security|Account Security]] initiative. Later, 2FA will be required for all users who can take security- or privacy-sensitive actions.</span>
'''Actualités pour la contribution'''
* Suite aux déploiements de la semaine dernière, la fonctionnalité [[mw:Special:MyLanguage/Help:Growth/Tools/Add a link|Ajouter un lien]], qui permet aux rédacteurs de suggérer des liens lors de l’édition, sera disponible sur [[Phab:T410469|33 Wikipédias]] supplémentaires à partir du 9 décembre. Cette expansion est rendue possible grâce au nouveau modèle de prédiction qui prend désormais en charge toutes les langues, y compris celles qui n’étaient pas couvertes précédemment. Bien que la fonctionnalité soit disponible sur la plupart des Wikipédias depuis un certain temps, ce déploiement nous rapproche de l’utilisation du modèle amélioré partout. Si vous avez des questions ou souhaitez plus de détails, veuillez contacter [[mw:user:Trizek (WMF)|Trizek (WMF)]].
* La semaine dernière, [[mw:Special:MyLanguage/Wikimedia Search Platform|l'équipe de recherche Wikimedia]] a ajouté des suggestions de recherche [[:fr:Transcription et translittération|translittérées]] en temps réel aux wikis géorgiens. S'il n'y a que quelques suggestions de recherche habituelles, alors les requêtes en script latin ou cyrillique [[phab:T127003|sont maintenant réécrites en script géorgien]] pour trouver plus de correspondances. Par exemple, la recherche de <bdi lang="ka-Latn" dir="ltr">''bedniereba''</bdi> ou <bdi lang="ka-Cyrl" dir="ltr">''бедниереба''</bdi> suggérera désormais l'article existant sur <bdi lang="ka" dir="ltr">ბედნიერება</bdi> ("bonheur"). Vous pouvez recommander d'autres langues où les suggestions translittérées pourraient être utiles [[phab:T375215|sur Phabricator]] pour un développement futur.
* <span lang="en" dir="ltr" class="mw-content-ltr">Later this week, a controlled experiment will begin for editors on the 100 largest Wikipedias who are editing a section in the mobile web visual editor. 50% of these editors will notice a new "Edit full page" button that will enable them to expand their editing session to the whole page. This feature is intended to make it easier for people on mobile web to edit any article section, regardless of which section-edit icon they tapped to begin. The experiment will last ~4 weeks. You can find [[phab:T409112|more details]] about the project.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">Later this week, the [[mw:Special:MyLanguage/Readers/Reader Growth|Reader Growth team]] will launch a [[mw:Special:MyLanguage/Readers/Reader Growth/WE3.1.14 Expanded Mobile Sections|mobile web experiment]] to expand all article sections by default (currently they are collapsed by default) and pin the section header the user is currently reading to the top of the page. The experiment will affect 10% of users on Arabic, Chinese, French, Indonesian, and Vietnamese Wikipedias.</span> [https://phabricator.wikimedia.org/T409485]
* <span lang="en" dir="ltr" class="mw-content-ltr">The [[mw:Special:MyLanguage/Wikimedia Apps/Team/Wikipedia Year in Review/2025 Year in Review|Wikipedia Year in Review 2025]], a feature in the Wikipedia mobile apps (iOS and Android) that provides users with a personalised summary of their engagement with Wikipedia over the year, is now available on the iOS and Android apps. This edition includes expanded personalised insights, improved reading highlights, new donor messaging, and updated designs. Open the app to view your Year in Review and explore your reading journey from 2025.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">A recent software bug caused edits made with VisualEditor to make unintended changes to wikitext, including removing whitespace and replacing spaces with underscores in wikilinks inside citations. This was partially fixed last week, and further fixes are in progress. Editors who used VisualEditor between November 28 and December 2 should review their edits for unexpected modifications.</span> [https://phabricator.wikimedia.org/T411238]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]]. <span lang="en" dir="ltr" class="mw-content-ltr">For example, the incorrect handling of URLs copied from the address bar of Microsoft Edge users, has been resolved.</span> [https://phabricator.wikimedia.org/T341281]
'''Actualités pour la contribution technique'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Starting this week, users of the "{{int:codemirror-beta-feature-title}}" [[Special:Preferences#mw-prefsection-betafeatures|beta feature]] will have [[mw:Special:MyLanguage/Help:Extension:CodeMirror|CodeMirror]] as the editor for Lua, JavaScript, CSS, JSON and Vue content models, instead of [[mw:Special:MyLanguage/Extension:CodeEditor|CodeEditor]]. With this, the [[mw:Special:MyLanguage/Help:Extension:CodeMirror#Linting|linters]] will be upgraded. This is part of a larger effort to eventually replace CodeEditor and provide a consistent code editing experience.</span> [https://phabricator.wikimedia.org/T373711]
* <span lang="en" dir="ltr" class="mw-content-ltr">Developers are encouraged to take the [https://wikimediafoundation.limesurvey.net/552643 2025 Developer Satisfaction Survey], which remains open until 5 January 2026. If you build software for the Wikimedia ecosystem and would like to share your experiences or feedback, your participation is greatly appreciated.</span> [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/W4WBKO6Q55UWWCCSFWQATKEXBEHP3QNR/]
* Il n’y aura pas de nouvelle version de MediaWiki cette semaine.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/50|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W50"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 8 décembre 2025 à 18:45 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29738112 -->
== <span lang="en" dir="ltr">Tech News: 2025-51</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W51"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/51|Translations]] are available.
'''Updates for editors'''
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:18}} community-submitted {{PLURAL:18|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, one of the fixes addressed an issue for temporary accounts adding an external URL, which triggered an hCaptcha request in more cases than intended, and did not display the required popup on the first attempt to publish the edit. [https://phabricator.wikimedia.org/T411927]
'''Updates for technical contributors'''
* To improve database and site performance, external links to Wikimedia projects will no longer be stored in the database. This means they will not be searchable in [[{{#special:LinkSearch}}]], will not be checked by the Spam Blacklist or AbuseFilter as new links, and will not be in the <code dir=ltr>externallinks</code> table on database replicas. In the future this may be extended to other highly-linked trusted websites on a per-wiki basis, such as Creative Commons links on Wikimedia Commons. [https://phabricator.wikimedia.org/T405005]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.7|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/51|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W51"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 15 décembre 2025 à 20:02 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29796010 -->
== <span lang="en" dir="ltr">Tech News: 2025-52</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W52"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/52|Translations]] are available.
'''Updates for editors'''
* From January, edit filters [[mw:Special:MyLanguage/Extension:AbuseFilter/Access flags|can be set]] to automatically suppress their details such as rules and list of attempted edits and actions. This will help oversighters use edit filters to prevent doxxing or other suppressible material. [https://phabricator.wikimedia.org/T290324]
* The next issue of Tech News will be sent out on 12 January 2026 because of the end of year holidays. Thank you to all of the translators, and people who submitted content or feedback, this year.
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:16}} community-submitted {{PLURAL:16|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the crash that occurred when tapping "First Steps" in the Wikipedia Android Year in Review has now been fixed, and the feature opens as expected. [https://phabricator.wikimedia.org/T411546]
'''Updates for technical contributors'''
* Interface elements such as diffs and categories generated by MediaWiki used to have the attribute <code dir=ltr>data-mw="interface"</code> to distinguish from wiki content. The attribute has been replaced with <code dir=ltr>data-mw-interface=""</code>, to avoid potential conflicts with other <code dir=ltr>data-mw</code> attributes, which are generated by Parsoid. [https://phabricator.wikimedia.org/T409187]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] There is no new MediaWiki version this week or next week.
'''Meetings and events'''
* The [[mw:Wikimedia Hackathon Northwestern Europe 2026|Wikimedia Hackathon Northwestern Europe 2026]] will take place on 13-14 March 2026 in Arnhem, the Netherlands. Applications just opened mid-December and will close in mid-January or earlier if capacity is reached. With space for approximately 100 participants, early application is encouraged.
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/52|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W52"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 22 décembre 2025 à 22:45 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29831856 -->
2en3nci5wo061d9utacbdpfv52uotyo
Les cartes graphiques/Les unités de gestion de la géométrie
0
82199
763653
739198
2026-04-14T04:02:13Z
JackBot
14683
Robot : correction d’une double redirection vers [[Les cartes graphiques/Le pipeline géométrique : évolution]]
763653
wikitext
text/x-wiki
#REDIRECTION [[Les cartes graphiques/Le pipeline géométrique : évolution]]
pquowhpzwsnobkt2dwtc4plvodwi9ee
Les cartes graphiques/Les unités de gestion des primitives
0
82200
763654
739200
2026-04-14T04:02:13Z
JackBot
14683
Robot : correction d’une double redirection vers [[Les cartes graphiques/Le pipeline géométrique d'un GPU]]
763654
wikitext
text/x-wiki
#REDIRECTION [[Les cartes graphiques/Le pipeline géométrique d'un GPU]]
tjjmlifbeue8rjp9pf2mttiabg4nzea
Discussion Wikilivres:Le Bistro/2026
5
83406
763589
763044
2026-04-13T15:19:03Z
MediaWiki message delivery
36013
/* Tech News: 2026-16 */ nouvelle section
763589
wikitext
text/x-wiki
== Actualités techniques n° 2026-03 ==
<section begin="technews-2026-W03"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/03|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* La Fondation Wikimedia a publié des questions directrices pour son plan annuel de juillet 2026 à juin 2027 sur les plateformes [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2026-2027/Product & Technology OKRs|Meta]] et ''[[diffblog:2025/12/10/shaping-wikimedia-foundations-2026-2027-annual-goals-key-questions-for-the-wikimedia-movement/|Diff]]''. Celles-ci portent sur les tendances mondiales, une expérimentation plus rapide et plus constructive, un meilleur accompagnement des nouveaux contributeurs, le renforcement du rôle des éditeurs et des utilisateurs avancés, l'amélioration de la collaboration entre les projets, ainsi que le développement et la fidélisation du lectorat. Des commentaires et suggestions sont les bienvenus sur la [[m:Talk:Wikimedia Foundation Annual Plan/2026-2027|page de discussion]].
'''Actualités pour la contribution'''
* Dans le cadre des travaux en cours de l'équipe technique communautaire sur le projet [[m:Special:MyLanguage/Community Wishlist/W372|Listes de surveillance multiples]], l'affichage de [[Special:EditWatchlist|Modifier la liste de surveillance]] sera mis à jour entant que qu'une première étape vers la prise en charge de plusieurs listes de surveillance. De plus, la pagination de [[Special:Search|Recherche]] sera également mise à jour, dans le cadre du travail sur le souhait [[m:Special:MyLanguage/Community Wishlist/W186|Refonte de la pagination / navigation des pages]]. [https://phabricator.wikimedia.org/T411596]
* [[m:Special:GlobalWatchlist|La Liste de Surveillance Globale]] est une [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] de MediaWiki qui vous permet de voir vos listes de surveillance provenant de différents wikis sur la même page. Il a récemment été mis à jour pour ressembler davantage à la [[Special:Watchlist|Liste de surveillance]] régulière, par exemple en le préparant pour les comptes temporaires dans le masquage IP (y compris le réacheminement des liens des utilisateurs vers les pages de contributions), en mettant les titres de page en gras et en ouvrant les liens dans les résumés d'édition et les balises dans de nouveaux onglets du navigateur. [https://phabricator.wikimedia.org/T398361][https://phabricator.wikimedia.org/T298919][https://phabricator.wikimedia.org/T273526][https://phabricator.wikimedia.org/T286309]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:28|la tâche soumise|les {{formatnum:28}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:28||s}} la semaine dernière]]. Par exemple, le problème selon lequel les blocs globaux ne disposaient pas de l'option permettant de désactiver l'envoi d'e-mails a maintenant été résolu et sera disponible à l'utilisation à partir de la semaine du 13 janvier. [https://phabricator.wikimedia.org/T401293]
'''Actualités pour la contribution technique'''
* L'[[mw:Special:MyLanguage/VisualEditor/Citation tool|outil de citation VisualEditor]] et les [[mw:Special:MyLanguage/Help:Reference Previews|Aperçus de référence]] prennent désormais en charge "carte" comme type de référence. [https://phabricator.wikimedia.org/T411083]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.10|MediaWiki]]/[[mw:MediaWiki 1.46/wmf.11|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2026/03|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2026-W03"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 12 janvier 2026 à 20:33 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29907192 -->
== Thank You for Last Year – Join Wiki Loves Ramadan 2026 ==
Dear Wikimedia communities,
We hope you are doing well, and we wish you a happy New Year.
''Last year, we captured light. This year, we’ll capture legacy.''
In 2025, communities around the world shared the glow of Ramadan nights and the warmth of collective iftars. In 2026, ''Wiki Loves Ramadan'' is expanding, bringing more stories, more cultures, and deeper global connections across Wikimedia projects.
We invite you to explore the ''Wiki Loves Ramadan 2026'' [[m:Special:MyLanguage/Wiki Loves Ramadan 2026|Meta page]] to learn how you can participate and [[m:Special:MyLanguage/Wiki Loves Ramadan 2026/Participating communities|sign up]] your community.
📷 ''Photo campaign on '' [[c:Special:MyLanguage/Commons:Wiki Loves Ramadan 2026|Wikimedia Commons]]
If you have questions about the project, please refer to the FAQs:
* [[m:Special:MyLanguage/Wiki Loves Ramadan/FAQ/|Meta-Wiki]]
* [[c:Special:MyLanguage/Commons:Wiki Loves Ramadan/FAQ|Wikimedia Commons]]
''Early registration for updates is now open via the '''[[m:Special:RegisterForEvent/2710|Event page]]'''''
''Stay connected and receive updates:''
* [https://t.me/WikiLovesRamadan Telegram channel]
* [https://lists.wikimedia.org/postorius/lists/wikilovesramadan.lists.wikimedia.org/ Mailing list]
We look forward to collaborating with you and your community.
'''The Wiki Loves Ramadan 2026 Organizing Team''' 16 janvier 2026 à 20:44 (CET)
<!-- Message envoyé par User:ZI Jony@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=29879549 -->
== <span lang="en" dir="ltr">Tech News: 2026-04</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2026-W04"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/04|Translations]] are available.
'''Updates for editors'''
* The tray shown on [[Special:Diff|Special:Diff]] in mobile view has been redesigned. It is now collapsed by default, and incorporates a link to undo the edit being viewed, making it easier for mobile editors and reviewers to take action while keeping the interface uncluttered. [https://phabricator.wikimedia.org/T402297]
* [[m:Special:GlobalWatchlist|The Global Watchlist]] lets you view your watchlists from multiple wikis on one page. The [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] continues to improve — it now automatically determines the text direction (ensuring correct display of sites with unusual domain names) and shows detailed descriptions for log actions. Later this week, a new permanent link for page creations and CSS classes for each entry element will be added. [https://phabricator.wikimedia.org/T412505][https://phabricator.wikimedia.org/T287929][https://phabricator.wikimedia.org/T262768][https://phabricator.wikimedia.org/T414135]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:32}} community-submitted {{PLURAL:32|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the previously observed issue in Vector 2022, where anchor link targets were obscured by the sticky header, has now been addressed. [https://phabricator.wikimedia.org/T406114]
'''Updates for technical contributors'''
* As mentioned in the [[m:Special:MyLanguage/Tech/News/2025/44|October 2025 deprecation announcement]], MediaWiki Interfaces team will begin sunsetting all transform endpoints containing a trailing slash from the MediaWiki REST API the week of January 26. Changes are expected to roll out to all wikis on or before January 30th. All API users currently calling them are encouraged to transition to the non-trailing slash versions. Both endpoint variations can be found, compared, and tested using the [https://test.wikipedia.org/wiki/Special:RestSandbox REST Sandbox]. If you have questions or encounter any problems, please file a ticket in Phabricator to the [https://phabricator.wikimedia.org/project/view/6931/ #MW-Interfaces-Team board].
* Interactive reference documentation for the [[mw:Special:MyLanguage/Wikimedia REST API|Wikimedia REST API]] has moved. Requests to API docs previously hosted through [[mw:Special:MyLanguage/RESTBase|RESTBase]] (e.g.: <code dir=ltr>https://en.wikipedia.org/api/rest_v1/</code>) are now redirected to the [[w:en:Special:RestSandbox|REST Sandbox]].
* The [[mw:Special:MyLanguage/Wikidata Platform|WMF Wikidata Platform team]] (WDP) has published its [[d:Special:MyLanguage/Wikidata:Wikidata Platform team/Newsletter|January 2026 newsletter]]. It includes updates on the legacy full-graph endpoint decommissioning, the User-Agent policy change, the monthly Blazegraph migration office hours, and efforts to reduce regressions caused by the legacy endpoint shutdown. As a reminder, you can [[m:Special:MyLanguage/Global message delivery/Targets/WDP team updates|subscribe to the WDP newsletter]]!
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.12|MediaWiki]]
'''Meetings and events'''
* The [[mw:Wikimedia Hackathon Northwestern Europe 2026|Wikimedia Hackathon Northwestern Europe 2026]] will take place on 13-14 March 2026 in Arnhem, the Netherlands. Applications opened mid-December and will close soon or when capacity is reached. It's a two-day, technically oriented hackathon bringing together Wikimedians from the region. Hope to see you there!
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2026/04|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2026-W04"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 19 janvier 2026 à 21:29 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29943403 -->
== Révision annuelle du code universel de conduite et des lignes directrices de l'application ==
<section begin="announcement-content" />
Nous vous informons que la période de relecture annuelle du Code de conduite universel et des règles d'applications est actuellement ouverte. Vous pouvez faire vos commentaires sur les modifications que vous souhaitez apporter jusqu'au 9 février 2026. C'est la première d'une série d'étapes nécessaires pour la révision annuelle. Vous trouverez [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|d'autres informations et les discussions auxquelles participer sur la page UCoC de Meta]].
Le [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Comité de coordination du code universel de conduite]] (U4C — Universal Code of Conduct Coordinating Committee) est un groupe global dont le rôle est de fournir une implémentation équitable et cohérente de l'UCoC. Cette relecture annuelle a été envisagée et mise en place par l'U4C. Pour plus d'informations et les responsabilités de l'U4C, veuillez lire la [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|Charte de l'U4C]].
Veuillez partager ces informations avec les autres membres concernés de votre communauté.
-- En coopération avec l'U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|discussion]])<section end="announcement-content" />
19 janvier 2026 à 22:01 (CET)
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 -->
== Actualités techniques n° 2026-05 ==
<section begin="technews-2026-W05"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/05|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* La Fondation Wikimedia invite à donner des commentaires sur [[m:Special:MyLanguage/Product and Technology Advisory Council/Year1 Reflections and Proposed Way Forward 2026 Update|l’avenir proposé]] du [[:m:Special:MyLanguage/Product and Technology Advisory Council|Conseil consultatif des produits et technologies]] jusqu’au 28 février.
* Tous les utilisateurs disposant d'un compte enregistré peuvent désormais utiliser des clés d'accès pour la [[m:Special:MyLanguage/Help:Two-factor authentication|double authentification]] (2FA). Les clés d'accès sont un moyen simple de se connecter sans utiliser un second appareil. Elles vérifient l'identité de l'utilisateur à l'aide d'une empreinte digitale, d'une reconnaissance faciale ou d'un code PIN. Pour configurer une clé d'accès, configurez d'abord une méthode 2FA classique. Actuellement, pour se connecter avec une clé d'accès, les utilisateurs doivent également utiliser un mot de passe. Plus tard ce trimestre, la connexion sans mot de passe permettra aux utilisateurs de se connecter d'un simple clic avec une clé d'accès. Les utilisateurs disposant de droits avancés devront également avoir la 2FA activée. Cela fait partie du projet [[mw:Special:MyLanguage/Product Safety and Integrity/Account Security|Sécurité du compte]].
* Les contributeurs non enregistrés sur des IP bloquées ou des plages d'IP bloquées peuvent désormais interagir sur le wiki pour faire appel d'un blocage en créant un compte temporaire afin de contester un blocage sur la page de discussion de l'utilisateur, sauf si l'option « empêcher cet utilisateur de modifier sa propre page de discussion » est activée. Cela résout le problème des utilisateurs déconnectés incapables d'utiliser le processus de déblocage par défaut via la page de discussion de l'utilisateur. [https://phabricator.wikimedia.org/T398673]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:20|la tâche soumise|les {{formatnum:20}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:20||s}} la semaine dernière]]. Par exemple, la description des méthodes d'authentification à deux facteurs (2FA) sur la page de gestion a été mise à jour. Il est désormais plus clair et plus facile pour les utilisateurs à comprendre et à utiliser. [https://phabricator.wikimedia.org/T332385]
'''Actualités pour la contribution technique'''
* Une nouvelle variable AbuseFilter, <code>account_type</code>, a été ajoutée pour fournir un moyen fiable de déterminer le type de compte créé dans les actions <code>createaccount</code> et <code>autocreateaccount</code>. Dans le cadre de ce changement, la variable <code>accountname</code> a été renommée en <code>account_name</code>, et <code>accountname</code> est désormais obsolète. Les gestionnaires de filtres doivent mettre à jour tous les filtres qui utilisent des vérifications de type de compte codées en dur ou la variable obsolète. [https://phabricator.wikimedia.org/T414049]
* Les vignettes d'images demandées dans des tailles non standard, et en utilisant des méthodes non standard telles que les requêtes directes à <code dir=ltr><nowiki>upload.wikimedia.org/…</nowiki></code>, cesseront de fonctionner dans un proche avenir. Ce changement vise à prévenir les abus externes continus par des robots et des aspirateurs web. Certains utilisateurs ayant des CSS/JS personnalisés, les administrateurs d'interface qui peuvent corriger les gadgets et les thèmes locaux, ainsi que les auteurs d'outils, devront mettre à jour leur code pour utiliser des tailles de vignettes standard. [[phab:T414805|Des détails, des liens de recherche et des exemples de correction sont disponibles dans la tâche]].
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.13|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2026/05|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2026-W05"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 26 janvier 2026 à 22:17 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29969530 -->
== <span lang="en" dir="ltr">Tech News: 2026-06</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2026-W06"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/06|Translations]] are available.
'''Updates for editors'''
* The "{{int:pageinfo-toolboxlink}}" feature, which gives validating information about a page ([{{fullurl:{{FULLPAGENAME}}|action=info}} example]), now automatically includes a table of contents. If there is a local [[{{ns:8}}:Pageinfo-header]] page created by individual users, it can now be removed. [https://phabricator.wikimedia.org/T363726]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:21}} community-submitted {{PLURAL:21|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, VisualEditor previously added bold or italic formatting inside link descriptions, making the wikicode complex. This has now been fixed. [https://phabricator.wikimedia.org/T409669]
'''Updates for technical contributors'''
* There was no XML dump on 20 January. Additionally, from now on, dumps will be generated once per month only. [https://phabricator.wikimedia.org/T414389]
* The MediaWiki Interfaces team removed support for all transform endpoints containing a trailing slash from the [https://www.mediawiki.org/wiki/Special:MyLanguage/API:REST%20API MediaWiki REST API]. All API users currently calling those endpoints are encouraged to transition to the non-trailing slash versions. If you have questions or encounter any problems, please file a ticket in phabricator to the [https://phabricator.wikimedia.org/project/view/6931/ #MW-Interfaces-Team board].
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.14|MediaWiki]]
'''Weekly highlight'''
* Users are reminded that the Wikimedia Foundation has shared some guiding questions for the July 2026–June 2027 Annual Plan on [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2026-2027/Product & Technology OKRs|Meta]] and ''[[diffblog:2025/12/10/shaping-wikimedia-foundations-2026-2027-annual-goals-key-questions-for-the-wikimedia-movement/|Diff]]''. These focus on global trends, faster and healthier experimentation, better support for newcomers, strengthening editors and advanced users, improving collaboration across projects, and growing and retaining readership. Feedback and ideas are welcome on the [[m:Talk:Wikimedia Foundation Annual Plan/2026-2027|talk page]].
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2026/06|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2026-W06"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 2 février 2026 à 18:43 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30000986 -->
== Actualités techniques n° 2026-07 ==
<section begin="technews-2026-W07"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/07|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* [[File:Maki-gift-15.svg|12px|link=|class=skin-invert|Concerne un souhait]] Les contributeurs connectés qui gèrent de grandes ou complexes listes de suivi peuvent désormais organiser et filtrer les pages surveillées de manière à améliorer leurs flux de travail grâce à la nouvelle fonctionnalité [[mw:Special:MyLanguage/Help:Watchlist labels|Étiquettes de liste de suivi]]. En ajoutant des étiquettes personnalisées (par exemple : pages que vous avez créées, pages surveillées pour vandalisme, ou pages de discussion), les utilisateurs peuvent identifier plus rapidement ce qui nécessite une attention, réduire la charge cognitive et répondre plus efficacement. Cela améliore l'utilisabilité de la liste de suivi, en particulier pour les éditeurs très actifs.
* Une nouvelle fonctionnalité disponible sur [[Special:Contributions|Special:Contributions]] montre [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|des comptes temporaires]] qui sont probablement utilisés par la même personne, et rend ainsi le patrouillage moins chronophage. En vérifiant les contributions d'un compte temporaire, les utilisateurs ayant accès aux adresses IP des comptes temporaires peuvent désormais avoir une vue des contributions des comptes temporaires associés. La fonctionnalité recherche toutes les adresses IP associées à un compte temporaire donné pendant la période de conservation des données et affiche toutes les contributions de tous les comptes temporaires ayant utilisé ces adresses IP. [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts#February 2026: Improvements to the patroller tooling|Plus...]] [https://phabricator.wikimedia.org/T415674]
* Lorsque les éditeurs prévisualisent une modification de wikitexte, la boîte de rappel indiquant qu'ils ne voient qu'une prévisualisation (qui est affichée en haut) a désormais un fond gris/neutre au lieu d'un fond jaune/d'avertissement. Cela facilite la distinction entre les notes de prévisualisation et les avertissements réels (par exemple, les conflits de modification ou les cibles de redirection problématiques), qui seront désormais affichés dans des boîtes d'avertissement ou d'erreur séparées. [https://phabricator.wikimedia.org/T414742]
* La [[m:Special:GlobalWatchlist|Liste de suivi globale]] vous permet de consulter vos listes de suivi provenant de plusieurs wikis sur une seule page. L' [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] continue de s'améliorer — elle prend désormais en charge correctement plus d'un site Wikibase, par exemple à la fois [[d:|Wikidata]] et [[testwikidata:|testwikidata]]. De plus, des problèmes concernant la direction du texte ont été résolus pour les utilisateurs qui préfèrent Wikidata ou d'autres sites Wikibase dans des langues de droite à gauche (RTL). [https://phabricator.wikimedia.org/T415440][https://phabricator.wikimedia.org/T415458]
* <span lang="en" dir="ltr" class="mw-content-ltr">The automatic "magic links" for ISBN, RFC, and PMID numbers have been [[mw:Special:MyLanguage/Help:Magic links|deprecated in wikitext since 2021]] due to inflexibility and difficulties with localization. Several wikis have successfully replaced RFC and PMID magic links with equivalent external links, but a template was often required to replace the functionality of the ISBN magic link. There is now a new [[mw:Special:MyLanguage/Help:Magic words#isbn|built-in parser function]] <code dir=ltr><nowiki>{{#isbn}}</nowiki></code> available to replace the basic functionality of the ISBN magic link. This makes it easier for wikis who wish to migrate off of the deprecated magic link functionality to do so.</span> [https://phabricator.wikimedia.org/T145604]
* Deux nouveaux wikis ont été créés :
** un {{int:project-localized-name-group-wikipedia}} dans [[d:Q35401|Jju]] ([[w:kaj:|<code>w:kaj:</code>]]) [https://phabricator.wikimedia.org/T413283]
** un {{int:project-localized-name-group-wikipedia}} dans [[d:Q1186896|Nawat]] ([[w:ppl:|<code>w:ppl:</code>]]) [https://phabricator.wikimedia.org/T413273]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Un nouveau groupe d'utilisateurs global a été créé : [[{{int:grouppage-local-bot}}|{{int:group-local-bot}}]]. Il sera utilisé en interne par le logiciel pour permettre aux robots communautaires de contourner les limites de débit appliquées aux [[w:en:Web_scraping|web scrapers]] abusifs. Les comptes approuvés en tant que robots sur au moins un wiki Wikimedia seront automatiquement ajoutés à ce groupe. Cela ne changera pas les autorisations dont dispose le robot. [https://phabricator.wikimedia.org/T415588]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.15|MediaWiki]]
'''Rencontres et évènements'''
* La [[mw:Special:MyLanguage/MediaWiki Users and Developers Conference Spring 2026|Conférence des utilisateurs et des développeurs de MediaWiki, Printemps 2026]] se tiendra du 25 au 27 mars à Salt Lake City, États-Unis. Cet événement est organisé par et pour la communauté MediaWiki de tiers. Vous pouvez proposer des sessions et vous inscrire pour y assister. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/AZBWVI46SDEB65PGR5J6E4TYOQQEZXM7/]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2026/07|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2026-W07"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 10 février 2026 à 00:30 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30026671 -->
== Actualités techniques n° 2026-08 ==
<section begin="technews-2026-W08"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/08|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* <span class="mw-translate-fuzzy">L'[[mw:Special:MyLanguage/Wikimedia Site Reliability Engineering|équipe SRE]] va procéder au nettoyage d'[[m:Special:MyLanguage/Etherpad|Etherpad]], l'éditeur web open source de documents collaboratifs en temps réel. Tous les blocs-notes seront définitivement supprimés après le 30 avril 2026 – si des projets de migration sont encore en cours à cette date, l'équipe pourra réexaminer la date au cas par cas. Veuillez effectuer des sauvegardes locales de tout contenu que vous souhaitez conserver, car les données supprimées ne pourront pas être récupérées. Ce nettoyage permet de réduire la taille de la base de données et l'empreinte de l'infrastructure. Etherpad continuera de prendre en charge la collaboration en temps réel, mais le stockage à long terme n'est plus assuré. D'autres nettoyages pourront avoir lieu ultérieurement sans préavis.</span> [https://phabricator.wikimedia.org/T415237]
'''Actualités pour la contribution'''
* L'équipe de Recherche d'Informations lancera une [[mw:Special:MyLanguage/Readers/Information Retrieval/Phase 1|expérimentation sur l'application mobile Android]], afin de tester des fonctionnalités de recherche hybrides capables de gérer à la fois les requêtes sémantiques et par mots-clés. L'amélioration de la recherche sur la plateforme permettra aux lecteurs de trouver plus facilement ce qu'ils cherchent, directement sur Wikipédia. L'expérimentation sera d'abord lancée sur Wikipédia en grec fin février, puis sur les versions anglaise, française et portugaise en mars. [https://diff.wikimedia.org/2026/01/08/semantic-search-making-it-easier-to-find-the-information-readers-want/ En savoir plus] sur le blog ''Diff''. [https://www.mediawiki.org/wiki/Readers/Information_Retrieval]
* L'équipe « Croissance des lecteurs » mènera [[mw:Special:MyLanguage/Readers/Reader Growth/WE3.10.2 Mobile Table of Contents|une expérience]] auprès des utilisateurs de la version mobile du site web qui ajoute une table des matières et développe automatiquement toutes les sections des articles, afin de mieux comprendre les problèmes de navigation qu'ils rencontrent. Le test sera disponible sur les versions arabe, chinoise, anglaise, française, indonésienne et vietnamienne de Wikipedia.
* Auparavant, les notifications ([[{{ns:8}}:Sitenotice]] et [[{{ns:8}}:Anonnotice]]) du site ne s'affichaient que sur la version ordinateur. Maintenant, elles s'afficheront désormais sur toutes les plateformes. Les utilisateurs mobiles verront ces notifications. Les administrateurs du site doivent être prêts à tester et à corriger les notifications sur les appareils mobiles afin d'éviter toute interférence avec les articles. Pour désactiver ces notifications, les administrateurs d'interface peuvent ajouter <code dir="ltr">#siteNotice { display: none; }</code> à [[{{ns:8}}:Minerva.css]]. [https://phabricator.wikimedia.org/T138572][https://phabricator.wikimedia.org/T416644]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:19|la tâche soumise|les {{formatnum:19}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:19||s}} la semaine dernière]]. Par exemple, un problème concernant la section ''[[Special:RecentChanges|Spécial:Modifications récentes]]'' a été résolu. Auparavant, cliquer sur « Masquer » dans les filtres actifs entraînait la disparition du bouton « Afficher les nouvelles modifications depuis… », alors qu'il aurait dû rester visible. Ce bouton fonctionne désormais correctement. [https://phabricator.wikimedia.org/T406339]
'''Actualités pour la contribution technique'''
* Une nouvelle documentation est désormais disponible pour aider les rédacteurs à déboguer les fonctionnalités de recherche interne. Elle facilite le dépannage lorsque des pages n'apparaissent pas dans les résultats, lorsque le classement semble inattendu et lorsqu'il est nécessaire d'inspecter le contenu indexé, ce qui permet de mieux comprendre et d'analyser le comportement de la recherche. [[mw:Help:CirrusSearch/Debug|En savoir plus]]. [https://phabricator.wikimedia.org/T411169]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.16|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2026/08|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2026-W08"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 16 février 2026 à 20:17 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30086330 -->
== <span lang="en" dir="ltr">Tech News: 2026-09</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2026-W09"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/09|Translations]] are available.
'''Weekly highlight'''
* [[mw:Special:MyLanguage/Edit check/Reference Check|Reference Check]] has been deployed to English Wikipedia, completing its rollout across all Wikipedias. The feature prompts newcomers to add a citation before publishing new content, helping reduce common citation-related reverts and improve verifiability. In A/B testing, the impact was substantial: newcomers shown Reference Check were approximately 2.2 times more likely to include a reference on desktop and about 17.5 times more likely on mobile web. [https://analytics.wikimedia.org/published/reports/editing/reference_check_ab_test_report_final_2025.html]
'''Updates for editors'''
* The [[mw:Special:MyLanguage/Extension:InterwikiSorting|InterwikiSorting extension]], which allowed for the [[m:Special:MyLanguage/Interwiki sorting order|sorting of interwiki links]], has been undeployed from Wikipedia. As a result, editors who had enabled interwiki link sorting in non-compact mode (full list format) will now see links reordered. The links moving forward will be listed in the alphabetical order of language code. [https://phabricator.wikimedia.org/T253764]
* Later this week, people who are editing a page-section using the mobile visual editor, will notice a new "Edit full page" button. When tapped, you will be able to edit the entire article. This helps when the change you want to make is outside the section you initially opened. [https://phabricator.wikimedia.org/T387175][https://phabricator.wikimedia.org/T409112]
* [[mw:Special:MyLanguage/Readers/Reader Experience|The Reader Experience team]] is inviting editors to assess whether dark mode should still be considered "beta" on their wiki, based on their experience of how well it functions on desktop and mobile. If the feature is deemed mature, editors can update the interface messages in <code dir=ltr>MediaWiki:skin-theme-description</code> and <code dir=ltr>MediaWiki:Vector-night-mode-beta-tag</code> to indicate that dark mode is ready and no longer considered beta.
* The improved [[mw:Wikimedia_Apps/Team/iOS/Activity_Tab|Activity tab]] which displays user-insights is now available to all users of the Wikipedia iOS app (version 7.9.0 and later). Following earlier A/B testing that showed higher account creation among users with access to the feature, it has been rolled out to 100% of users along with some updates. The Activity tab now shows your edited articles in the timeline, offers editing impact insights like contribution counts and article view trends, and customization options to improve in-app experience for users.
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:21}} community-submitted {{PLURAL:21|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, a bug that prevented [[mw:Special:MyLanguage/Extension:DiscussionTools|DiscussionTools]] from working on mobile has now been fixed, restoring full functionality. [https://phabricator.wikimedia.org/T415303]
'''Updates for technical contributors'''
* The [[m:Special:GlobalWatchlist|Global Watchlist]] lets you view your watchlists from multiple wikis on one page. The [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] that makes this possible continues to improve. The latest upgrade is the inclusion of a [[mw:Extension:GlobalWatchlist#hook|new hook]], <code dir=ltr>ext.globalwatchlist.rebuild</code>, which fires after each watchlist rebuild. This allows you to run gadgets and user scripts for the Special page. [https://phabricator.wikimedia.org/T275159]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.17|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2026/09|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2026-W09"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 23 février 2026 à 20:03 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30119102 -->
== Actualités techniques n° 2026-10 ==
<section begin="technews-2026-W10"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/10|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Le [[m:Special:MyLanguage/Wikipedia 25/Easter egg experiments|mode Anniversaire]] Wikipedia 25 est maintenant disponible sur Wikipédia en français, anglais, betawi, breton, chinois, espagnol, gorontalo, indonésien, italien, luxembourgeois, madurais, néerlandais, sicilien, tchèque, thaï et vietnamien ! Cette campagne à temps limitée célèbre 25 ans de Wikipédia avec une mascotte : « Baby Globe », disponible sous la forme d'un réglage. Lorsque ce réglage est activé, Baby Globe est montrée sur [[m:Special:MyLanguage/Wikipedia 25/Easter egg experiments/article configuration|environ 2 500 articles]], attendant d'être découverte par des lecteurs. Chaque communauté peut choisir d'activer le mode Anniversaire par consensus et en demandant à un administrateur de le rendre disponible et de le personaliser via une [[m:Special:MyLanguage/Wikipedia 25/Easter egg experiments#Community Configuration Demo|configuration]] sur le wiki local.
'''Actualités pour la contribution'''
* Le [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing|sous-référencement]], une nouvelle fonctionalité pour réutiliser des références avec des détails différents est maintenant disponible sur Wikipédia en suédois, polonais et [[:phab:T418209|quelques autres]]. Vous pouvez [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing#test|essayer la fonctionalité]] sur ces projets ou sur testwiki et [https://en.wikipedia.beta.wmcloud.org/wiki/Sub-referencing betawiki]. Les retours des premiers essais sur Wikipédia en allemand ont été [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing/Learnings|publiés dans un rapport]]. Contactez l'équipe de Wikimédia Allemagne si vous êtes [[:m:Talk:WMDE Technical Wishes/Sub-referencing#Pilot wikis|intéressés pour devenir un wiki pilote]].
* La [[mw:Special:MyLanguage/Help:Edit check#Paste check|vérification du collage clavier]] sera disponible sur tous les Wikipédias cette semaine. Cette fonctionalité avertit les nouveaux contributeurs qui collent du texte qu'ils n'ont probablement pas écrit de vérifier si laisser celui-ci risque de causer une violation du droit d'auteur. La vérification du collage clavier [[mw:Special:MyLanguage/Edit check/Tags|marque]] toutes les modifications où l'avertissement a été montré pour permettre leur vérification. Les administrateurs locaux peuvent configurer les différents aspects de cette fonctionalité à travers [[{{#special:EditChecks}}]]. Des [[mw:Special:MyLanguage/Edit check/Paste Check#A/B Experiment|études]] sur 22 wikis ont montré que cette vérification permet une réduction de 18% des annulations comparé au groupe de contrôle. Les traducteurs peuvent [https://translatewiki.net/w/i.php?title=Special%3ATranslate&group=ext-visualeditor-ve-mw-editcheck&filter=&optional=1&action=translate aider à traduire] cette fonctionalité.
* <span lang="en" dir="ltr" class="mw-content-ltr">The [[mw:Special:MyLanguage/Readers/Reader Experience|Reader Experience team]] will be standardizing the user menu in the top right for all mobile users so that it is closer to the desktop experience. Currently this user menu is only visible to users with Advanced Mobile Controls (AMC) turned on. The only change is that a couple buttons previously in the left-side menu will move to the top right for users who do not have AMC turned on. This change is expected to go out March 9 and seeks to improve the user interface.</span> [https://phabricator.wikimedia.org/T413912]
* À partir de la semaine du 2 mars, les emails envoyés lorsqu'une adresse email a été ajoutée, supprimée ou changée pour un compte changera pour adopter un formattage HTML beaucoup plus agréable et plus clair que le texte brut précédent. [https://phabricator.wikimedia.org/T410807]
* Les notifications sont actuellement limitées à 2 000 entrées historiques par utilisateur et remontent à 2013 lorsque la fonctionnalité a été publiée. Le système va être modifié pour ne stocker que les notifications des 5 dernières années, mais jusqu'à 10 000 d'entre elles. Cela contribuera à la santé à long terme des infrastructures et à empêcher que les notifications plus récentes disparaissent trop tôt. [https://phabricator.wikimedia.org/T383948]
* <span lang="en" dir="ltr" class="mw-content-ltr">The [[m:Special:GlobalWatchlist|Global Watchlist]] which lets you view your watchlists from multiple wikis on a single page continues to see improvements. The latest update improves label usage experience. The [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] now allows activating the [[mw:Special:MyLanguage/Manual:Language#Fallback languages|language fallback system]] for Wikidata items without labels in the viewed language, and showing those labels in the user’s preferred Wikidata language if no <code dir=ltr>uselang=</code> URL parameter is provided.</span> [https://phabricator.wikimedia.org/T373686][https://phabricator.wikimedia.org/T416111]
* L'équipe Wikipédia Android a commencé un test beta de la [[mw:Special:MyLanguage/Readers/Information Retrieval/Phase 1|recherche hybride]] sur Wikipédia en grec. Cette recherche hybride supporte les requêtes sémantique et par mot clés, permettant aux utilisateurs de trouver ce qu'ils cherchent plus facilement.
* Pour des raisons de sécurité, les membres de certains groupes sont [[m:Special:MyLanguage/Mandatory two-factor authentication for users with some extended rights|forcés d'avoir la double authentification]] (A2F) d'activée. Actuellement, l'A2F n'est nécessaire que pour utiliser les droits du groupe, et non pour en faire partie. Vu que ce système admet certaines failles, il sera [[phab:T418580|changé graduellement en mars]]. Les membres de ces groupes ne pourront plus désactiver la dernière méthose d'A2F sur leur compte, et il sera impossible d'ajouter des utilisateurs sans A2F à ces groupes. Il sera toujours possible de rajouter d'autres méthodes d'authentification et d'en enlever, tant qu'une est toujours activée. Dans la seconde moitié de mars, les utilisateurs sans A2F seront retirés de ces groupes. Cela s'applique aux administrateurs CentralNotice, aux vérificateurs d'utilisateurs, aux administrateurs d'interface, aux masqueurs, aux staff de Wikidata et Wikifonctions ainsi qu'aux bureaux IT et Confiance et sécurité de la WMF. Rien ne changera pour les autres utilisateurs. Voir la tâche liée pour le calendrier de déploiement. [https://phabricator.wikimedia.org/T418580]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]]. Par exemple, le problème empêchant les utilisateurs de créer une instance dans [https://www.wikibase.cloud/ Wikibase.cloud] a maintenant été résolu. [https://phabricator.wikimedia.org/T416807]
'''Actualités pour la contribution technique'''
* <span lang="en" dir="ltr" class="mw-content-ltr">To help ensure [[mw:Special:MyLanguage/MediaWiki Product Insights/Responsible Reuse|fair use of infrastructure]], over the next month the Wikimedia Foundation will implement global API rate limits across our APIs. In early March, stricter limits will be applied to unidentified requests from outside Toolforge/WMCS and API requests that are made from web browsers. In April, higher limits will be applied to identified traffic. These limits are intentionally set as high as possible to minimise impact on the community. Bots running in Toolforge/WMCS or with the bot user right on any wiki should not be affected for now. However, all developers are advised to follow updated best practices. For more information, see [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits|Wikimedia APIs/Rate limits]].</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">The Wikidata Query Service Linked Data Fragment (LDF) endpoint will be decommissioned in February. This endpoint served limited traffic, which was successfully migrated to other data access methods that were better suited to support existing use cases. The hardware used to support the LDF endpoint will be reallocated to support the ongoing backend migration efforts.</span> [https://phabricator.wikimedia.org/T415696]
* Le nouvel analyseur syntaxique Parsoid [[mw:Special:MyLanguage/Parsoid/Parser Unification/Updates|continue d'être déployés sur plus de wikis]], améliorant la pérennité de la platforme et rendant plus facile l'ajout de nouvelles fonctionalités de lecture et de modification. Parsoid est maintenant l'analyseur par défaut sur 488 wikis de la WMF (268 Wikipédias), couvrant plus de 10% de toutes les lectures de pages Wikipédia.
* Le processus et les critères pour [[Special:MyLanguage/Wikimedia Enterprise#Access|demander un accès exceptionnel]] au flux à fort volume de l'API ''Wikimédia Entreprise'' (sans coût pour des utilisations en rapport à notre mission) [[m:Talk:Wikimedia Enterprise#Exceptional access criteria|ont maintenant été publiés]]. Notre but est de donner une documentation plus claire et plus complète aux utilisateurs.
* [https://techblog.wikimedia.org/ Le blog Tech], dédié à la communité technique de Wikimédia [https://techblog.wikimedia.org/2026/02/24/a-tech-blog-diff/ va migrer] vers [[diffblog:|Diff]], le blog pour les nouvelles et événements de la communauté. La migration devrait être terminée en Avril 2026, après quoi les nouveaux posts seront acceptés pour être publiés. Les lecteurs pourront lire les posts - anciens ou nouveaux - sur https://diff.wikimedia.org/.
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.18|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2026/10|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2026-W10"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 2 mars 2026 à 18:51 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30137798 -->
== <span lang="en" dir="ltr">Tech News: 2026-11</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2026-W11"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/11|Translations]] are available.
'''Weekly highlight'''
* [[m:Special:MyLanguage/Tech/Server switch|All wikis will be read-only]] for a few minutes on Wednesday, 25 March 2026 at [https://zonestamp.toolforge.org/1774450800 15:00 UTC]. This is for the datacenter server switchover backup tests, [[wikitech:Deployments/Yearly calendar|which happen twice a year]]. During the switchover, all Wikimedia website traffic is shifted from one primary data center to the backup data center to test availability and prevent service disruption even in emergencies.
* Last week, all wikis had 2 hours of read-only time, and extended unavailability for user-scripts and gadgets. This was due to a security incident which has since been resolved. Work is ongoing to prevent re-occurrences. For current information please see the [[m:Steward's noticeboard#Statement on Meta about today's user script security incident|post on the Stewards' noticeboard]] ([[m:Special:MyLanguage/Wikimedia Foundation/Product and Technology/Product Safety and Integrity/March 2026 User Script Incident|translations]]).
'''Updates for editors'''
* Users facing multiple blocks on mobile will now see the reasons for each block separately, instead of a generic message. This helps them understand why they are blocked and what steps they can take to resolve the issue. For example, users affected for using common VPNs (such as [[Special:MyLanguage/Apple iCloud Private Relay|iCloud Private Relay]]) will receive clearer guidance on what they need to do to start editing again. [https://phabricator.wikimedia.org/T357118]
* Later this week, [[mw:Special:MyLanguage/VisualEditor/Suggestion Mode|Suggestion Mode]] will become available as a beta feature within the visual editor at all Wikipedias. This feature proactively suggests various types of actions that people can consider taking to improve Wikipedia articles, and learn about related guidelines. The feature is locally configurable, and can also be locally expanded with custom Suggestions. Current settings can be seen at [[Special:EditChecks]] and there are [[mw:Special:MyLanguage/Help:Suggestion mode#For administrators %E2%80%93 local customization|instructions for how administrators can customize]] the links to point to local guidelines. The feature is connected to [[mw:Special:MyLanguage/Help:Edit check|Edit check]] which suggests improvements while someone is writing new content. In the future, the Editing team plans to evaluate the feature's impact with newcomers through a controlled experiment. [https://phabricator.wikimedia.org/T404600]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:23}} community-submitted {{PLURAL:23|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the issue where the cursor became misaligned during the use of CodeMirror’s syntax highlighting, which makes wikitext and code easier to read, has now been fixed. This problem specifically affected users who defined a font rule in a custom stylesheet while creating a new topic with DiscussionTools. [https://phabricator.wikimedia.org/T418793]
'''Updates for technical contributors'''
* API rate limiting update: To help ensure [[mw:Special:MyLanguage/MediaWiki Product Insights/Responsible Reuse|fair use of infrastructure]], global API rate limits will be applied this week to requests without a compliant User-Agent that originate from outside Toolforge/WMCS and to unauthenticated requests made from web browsers. Higher limits will be applied to identified traffic in April. Bots running in Toolforge/WMCS or with the bot user right on any wiki should not be affected for now. However, all developers are advised to follow updated best practices. For more information, see [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits|Wikimedia APIs/Rate limits]].
* The new GraphQL API has been released. The API was developed as a flexible alternative to select features of the Wikidata Query Service (WDQS), to improve developer experience and foster adaptability, and efficient data access. Try it out and [[d:Wikidata:Wikibase GraphQL#Feedback and development|give feedback]]. You can also [https://greatquestion.co/wikimediadeutschland/GraphQLAPI/apply sign up for usability tests].
* The [[m:Special:MyLanguage/Product and Technology Advisory Council/Unsupported Tools Working Group|PTAC Unsupported Tools Working Group]] continued improvements to [[commons:Special:MyLanguage/Commons:Video2commons#|Video2Commons]] in February, with fixes addressing authentication errors, large-file handling, task queue visibility, and clearer upload behavior. Work is still ongoing in some areas, including changes related to deprecated server-side uploads. Read [[m:Special:MyLanguage/Product and Technology Advisory Council/Unsupported Tools Working Group#February 2026|this update]] to learn more.
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.19|MediaWiki]]
'''In depth'''
* The Article Guidance team invites experienced Wikipedia editors from selected [[mw:Special:MyLanguage/Article guidance/Pilot wikis and collaborators#Collaborators|pilot wikis]] and interested contributors from other Wikipedias to fill out this questionnaire which is available in [https://docs.google.com/forms/d/e/1FAIpQLSfmLeVWnxmsCbPoI_UF2jyRcn73WRGWCVPHzerXb4Cz97X_Ag/viewform English], [https://docs.google.com/forms/d/e/1FAIpQLSd6rzr4XXQw8r4024fE3geTPFe13M_6w7Mitj-YJi0sOlWTAw/viewform?usp=header Arabic], [https://docs.google.com/forms/d/e/1FAIpQLSdok3-RfB18lcugYTUMGkpwmqG_8p760Wv4dCXitOXOszjUDw/viewform?usp=header Bengali], [https://docs.google.com/forms/d/e/1FAIpQLSfjTfYp4jEo0akA4B1e-Nfg3QZPCudUjhJzHzzDi6AHyAaMGA/viewform?usp=header Japanese], [https://docs.google.com/forms/d/e/1FAIpQLScteVoI29Aue4xc72dekk-6RYtvmMgQxzMI900UOawrFrSTWg/viewform?usp=header Portuguese], [https://docs.google.com/forms/d/e/1FAIpQLSetdxnYwL3ub2vqA7awCg5hJZPMIYcDPaiTe12rY9h0GYnVlw/viewform?usp=header Persian], and [https://docs.google.com/forms/d/e/1FAIpQLScNvfJF-Ot-4pzA4qAN771_0QDJ4Li19YcUsaTgSKW8Nc7U_Q/viewform?usp=header Turkish]. Your answers will help the team customize guidance for less experienced editors and help them learn community policies and practices while creating an article. Learn more [[mw:Special:MyLanguage/Article guidance|on the project page]].
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2026/11|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2026-W11"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 9 mars 2026 à 19:52 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30213008 -->
== <span lang="en" dir="ltr">Tech News: 2026-12</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2026-W12"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/12|Translations]] are available.
'''Updates for editors'''
* The [[mw:Special:MyLanguage/Help:Extension:CodeMirror|{{int:codemirror-beta-feature-title}}]] beta feature, also known as [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror 6]], has been used for wikitext syntax highlighting since November 2024. It will be promoted out of beta by May 2026 in order to bring improvements and new [[mw:Special:MyLanguage/Help:Extension:CodeMirror#Features|features]] to all editors who use the standard syntax highlighter. If you have any questions or concerns about promoting the feature out of beta, [[mw:Special:MyLanguage/Help talk:Extension:CodeMirror|please share]]. [https://phabricator.wikimedia.org/T259059]
* Some changes to local user groups are performed by stewards on Meta-Wiki and logged there only. Now, interwiki rights changes will be logged both on Meta-Wiki and the wiki of the target user to make it easier to access a full record of user's rights changes on a local wiki. Past log entries for such changes will be backfilled in the coming weeks. [https://phabricator.wikimedia.org/T6055]
* On wikis using [[m:Special:MyLanguage/Flagged Revisions|Flagged Revisions]], the number of pending changes shown on [[{{#Special:PendingChanges}}]] previously counted pages which were no longer pending review, because they have been removed from the system without being reviewed, e.g. due to being deleted, moved to a different namespace, or due to wiki configuration changes. The count will be correct now. On some wikis the number shown will be much smaller than before. There should be no change to the list of pages itself. [https://phabricator.wikimedia.org/T413016]
* Wikifunctions composition language has been rewritten, resulting in a new version of the language. This change aims to increase service stability by reducing the orchestrator's memory consumption. This rewrite also enables substantial latency reduction, code simplification, and better abstractions, which will open the door to later feature additions. Read more about [[f:Special:MyLanguage/Wikifunctions:Status updates/2026-03-11|the changes]].
* Users can now sort search results alphabetically by page title. The update gives an additional option to finding pages more easily and quickly. Previously, results could be sorted by Edit date, Creation date, or Relevance. To use the new option, open 'Advanced Search' on the search results page and select 'Alphabetically' under 'Sorting Order'. [https://phabricator.wikimedia.org/T403775]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:28}} community-submitted {{PLURAL:28|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the bug that prevented UploadWizard on Wikimedia Commons from importing files from Flickr has now been fixed. [https://phabricator.wikimedia.org/T419263]
'''Updates for technical contributors'''
* A new special page, [[{{#special:LintTemplateErrors}}]], has been created to list transcluded pages that are flagged as containing lint errors to help users discover them easily. The list is sorted by the number of transclusions with errors. For example: [[{{#special:LintTemplateErrors}}/night-mode-unaware-background-color]]. [https://phabricator.wikimedia.org/T170874]
* Users of the [[mw:Special:MyLanguage/Help:Extension:CodeMirror|{{int:codemirror-beta-feature-title}}]] beta feature have been using [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror]] instead of [[mw:Special:MyLanguage/Extension:CodeEditor|CodeEditor]] for syntax highlighting when editing JavaScript, CSS, JSON, Vue and Lua content pages, for some time now. Along with promoting CodeMirror 6 out of beta, the plan is to replace CodeEditor as the standard editor for these content models by May 2026. [[mw:Special:MyLanguage/Help talk:Extension:CodeMirror|Feedback or concerns are welcome]]. [https://phabricator.wikimedia.org/T419332]
* The [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror]] JavaScript modules will soon be upgraded to CodeMirror 6. Leading up to the upgrade, loading the <code dir=ltr>ext.CodeMirror</code> or <code dir=ltr>ext.CodeMirror.lib</code> modules from gadgets and user scripts was deprecated in July 2025. The use of the <code dir=ltr>ext.CodeMirror.switch</code> hook was also deprecated in March 2025. Contributors can now make their scripts or gadgets compatible with CodeMirror 6. See the [[mw:Special:MyLanguage/Extension:CodeMirror#Gadgets and user scripts|migration guide]] for more information. [https://phabricator.wikimedia.org/T373720]
* The MediaWiki Interfaces team is expanding coverage of REST API module definitions to include [[mw:Special:MyLanguage/API:REST API/Extensions|extension APIs]]. REST API modules are groups of related endpoints that can be independently managed and versioned. Modules now exist for [https://phabricator.wikimedia.org/T414470 GrowthExperiments] and [https://phabricator.wikimedia.org/T419053 Wikifunctions] APIs. As we migrate extension APIs to this structure, documentation will move out of the main MediaWiki OpenAPI spec and REST Sandbox view, and will instead be accessible via module-specific options in the dropdown on the [https://test.wikipedia.org/wiki/Special:RestSandbox REST Sandbox] (i.e., [[{{#Special:RestSandbox}}]], available on all wiki projects).
* The [[mw:Special:MyLanguage/Extension:Scribunto|Scribunto]] extension provides different pieces of information about the wiki where the module is being used via the [[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual|mw.site]] library. Starting last week, the library also provides a [[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#mw.site.wikiId|way]] of accessing the [[mw:Special:MyLanguage/Manual:Wiki ID|wiki ID]] that can be used to facilitate cross-wiki module maintenance. [https://phabricator.wikimedia.org/T146616]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.20|MediaWiki]]
'''In depth'''
* The [[m:Special:MyLanguage/Coolest Tool Award|2026 Coolest Tool Award]] celebrating outstanding community tools, is now open for nominations! Nominate your favorite tool using the [https://wikimediafoundation.limesurvey.net/435684?lang=en nomination survey] form by 23 March 2026. For more information on privacy and data handling, please see the [[foundation:Special:MyLanguage/Legal:Coolest_Tool_Award_2026_Survey_Privacy_Statement|survey privacy statement]].
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2026/12|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2026-W12"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 16 mars 2026 à 20:35 (CET)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30260505 -->
== <span lang="en" dir="ltr">Upcoming deployment of CampaignEvents extension to Wikibooks</span> ==
<div lang="en" dir="ltr">
<section begin="message"/>
Hello everyone,
We are writing to inform you that the [[mw:Help:Extension:CampaignEvents|CampaignEvents extension]] will be deployed to all Wikibooks projects during the week of '''23 March 2026'''.
This follows last year’s broader rollout across Wikimedia projects. We realized that Wikibooks was not included at the time, and we’re now addressing that to ensure consistency across all communities.
The CampaignEvents extension provides tools to support event and campaign organization on-wiki, including features like on-wiki event registration and collaboration lists(global event list).
We welcome any questions, feedback, or concerns you may have. We are also happy to support anyone interested in trying out the tools.
''Apologies if this message is not in your preferred language. If you’re able to help translate it for your community, please feel free to do so.''
<section end="message"/>
</div>
<bdi lang="en" dir="ltr">[[User:Udehb-WMF|Udehb-WMF]] ([[User talk:Udehb-WMF|discussion]]) 19 mars 2026 à 19:22 (CET)</bdi>
<!-- Message envoyé par User:Udehb-WMF@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Udehb-WMF/sandbox/MM_target&oldid=30284073 -->
== <span lang="en" dir="ltr">Tech News: 2026-13</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2026-W13"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/13|Translations]] are available.
'''Weekly highlight'''
* Wikimedia site users can now log in without a password using passkeys. This is a secure method supported by fingerprint, facial recognition, or PIN. With this change, all users who opt for passwordless login will find it easier, faster, and more secure to log in to their accounts using any device. The new passkey login option currently appears as an autofill suggestion in the username field. An additional [[phab:T417120|"Log in with passkey" button]] will soon be available for users who have already registered a passkey. This update will improve security and user experience. The [[c:File:Passwordless_login_screencast.webm|screen recording]] demonstrates the passwordless login process step by step.
* [[m:Special:MyLanguage/Tech/Server switch|All wikis will be read-only]] for a few minutes on Wednesday, 25 March 2026 at [https://zonestamp.toolforge.org/1774450800 15:00 UTC]. This is for the datacenter server switchover backup tests, [[wikitech:Deployments/Yearly calendar|which happen twice a year]]. During the switchover, all Wikimedia website traffic is shifted from one primary data center to the backup data center to test availability and prevent service disruption even in emergencies.
'''Updates for editors'''
* Wikimedia site users can now export their notifications older than 5 years using a [[toolforge:echo-chamber|new Toolforge tool]]. This will ensure that users retain their important notifications and avoid them being lost based on the planned change to delete notifications older than 5 years, as previously announced. [https://phabricator.wikimedia.org/T383948]
* Wikipedia editors in Indonesian, Thai, Turkish, and Simple English now have access to Special:PersonalDashboard. This is an [[mw:Special:MyLanguage/Moderator Tools/Dashboard|early version of an experience]] that introduces newer editors to patrolling workflows, making it easier for them to move from making edits to participating in more advanced moderation work on their project. [https://phabricator.wikimedia.org/T402647]
* The [[Special:Block]] now has two minor interface changes. Administrators can now easily perform indefinite blocks through a dedicated radio button in the expiry section. Also, choosing an indefinite expiry provides a different set of common reasons to select from, which can be changed at: [[MediaWiki:Ipbreason-indef-dropdown]]. [https://phabricator.wikimedia.org/T401823]
* Mobile editors [[mw:Special:MyLanguage/Contributors/Account Creation Experiments#Logged-out|at several wikis]] can now see an improved logged-out edit warning, thanks to the recent updates from the Growth team. These changes released last week are part of ongoing efforts and tests to enhance [[mw:Special:MyLanguage/Contributors/Account Creation Experiments|account creation experience on mobile]] and then increase participation. [https://phabricator.wikimedia.org/T408484]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:36}} community-submitted {{PLURAL:36|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the bug that prevented mobile web users from seeing the block information when affected by multiple blocks has been fixed. They can now see messages of all the blocks currently affecting them when they access Wikipedia.
'''Updates for technical contributors'''
* Images built using Toolforge will soon get the upgraded buildpacks version, bringing support for newer language versions and other upstream improvements and fixes. If you use Toolforge Build Service, review the recent [https://lists.wikimedia.org/hyperkitty/list/cloud-announce@lists.wikimedia.org/thread/EMYTA32EV2V5SQ2JIEOD2CL66YFIZEKV/ cloud-announce email] and update your build configuration as necessary to ensure your tools are compatible. [https://wikitech.wikimedia.org/w/index.php?title=Help:Toolforge/Building_container_images&oldid=2392097#Buildpack_environment_upgrade_process][https://phabricator.wikimedia.org/T380127]
* The [https://api.wikimedia.org/wiki/Main_Page API Portal] documentation wiki will shut down in June 2026. API keys created on the API Portal will continue to work normally. api.wikimedia.org endpoints will be deprecated gradually starting in July 2026. Documentation on the API Portal is moving to [[mw:Wikimedia APIs|mediawiki.org]]. Learn more on the [[wikitech:API Portal/Deprecation|project page]].
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.21|MediaWiki]]
'''In depth'''
* [[m:Special:MyLanguage/WMDE Technical Wishes|WMDE Technical Wishes]] is considering improvements to [[m:WMDE Technical Wishes/References/VisualEditor automatic reference names|automatically generated reference names in VisualEditor]]. Please check out the [[m:WMDE Technical Wishes/References/VisualEditor automatic reference names#Proposed solutions|proposed solutions]] and participate in the [[m:Talk:WMDE Technical Wishes/References/VisualEditor automatic reference names#Request for comment|request for comment]].
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2026/13|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2026-W13"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 23 mars 2026 à 17:51 (CET)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30268305 -->
== Actualités techniques n° 2026-14 ==
<section begin="technews-2026-W14"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/14|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Le version Beta de [[abstract:|Abstract Wikipedia]], un nouveau projet Wikimédia indépendant du langage, a été lancée la semaine dernière. Ce projet permet aux communautés de construire des articles Wikipédia dans leur langue natale, qui peuvent directement être lus par les autres utilisateurs et utilisatrices dans leur propre langage. Le wiki fonctionne grâce à des instructions de Wikifunctions et au contenu structuré issu de Wikidata. [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-03-26|En savoir plus]].
'''Actualités pour la contribution'''
* L'équipe Croissance mène un test A/B afin d'évaluer l'effet d'un message plus clair et plus convivial encourageant à la création de comptes sur les wikis. Actuellement, lorsqu'un utilisateur mobile non connecté lance la modification, un message d'avertissement s'affiche, pouvant paraître abrupt et décourageant. Il présente également la modification par compte temporaire comme option par défaut, au lieu d'inciter à la création d'un compte. Le test est mené sur dix Wikipédia, dont les versions en arabe, français, espagnol et allemand. [[mw:Special:MyLanguage/Contributors/Account Creation Experiments#2. Improve logged-out warning message (T415160)|En savoir plus]].
* L'équipe des applications Wikimédia sollicite vos commentaires sur [[mw:Special:MyLanguage/Wikimedia Apps/Team/Future of Editing on the Mobile Apps|comment devrait fonctionner l'édition dans les applications mobiles Wikipédia]]. La discussion porte sur l'amélioration de l'accès aux outils d'édition lorsque les utilisateurs appuient sur « Modifier ». Cette initiative s'inscrit dans un effort plus large visant à offrir aux lecteurs intéressés par la contribution une expérience utilisateur plus intuitive.
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:45|la tâche soumise|les {{formatnum:45}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:45||s}} la semaine dernière]]. Par exemple, un problème avec la récupération de citations à partir du site d'archive de journaux [https://www.newspapers.com Newspapers.com], qui ne fonctionnait plus en raison d'un blocage des requêtes [[mw:Special:MyLanguage/Citoid|Citoid]], a maintenant été résolu. [https://phabricator.wikimedia.org/T419903]
'''Actualités pour la contribution technique'''
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.22|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2026/14|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2026-W14"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 30 mars 2026 à 21:25 (CEST)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30329462 -->
== Action Required: Update templates/modules for electoral maps (Migrating from P1846 to P14226) ==
Hello everyone,
This is a notice regarding an ongoing data migration on Wikidata that may affect your election-related templates and Lua modules (such as <code>Module:Itemgroup/list</code>).
'''The Change:'''<br />
Currently, many templates pull electoral maps from Wikidata using the property [[:d:Property:P1846|P1846]], combined with the qualifier [[:d:Property:P180|P180]]: [[:d:Q19571328|Q19571328]].
We are migrating this data (across roughly 4,000 items) to a newly created, dedicated property: '''[[:d:Property:P14226|P14226]]'''.
'''What You Need To Do:'''<br />
To ensure your templates and infoboxes do not break or lose their maps, please update your local code to fetch data from [[:d:Property:P14226|P14226]] instead of the old [[:d:Property:P1846|P1846]] + [[:d:Property:P180|P180]] structure. A [[m:Wikidata/Property Migration: P1846 to P14226/List|list of pages]] was generated using Wikimedia Global Search.
'''Deadline:'''<br />
We are temporarily retaining the old data on [[:d:Property:P1846|P1846]] to allow for a smooth transition. However, to complete the data cleanup on Wikidata, the old [[:d:Property:P1846|P1846]] statements will be removed after '''May 1, 2026'''. Please update your modules and templates before this date to prevent any disruption to your wiki's election articles.
Let us know if you have any questions or need assistance with the query logic. Thank you for your help! [[User:ZI Jony|ZI Jony]] using [[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 3 avril 2026 à 19:11 (CEST)
<!-- Message envoyé par User:ZI Jony@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=29941252 -->
== Actualités techniques n° 2026-15 ==
<section begin="technews-2026-W15"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/15|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* L’[[mw:Special:MyLanguage/Help:Extension:CampaignEvents|extension CampaignEvents]] comprend désormais une nouvelle fonctionnalité de définition d’objectifs de groupe, permettant aux organisateurs de définir et de suivre les objectifs de l’événement, tels que le nombre d’articles créés et de contributeurs participants en temps réel. De même, les participants peuvent travailler vers des cibles communes et voir leur impact collectif au fur et à mesure que l’événement se déroule. Cette fonctionnalité est désormais disponible sur tous les wikis Wikimedia. Pour en savoir plus, consultez [[mw:Special:MyLanguage/Help:Extension:CampaignEvents/Registration/Collaborative contributions#Goal setting|la documentation]].
* [[File:Maki-gift-15.svg|12px|link=|class=skin-invert|Concerne un souhait]] La nouvelle fonctionnalité d'[[mw:Special:MyLanguage/Help:Watchlist labels|étiquettes de liste de suivi]] (annoncée dans les [[m:Special:MyLanguage/Tech/News/2026/07|Actualités techniques 2026-07 ]]) est désormais disponible via l'ÉditeurVisuel, l'éditeur de code et l'«étoile de suivi»(ou le lien de suivi, pour les habillages qui n'ont pas d'icône d'étoile). Auparavant, il n'était possible d'attribuer des étiquettes que via [[Special:EditWatchlist|Modifier la liste de suivi]]. Dans ces trois emplacements, il s'agit d'un nouveau champ situé après le champ d'expiration.
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]]. Par exemple, le problème où les pages de discussion sur mobile avec Parsoid sont inutilisables après les en-têtes de section vides, a maintenant été résolu. [https://phabricator.wikimedia.org/T419171]
'''Actualités pour la contribution technique'''
* La [[m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing|fonctionnalité de sous-référencement]], qui permet aux contributeurs d'ajouter des détails à une référence existante sans la dupliquer, sera progressivement déployée sur [[phab:T414094|davantage de wikis]] plus tard cette année. Les wikis utilisant le gadget [[mw:Special:MyLanguage/Reference Tooltips|Reference Tooltips]] sont encouragés à mettre à jour leur version (généralement sur [[m:MediaWiki:Gadget-ReferenceTooltips.js|MediaWiki:Gadget-ReferenceTooltips.js]] comme indiqué [https://en.wikipedia.org/w/index.php?diff=1344408362 ici]) pour assurer la compatibilité. D'autres gadgets liés aux références pourraient également être affectés. [https://phabricator.wikimedia.org/T416304]
* Toutes les éditions de Wikinews seront fermées et passeront en mode lecture seule le 4 mai 2026. Le contenu restera accessible, mais aucune nouvelle modification ni aucun nouvel article ne pourra être ajouté. Cette fermeture a été approuvée par le Conseil d'administration de la Fondation Wikimedia à la suite de discussions prolongées. [[m:Wikimedia Foundation Board noticeboard#Board of Trustees Approves Closure of Wikinews|En savoir plus]].
* L'[[:mw:Special:MyLanguage/API:Action API|API d'action]] a proposé plusieurs formats pour les résultats demandés. L'un d'entre eux, <bdi lang="zxx" dir="ltr"><code><nowiki>format=php</nowiki></code></bdi>, sera bientôt supprimé. Veuillez vous assurer que vos scripts ou robots utilisent le [[mw:Special:MyLanguage/API:Data formats#Output|format JSON]]. Cette suppression devrait affecter très peu de scripts et de robots. [https://phabricator.wikimedia.org/T118538]
* La page [[Special:NamespaceInfo|Special:NamespaceInfo]] inclut désormais les alias d'espace de noms. Par exemple «WP» pour l'espace de noms ''Projet'' (''Wikipédia'') sur la Wikipédia en allemand. [https://phabricator.wikimedia.org/T381455]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.23|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2026/15|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2026-W15"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 6 avril 2026 à 18:19 (CEST)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30362761 -->
== <span lang="en" dir="ltr">Tech News: 2026-16</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2026-W16"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/16|Translations]] are available.
'''Weekly highlight'''
* Experienced editors are invited to [https://b24e11a4f1.catalyst.wmcloud.org/wiki/Main_Page test] the [[mw:Special:MyLanguage/Article guidance|Article guidance]] feature, designed to help less-experienced editors create well-structured, policy-compliant Wikipedia articles. Testing instructions are [[mw:Special:MyLanguage/Article guidance/Test feature guide|available]]. Also, after reviewing [https://b24e11a4f1.catalyst.wmcloud.org/wiki/Category:Pages_using_article_guidance the outlines], please provide feedback on the [[mw:Talk:Article guidance|project talk page]]. Based on your input, the feature will be refined and transferred to the pilot Wikipedias to translate and adapt. Check out [[c:File:Article Guidance workflow demo - April 2026.webm|the video]] explaining the feature.
'''Updates for editors'''
* On most wikis, all autoconfirmed users can now use [[Special:ChangeContentModel|Special:ChangeContentModel]] page to [[mw:Special:MyLanguage/Help:ChangeContentModel|create new pages with custom content models]], such as mass message lists, making custom page formats more accessible. Check [[Special:ListGroupRights|Special:ListGroupRights]] for the status of your wiki. [https://phabricator.wikimedia.org/T248294]
* The Growth team has launched an [[mw:Special:MyLanguage/Contributors/Account_Creation_Experiments|account creation experiment]] to evaluate whether adding an account creation button to the mobile web header increases new account registrations and encourages more mobile users to contribute to the wikis. The experiment is currently live on Hindi, Indonesian, Bengali, Thai, and Hebrew Wikipedia, and targets 10% of logged-out mobile web users.
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:30}} community-submitted {{PLURAL:30|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where VisualEditor could get stuck loading on Windows devices with animations turned off, has now been fixed. [https://phabricator.wikimedia.org/T382856]
'''Updates for technical contributors'''
* Starting later this week, {{int:group-abusefilter}} who have the [[mw:Special:MyLanguage/Help:Extension:CodeMirror|{{int:codemirror-beta-feature-title}}]] beta feature enabled will have [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror]] instead of [[mw:Special:MyLanguage/Extension:CodeEditor|CodeEditor]] as the editor at [[Special:AbuseFilter|Special:AbuseFilter]]. This is part of the broader effort to make the user experience more consistent across all editors. [https://phabricator.wikimedia.org/T399673][https://phabricator.wikimedia.org/T419332]
* Tools and bots that access the [[mw:Special:MyLanguage/Notifications/API|Notifications API]] (<bdi lang="zxx" dir="ltr"><code><nowiki>action=query&meta=notifications</nowiki></code></bdi>) will need to update their OAuth or BotPassword grants to also include access to private notifications. [https://phabricator.wikimedia.org/T421991]
* Due to a library upgrade, listings on category pages may be displayed out of order starting on Monday, 20th April. A migration script will be run to correct this, and will take hours to days depending on the size of the wiki (up to a week for English Wikipedia). [https://phabricator.wikimedia.org/T422544]
* [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.24|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2026/16|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2026-W16"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 13 avril 2026 à 17:19 (CEST)
<!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30380527 -->
6kpjfx7xlwegews3tdecjjwqnts4rkf
Fonctionnement d'un ordinateur/Les coprocesseurs : FPU et IO
0
83778
763581
763008
2026-04-13T14:13:32Z
Mewtow
31375
/* Les registres de contrôle et d'état */
763581
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Lors des accès mémoire, il y avait parfois des conversions entre flottants 80 bits et flottants 32/64 bits. L'instruction LOAD flottantes pouvait lire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. Les flottants 32 et 64 bits étaient convertis en flottants 80 bits lors du chargement. Même chose pour l'enregistrement en mémoire via l'instruction STORE flottante. Les flottants 80 bit était soit convertit en flottant 32 ou 64 bits, soit enregistrés directement avec 80 bits.
Le problème est que faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent. De plus, la conversion lors des écritures mémoire effectue un arrondi pour faire rentrer le résultat sur 32/64 bits, arrondi qui modifie encore les résultats. Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Une autre conséquence est que les résultats sont impactés par l'ordre des accès mémoire, par la manière dont sont gérés les registres flottants. En effet, les problèmes d'arrondis ont lieu lors de l'écriture. Plus longtemps les résultats intermédiaires sont enregistrés dans les registres, plus on retarde les problèmes. Mais il arrive fatalement un moment où des flottants doivent quitter les registres flottants pour arriver en RAM.
Et ce moment dépend du nombre de registres et du nombre d'opérandes traitées. Si vous vous débrouillez pour faire tous vos calculs flottants avec les 8 registres disponibles, vous ne ferez d'arrondi qu'à la toute fin de vos calculs, pour enregistrer les résultats. Si vous utilisez plus, vous aller devoir faire un vas et vient entre RAM et registres. Dans ce cas, suivant l'ordre des accès mémoire, les arrondis se feront à des instants différents.
Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
05z350p1wkypn3p1uu9lut0dbpl9nxr
763582
763581
2026-04-13T14:19:32Z
Mewtow
31375
/* Les doubles arrondis avec les registres x87 */
763582
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent. Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Une autre conséquence est que les résultats sont impactés par l'ordre des accès mémoire, par la manière dont sont gérés les registres flottants. En effet, les problèmes d'arrondis ont lieu lors de l'écriture. Plus longtemps les résultats intermédiaires sont enregistrés dans les registres, plus on retarde les problèmes. Mais il arrive fatalement un moment où des flottants doivent quitter les registres flottants pour arriver en RAM.
Et ce moment dépend du nombre de registres et du nombre d'opérandes traitées. Si vous vous débrouillez pour faire tous vos calculs flottants avec les 8 registres disponibles, vous ne ferez d'arrondi qu'à la toute fin de vos calculs, pour enregistrer les résultats. Si vous utilisez plus, vous aller devoir faire un vas et vient entre RAM et registres. Dans ce cas, suivant l'ordre des accès mémoire, les arrondis se feront à des instants différents.
Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
8tyuf5znkys6wdm6thtd27ifhs13ulr
763583
763582
2026-04-13T14:20:47Z
Mewtow
31375
/* Les doubles arrondis avec les registres x87 */
763583
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
r0mqvh3lk00oxm4j36sx4vfc6n3fb79
763584
763583
2026-04-13T14:40:25Z
Mewtow
31375
/* L'intel 8087 et ses successeurs */
763584
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167.
Le 1067 avait pour particularité d'être fournit en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le 11167 regroupait ces trois pièces détachées sur une carte d'extension ISA.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
ctoc39zsptc3mfztj6jl7490bed85xd
763585
763584
2026-04-13T15:03:52Z
Mewtow
31375
/* Les coprocesseurs x87 de Weitek */
763585
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167.
Le 1067 avait pour particularité d'être fournit en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le 11167 regroupait ces trois pièces détachées sur une carte d'extension ISA.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. A la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentées sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre accumulateur de 64 bits lui aussi. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, la seule exception étant l'opcode, qui est envoyée par l'unité de contrôle sur une entrée dédiée, notée F. Divers bits de commande commandent ces opérations.
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Pour l'envoi du résultat sur le bus, les signaux de commandes utilisés sont les signaux U, CSUS et CSUX. En sortie, il y avait 4 bits pour le registre de statut. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
3h8615skyoex9yfjym46fx9lubbafgd
763588
763585
2026-04-13T15:13:06Z
Mewtow
31375
/* Les coprocesseurs x87 de Weitek */
763588
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167.
Le '''Weitek 1067''' avait pour particularité d'être fournit en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. A la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentées sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. A part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
1zhre9tks4xo6hei7l02pnajzwpecqf
763590
763588
2026-04-13T15:31:26Z
Mewtow
31375
/* Les coprocesseurs x87 de Weitek */
763590
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. A la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386. Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOOOOOOh à COOOEFFFh. Pour exécuter une instruction, il envoie l'instruction en question sur une adresse bien précise, l'adresse C0000009h. Le coprocesseur mémorise alors l'instruction dans son registre d'instruction, et l'exécute. Le processeur peut envoyer un opérande au cycle suivant, si besoin.
Contrairement à ses prédécesseurs, il intégrait 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser une opérande 64 bits. Son registre de status contenait les résultats des comparaison flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Pour le cas où une opérande est envoyée sur le bus de données, cela demande la collaboration du CPU 386. Le CPU copie alors un registre sur le bus de données, à une adresse correspondant au coprocesseur, et le coprocesseur a lit automatiquement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
pfjfushqe5txrkyijbxohsx62b2uee9
763596
763590
2026-04-13T15:45:57Z
Mewtow
31375
/* Les coprocesseurs x87 de Weitek */
763596
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. A la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386. Contrairement à ses prédécesseurs, il intégrait 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser une opérande 64 bits. Son registre de status contenait les résultats des comparaison flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Les 16 bits de poids faible sont utilisés pour envoyer une instruction au coprocesseur, et encodent un opcode de 6 bits, et de quoi encoder la source des deux opérandes.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement une opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéro de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
0d06wk25gi2uk1llu66ttfouyre3ylk
763597
763596
2026-04-13T15:49:09Z
Mewtow
31375
/* Les coprocesseurs x87 de Weitek */
763597
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. A la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386. Contrairement à ses prédécesseurs, il intégrait 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser une opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant.
Son registre de status contenait les résultats des comparaison flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associée à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement une opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéro de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
biig7mo0ugnkdjjqctkiv7x7z0tm8r8
763598
763597
2026-04-13T15:51:20Z
Mewtow
31375
/* Les coprocesseurs x87 de Weitek */
763598
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. A la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais les deux fonctionnaient de la même manière au-delà de quelques détails. Le 4167 était totalement compatible avec le 3167.
Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser une opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaison flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associée à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement une opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéro de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
3iyptv7mt2h4pctrifewb87y7mv7s8q
763599
763598
2026-04-13T15:56:40Z
Mewtow
31375
/* Les coprocesseurs x87 de Weitek */
763599
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais les deux fonctionnaient de la même manière au-delà de quelques détails. Le 4167 était totalement compatible avec le 3167.
Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
8wz1tq5oajjcbditrzs05928gflq6m5
763600
763599
2026-04-13T16:40:51Z
Mewtow
31375
/* Les coprocesseurs x87 de Weitek */
763600
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Le coprocesseur a donc un rôle secondaire, reste à voir à quoi il sert.
===Les différents types de coprocesseurs===
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
mtphy5h9cx08g47c80sa6qa0jjvycr2
763603
763600
2026-04-13T17:34:01Z
Mewtow
31375
/* Les différents types de coprocesseurs : son, IO, FPU */
763603
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 6802 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
iy9vh3rd25h7elpt5o5ithsjjxvn03d
763604
763603
2026-04-13T17:37:33Z
Mewtow
31375
/* Les anciens co-processeurs flottants des PCs */
763604
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Un bon exemple de la seconde solution est le 68881 de Motorola, conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
52c2it7zbm5b2b1hh0fh40do2ind7hg
763605
763604
2026-04-13T17:57:19Z
Mewtow
31375
/* Les anciens co-processeurs flottants des PCs */
763605
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
sh3gye7xqh4792nzx9h7kjwg5gn2610
763606
763605
2026-04-13T17:57:43Z
Mewtow
31375
/* Les coprocesseurs x87 de Weitek */
763606
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
==Les coprocesseurs arithmétiques : généralités et exemples==
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les anciens co-processeurs flottants des PCs===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
===Les coprocesseurs arithmétiques des consoles de jeu===
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
====Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000. Le 68881 était mappé en mémoire, comme une entrée-sortie. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
3709h4ve0n2ixuxq1ptr618wjz6to16
763607
763606
2026-04-13T17:57:57Z
Mewtow
31375
/* Les coprocesseurs arithmétiques : généralités et exemples */
763607
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
====Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000. Le 68881 était mappé en mémoire, comme une entrée-sortie. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
32iuqti5x5lakaplmhjmawuhiv2t5h3
763608
763607
2026-04-13T17:58:04Z
Mewtow
31375
/* =Le Motorola 68881 et le 68882 */
763608
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000. Le 68881 était mappé en mémoire, comme une entrée-sortie. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Il avait aussi un registre de statut et un registre de contrôle, guère plus. Fait étonnant, les coprocesseurs pour les PC faisaient pareil : flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, comme on le verra plus tard.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
rsb0mgmke2bohstpjo2jai47088ewj0
763609
763608
2026-04-13T18:01:12Z
Mewtow
31375
/* Le Motorola 68881 et le 68882 */
763609
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Si elle était pour le coprocesseur, le 68000 lisait les opérandes depuis la mémoire et envoyait tout au coprocesseur 68881, qui exécutait l'instruction, puis rendait la main au 68000. Le 68881 était mappé en mémoire, comme une entrée-sortie. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
qq1iwthp35wib7m8hxphiv6rqgxl1xv
763610
763609
2026-04-13T18:19:11Z
Mewtow
31375
/* Le Motorola 68881 et le 68882 */
763610
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
hynlocqzkw0o6ns6fdvv9o6tolqb9yi
763611
763610
2026-04-13T18:26:16Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763611
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Les deux travaillent en parallèle, et il existe des mécanismes de synchronisation entre les deux processeurs. Typiquement, la synchronisation se fait en utilisant des ''interruptions inter-processeurs'', à savoir qu'un processeur exécute une interruption qui est exécutée par l'autre processeur. L'implémentation demande d'ajouter des circuits pour gérer ces interruptions entre processeurs.
Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Une autre solution traite le coprocesseur comme une entrée-sortie, connectée au bus système. De nombreux coprocesseurs arithmétiques sont dans ce cas. Ils sont mappés en mémoire, le processeur écrit une instruction et ses opérandes dans les registres, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un registre d’interfaçage.
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
14aasn3lbi5adkckjgou48svu5by5y0
763612
763611
2026-04-13T18:29:39Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763612
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Une implémentation simple traite le coprocesseur comme une entrée-sortie, connectée au bus système. De nombreux coprocesseurs arithmétiques sont dans ce cas. Ils sont mappés en mémoire, le processeur écrit une instruction et ses opérandes dans les registres, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un registre d’interfaçage.
La récupération du résultat peut se faire avec du ''pooling'', à savoir que le processeur revient quelques cycles plus tard. Si le coprocesseur n'a pas terminé, le CPU attend que le coprocesseur fournisse le résultat. Une autre solution utilise des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
gn1a7bglfwpwoccssqtgk6ehy4skj5f
763613
763612
2026-04-13T18:29:57Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763613
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores et d'IO sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Une implémentation simple traite le coprocesseur comme une entrée-sortie, connectée au bus système. De nombreux coprocesseurs arithmétiques sont dans ce cas. Ils sont mappés en mémoire, le processeur écrit une instruction et ses opérandes dans les registres, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'', à savoir que le processeur revient quelques cycles plus tard. Si le coprocesseur n'a pas terminé, le CPU attend que le coprocesseur fournisse le résultat. Une autre solution utilise des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
9zaubonxtglyb832awxxh4lnvs51w1w
763614
763613
2026-04-13T18:32:10Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763614
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Une implémentation simple traite le coprocesseur comme une entrée-sortie, connectée au bus système. De nombreux coprocesseurs arithmétiques sont dans ce cas. Ils sont mappés en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'', à savoir que le processeur revient quelques cycles plus tard. Si le coprocesseur n'a pas terminé, le CPU attend que le coprocesseur fournisse le résultat. Une autre solution utilise des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
l4ndnb3zcziz4rri8r8cjoywqfjd37e
763615
763614
2026-04-13T18:32:53Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763615
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Une implémentation simple traite le coprocesseur comme une entrée-sortie, connectée au bus système. De nombreux coprocesseurs arithmétiques sont dans ce cas. Ils sont mappés en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Les coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
2n8mcuro917873d2u3617cc1rs8pi6o
763616
763615
2026-04-13T18:33:25Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763616
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
Un coprocesseur peut être un processeur fait sur mesure, qui ne peut que servir de processeur secondaire, comme le sont tous les coprocesseurs arithmétiques. À l'inverse, de nombreuses consoles de jeu utilisaient un processeur des plus banals comme coprocesseur. Par exemple, de nombreuses consoles avaient un processeur principal, complété par un coprocesseur Z80 en guise de carte son. Et cette distinction est très liée à celle qui distingue les coprocesseurs faiblement couplés, et fortement couplés.
Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore.
Une implémentation simple traite le coprocesseur comme une entrée-sortie, connectée au bus système. De nombreux coprocesseurs arithmétiques sont dans ce cas. Ils sont mappés en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
De rares coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les coprocesseurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les coprocesseurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
4jujx0f9t2nfyuudu90e92eqk6cpgb2
763617
763616
2026-04-13T18:40:12Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763617
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
De rares coprocesseurs arithmétiques se distinguent des autres, car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Avec eux, il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur, une par une. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
s8zi0g0chsnpvrxdrumlvp0yxemxf6e
763618
763617
2026-04-13T18:43:11Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763618
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le coprocesseur lit directement les instructions depuis le bus mémoire, au même titre que le CPU. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suti, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
nbcm7ca5tsa8cyf07nus5rrdgud9ylc
763619
763618
2026-04-13T18:43:59Z
Mewtow
31375
/* Le Motorola 68881 et le 68882 */
763619
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le coprocesseur lit directement les instructions depuis le bus mémoire, au même titre que le CPU. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suti, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : généralités et exemples===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
cykdnimkiytbcrwhvjfbfbjhgd1ms3g
763620
763619
2026-04-13T18:44:14Z
Mewtow
31375
/* Les coprocesseurs arithmétiques : généralités et exemples */
763620
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le coprocesseur lit directement les instructions depuis le bus mémoire, au même titre que le CPU. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suti, qui sont un mélange des deux solutions.
===Les coprocesseurs fortement et faiblement couplés===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
p25nsdx988520zexno4m2rj5msvytlb
763621
763620
2026-04-13T18:45:05Z
Mewtow
31375
/* Les coprocesseurs fortement et faiblement couplés */
763621
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le coprocesseur lit directement les instructions depuis le bus mémoire, au même titre que le CPU. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suti, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : quelques généralités===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants et étaient généralement fortement couplés. Les coprocesseurs étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Les consoles de jeu incorporent souvent des coprocesseurs. Mais il est assez rare qu'elles incorporent des coprocesseurs arithmétiques et c'est surtout quelque chose qu'on observe sur les consoles des années 2000. La raison est que les consoles utilisent des processeurs commerciaux, utilisés dans les smartphones ou les PC. Elles n'utilisent pas de processeur spécifiquement conçu pour elles, sauf exceptions. Et de tels processeurs disposent de capacités de calcul flottant ou entières suffisantes, ils sont choisis pour. Mais il existe quelques cas où ce n'est pas le cas.
Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
h14cq58xbun06adrofsm2r01y4hnutm
763622
763621
2026-04-13T18:47:15Z
Mewtow
31375
/* Les coprocesseurs arithmétiques : quelques généralités */
763622
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le coprocesseur lit directement les instructions depuis le bus mémoire, au même titre que le CPU. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suti, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : quelques généralités===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants Ils étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Le problème ne se pose pas sur les consoles de jeu, mais il est assez rare que les consoles de jeu incorporent des coprocesseurs arithmétiques. Il existe cependant des exceptions. Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
470etzkniwnxpqjegwfy0rhy459gqji
763623
763622
2026-04-13T18:48:54Z
Mewtow
31375
/* Les différents types de coprocesseurs : son, IO, FPU */
763623
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le coprocesseur lit directement les instructions depuis le bus mémoire, au même titre que le CPU. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suti, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : quelques généralités===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants Ils étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Le problème ne se pose pas sur les consoles de jeu, mais il est assez rare que les consoles de jeu incorporent des coprocesseurs arithmétiques. Il existe cependant des exceptions. Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
qty7xkdamtvxyw5ay92qmbnt5e4p72m
763624
763623
2026-04-13T18:54:02Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763624
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur, dont l'encodage est différent. Les deux surveillent le bus mémoire et décident à qui est destinée l'instruction. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Pour cela, le processeur envoie son ''program counter'' sur le bus d'adresse, ce qui entraine l'apparition d'une instruction sur le bus de données. Là, le processeur et le coprocesseur reçoivent l'instruction et la décodent. Si l'instruction est destinée au processeur, le CPU l'exécutera, alors que le coprocesseur la traitera comme un NOP. Et inversement si l'instruction est destinée au coprocesseur.
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suti, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : quelques généralités===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants Ils étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Le problème ne se pose pas sur les consoles de jeu, mais il est assez rare que les consoles de jeu incorporent des coprocesseurs arithmétiques. Il existe cependant des exceptions. Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
pp02lpof8l06udcd90s7ltoaks6gy4x
763625
763624
2026-04-13T18:55:41Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763625
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur, dont l'encodage est différent. Les deux surveillent le bus mémoire et décident à qui est destinée l'instruction. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Pour cela, le processeur envoie son ''program counter'' sur le bus d'adresse, ce qui entraine l'apparition d'une instruction sur le bus de données. Là, le processeur et le coprocesseur reçoivent l'instruction et la décodent. Si l'instruction est destinée au processeur, le CPU l'exécutera, alors que le coprocesseur la traitera comme un NOP. Et inversement si l'instruction est destinée au coprocesseur.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suit, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : quelques généralités===
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants Ils étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Le problème ne se pose pas sur les consoles de jeu, mais il est assez rare que les consoles de jeu incorporent des coprocesseurs arithmétiques. Il existe cependant des exceptions. Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
30gtc3zdsdjem9c2wtts0yiulw8cuzq
763626
763625
2026-04-13T18:56:16Z
Mewtow
31375
/* Les différents types de coprocesseurs : son, IO, FPU */
763626
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur, dont l'encodage est différent. Les deux surveillent le bus mémoire et décident à qui est destinée l'instruction. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Pour cela, le processeur envoie son ''program counter'' sur le bus d'adresse, ce qui entraine l'apparition d'une instruction sur le bus de données. Là, le processeur et le coprocesseur reçoivent l'instruction et la décodent. Si l'instruction est destinée au processeur, le CPU l'exécutera, alors que le coprocesseur la traitera comme un NOP. Et inversement si l'instruction est destinée au coprocesseur.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suit, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : quelques généralités===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants Ils étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Le problème ne se pose pas sur les consoles de jeu, mais il est assez rare que les consoles de jeu incorporent des coprocesseurs arithmétiques. Il existe cependant des exceptions. Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
9opyh56u3knerqlu0eqkyrzrcx6fvlk
763627
763626
2026-04-13T18:58:17Z
Mewtow
31375
/* Les coprocesseurs faiblement et fortement couplés */
763627
wikitext
text/x-wiki
Les processeurs actuels sont des processeurs multicœurs, à savoir qu'ils intègrent plusieurs processeurs dans un seul circuit intégré. Du moins, c'est une explication simplifiée, des circuits sont mutualisés entre les processeurs. Les cœurs sont tous identiques, à savoir qu'ils ont le même jeu d'instruction, ont une microarchitecture similaire, etc. Mais ce n'est possible que parce que les processeurs actuels ont un grand nombre de transistors, grâce à la loi de Moore. Pour intégrer plusieurs cœurs sur un processeur, il faut le budget en transistor pour. Mais au siècle dernier, le budget en transistors n'était pas là. Aussi, utiliser plusieurs processeurs était plus compliqué.
Les ordinateurs de l'époque pouvaient utiliser plusieurs processeurs identiques, mais il fallait que la carte mère le supporte. Et les cartes mères avec plusieurs sockets identiques étaient rares. Les cartes mères pour serveur en étaient capables, mais pas les cartes mères grand public. Les cartes mères plus grand public avaient cependant une option pour gérer plusieurs processeurs. Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs secondaires qui complémentaient un processeur principal. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs : son, IO, FPU==
Le CPU et le coprocesseur sont foncièrement différents : ils n'ont pas le même jeu d'instruction, n'ont pas le même rôle, et j'en passe. Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur, dont l'encodage est différent. Les deux surveillent le bus mémoire et décident à qui est destinée l'instruction. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Pour cela, le CPU envoie son ''program counter'' sur le bus d'adresse, ce qui entraine l'apparition d'une instruction sur le bus de données. Là, le processeur et le coprocesseur reçoivent l'instruction et la décodent. Si l'instruction est destinée au processeur, le CPU l'exécutera, alors que le coprocesseur la traitera comme un NOP. Et inversement si l'instruction est destinée au coprocesseur.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suit, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : quelques généralités===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants Ils étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Le problème ne se pose pas sur les consoles de jeu, mais il est assez rare que les consoles de jeu incorporent des coprocesseurs arithmétiques. Il existe cependant des exceptions. Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
7z35bncsyl8ce9n5l3b41r6qqa93nhg
763641
763627
2026-04-13T20:29:02Z
Mewtow
31375
763641
wikitext
text/x-wiki
Les '''coprocesseurs''' sont des processeurs secondaires qui complémentent un processeur principal. Ils permettent de déléguer certains calculs à un processeur secondaire, afin de décharger le processeur principal. Un détail important est que le processeur principal et le coprocesseur sont très différents : ils n'ont pas le même jeu d'instruction, n'ont pas les mêmes performances, et bien d'autres différences. Nous en avions déjà vu dans le chapitre sur l'architecture de base, où nous avions analysé d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs==
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
A ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les second sont intégrés sur le bus mémoire.
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur, dont l'encodage est différent. Les deux surveillent le bus mémoire et décident à qui est destinée l'instruction. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Pour cela, le CPU envoie son ''program counter'' sur le bus d'adresse, ce qui entraine l'apparition d'une instruction sur le bus de données. Là, le processeur et le coprocesseur reçoivent l'instruction et la décodent. Si l'instruction est destinée au processeur, le CPU l'exécutera, alors que le coprocesseur la traitera comme un NOP. Et inversement si l'instruction est destinée au coprocesseur.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suit, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : quelques généralités===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants Ils étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Le problème ne se pose pas sur les consoles de jeu, mais il est assez rare que les consoles de jeu incorporent des coprocesseurs arithmétiques. Il existe cependant des exceptions. Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4e registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
bzwnf24xe9w9b5v5qnq9cvx116zt1xa
763643
763641
2026-04-13T20:32:29Z
Mewtow
31375
763643
wikitext
text/x-wiki
Les '''coprocesseurs''' sont des processeurs secondaires qui complémentent un processeur principal. Ils permettent de déléguer certains calculs à un processeur secondaire, afin de décharger le processeur principal. Un détail important est que le processeur principal et le coprocesseur sont très différents : ils n'ont pas le même jeu d'instruction, n'ont pas les mêmes performances, et bien d'autres différences. Nous en avions déjà vu dans le chapitre sur l'architecture de base, où nous avions analysé d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs==
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
À ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les seconds sont intégrés sur le bus mémoire.
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur, dont l'encodage est différent. Les deux surveillent le bus mémoire et décident à qui est destinée l'instruction. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Pour cela, le CPU envoie son ''program counter'' sur le bus d'adresse, ce qui entraine l'apparition d'une instruction sur le bus de données. Là, le processeur et le coprocesseur reçoivent l'instruction et la décodent. Si l'instruction est destinée au processeur, le CPU l'exécutera, alors que le coprocesseur la traitera comme un NOP. Et inversement si l'instruction est destinée au coprocesseur.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suit, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : quelques généralités===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. Il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants Ils étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Le problème ne se pose pas sur les consoles de jeu, mais il est assez rare que les consoles de jeu incorporent des coprocesseurs arithmétiques. Il existe cependant des exceptions. Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
Un autre exemple est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur.
Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire.
[[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]]
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4ᵉ registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
du0t2ej926785crg9375npdesr04cyd
763644
763643
2026-04-13T20:34:05Z
Mewtow
31375
/* Les coprocesseurs arithmétiques : quelques généralités */
763644
wikitext
text/x-wiki
Les '''coprocesseurs''' sont des processeurs secondaires qui complémentent un processeur principal. Ils permettent de déléguer certains calculs à un processeur secondaire, afin de décharger le processeur principal. Un détail important est que le processeur principal et le coprocesseur sont très différents : ils n'ont pas le même jeu d'instruction, n'ont pas les mêmes performances, et bien d'autres différences. Nous en avions déjà vu dans le chapitre sur l'architecture de base, où nous avions analysé d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail
==Les différents types de coprocesseurs==
Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.
Les '''coprocesseurs sonores''' sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.
Les '''coprocesseur d'IO''' sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.
[[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]]
Les '''coprocesseurs arithmétiques''' sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.
===Les coprocesseurs faiblement et fortement couplés===
À ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les seconds sont intégrés sur le bus mémoire.
Avec les '''coprocesseurs fortement couplés''', le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur, dont l'encodage est différent. Les deux surveillent le bus mémoire et décident à qui est destinée l'instruction. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.
Pour cela, le CPU envoie son ''program counter'' sur le bus d'adresse, ce qui entraine l'apparition d'une instruction sur le bus de données. Là, le processeur et le coprocesseur reçoivent l'instruction et la décodent. Si l'instruction est destinée au processeur, le CPU l'exécutera, alors que le coprocesseur la traitera comme un NOP. Et inversement si l'instruction est destinée au coprocesseur.
Les '''coprocesseurs faiblement couplés''' sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du ''pooling'' ou des ''interruptions inter-processeurs''. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.
Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.
Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.
[[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]]
Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suit, qui sont un mélange des deux solutions.
===Les coprocesseurs arithmétiques : quelques généralités===
Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. Il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.
Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants Ils étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.
Le problème ne se pose pas sur les consoles de jeu, mais il est assez rare que les consoles de jeu incorporent des coprocesseurs arithmétiques. Il existe cependant des exceptions. Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.
==Le jeu d'instruction x87 d'Intel==
Un exemple de coprocesseur arithmétique est celui de l'extension x87 qui a ajouté les nombres flottants aux processeurs x86. Pour rappel, les processeurs x86 sont ceux qui sont présents à l'intérieur des PC, à savoir tous les processeurs Intel et AMD actuels. Il s'agit d'un jeu d'instruction qui a reçu de nombreux ajouts au cours du temps. Les ajouts en question sont appelés des '''extensions x86'''. Nous allons maintenant voir celle qui a ajouté la gestion des flottants au x86. L'extension x87 n'est plus utilisée depuis l'arrivée des CPU 64 bits, car elle a été remplacée par l'extension SSE.
Sur les tout premiers processeurs x86, le support des nombres flottants n'était pas implémenté. A la place, ils utilisaient des coprocesseurs arithmétiques, appelés des '''coprocesseurs x87'''. Ils travaillaient en tandem avec un processeur x86 normal, et ne géraient que des instructions arithmétiques sur des flottants. Ils étaient capables d'exécuter les 4 opérations de base (add, sub, mul, div), la racine carrée, les opérations trigonométriques sinus, cosinus et tangente, l'arc tangente, et des instructions de calcul de logarithmes ou d'exponentielles.
===La pseudo-pile de registres===
Les coprocesseurs arithmétiques x87 avaient 8 registres flottants, qui étaient gérés avec une pseudo-pile, ainsi que 3 registres de contrôle. Les 8 registres x87 sont ordonnés et numérotés de 0 à 7. Les registres x87 sont organisés sous la forme d'une pile de registres, similaire à une pile d'assiette, que l'on remplit dans un ordre bien précis. Lors du chargement d'un opérande de la RAM vers les registres, il n'est pas possible de décider du registre de destination. Si on veut ajouter des flottants dans nos registres, on doit les « remplir » dans un ordre de remplissage imposé : on remplit d'abord le registre 7, puis le 6, puis le 5, et ainsi de suite jusqu'au registre 0. Si on veut ajouter un flottant dans cette pile de registres, celui-ci sera stocké dans le premier registre vide dans l'ordre de remplissage indiqué au-dessus.
Prenons un exemple, les 3 premiers registres sont occupés par un flottant et on veut charger un flottant supplémentaire : le 4ᵉ registre sera utilisé pour stocker ce flottant. La même chose existe pour le « déremplissage » des registres. Imaginez que vous souhaitez déplacer le contenu d'un registre dans la mémoire RAM et effacer complètement son contenu. On ne peut pas choisir n'importe quel registre pour faire cela : on est obligé de prendre le registre non vide ayant le numéro le plus grand.
{|
|[[File:Pseudo-pile x87. - PUSH.png|vignette|upright=2|Pseudo-pile x87 - chargement d'une opérande.]]
|[[File:Pseudo-pile x87 - POP.png|vignette|upright=2|Pseudo-pile x87 - retrait d'une opérande.]]
|}
Les instructions à un opérande dépilent le flottant au sommet de la pile. Les instructions dyadiques peuvent dépiler les deux opérandes au sommet de la pile, mais elles peuvent aussi utiliser d'autres modes d'adressage. Elles peuvent aller chercher la seconde opérande en RAM, en fournissant une adresse. Mais elles peuvent aussi adresser n'importe quel autre registre de la pile en fournissant son numéro de registre. Avec ce dernier mode d'adressage, le processeur agit comme une sorte de processeur à accumulateur, avec le sommet de la pile servant d'accumulateur.
En somme, les registres de la pile sont adressables, du moins pour ce qui est de gérer la seconde opérande. C'est cette particularité qui vaut le nom de pseudo-pile à cette organisation à mi-chemin entre une pile et une architecture à registres.
===Les instructions x87===
Maintenant, voyons quelles instructions les FPU x87 devaient gérer. L'extension x87 est en effet un jeu d'instruction, qui décrit quelles instructions doivent être gérées. Les instructions utilisent des opcodes inutilisés dans le jeu d'instruction x86, qui sont détournés pour fonctionner sur le x87.
On trouve évidemment des instructions de calculs, bien évidemment compatibles avec la norme IEE754 : l'addition FADD, la soustraction FSUB, la multiplication FMUL et la division FDIV. Mais il y a aussi la racine carrée FSQRT. La FPU x87 implémente aussi des instructions trigonométriques, qui ne sont pas supportées par la norme IEEE 754. Elle gérait l'instruction FCOS pour le cosinus, l'instruction FSIN pour le sinus, l'instruction FPTAN pour la tangente, l'instruction FPATAN pour l'arc tangente, ainsi que des instructions de calcul de logarithmes ou d'exponentielles.
La FPU x87 dispose aussi d'instructions de comparaisons compatibles avec la norme IEEE 754, capables de comparer le flottant au sommet de la pile avec un autre nombre qui peut être flottant ou entier ! Voici une liste de quelques instructions de comparaisons supportées par les FPU 87 :
* FCOM : compare le contenu du registre 0 avec une constante flottante ;
* FCOMI : compare le contenu des registres 0 et 1 ;
* FICOM : compare le contenu du registre 0 avec une constante entière ;
* FTST : compare le registre numéroté 0 avec la valeur 0.
Le x87 avait aussi des instructions de calcul de la valeur absolue (FABS) ou encore de changement de signe (FCHS).
Les instructions de calcul n'ayant besoin que d'un seul flottant pour s'exécuter, comme les opérations trigonométriques ou la valeur absolue, utilisent le flottant situé au sommet de la pile. Les instructions dyadiques (multiplication, addition, soustraction et autres) vont agir différemment suivant la situation. Elles peuvent prendre les deux flottants les plus haut placés dans cette pile, prendre le flottant au sommet de la pile, utiliser une donnée en provenance de la mémoire, ou encore utiliser le flottant le plus haut placé et un flottant stocké dans l'importe quel registre de cette pile de registres. La pile de registre était donc une sorte de mélange entre un accumulateur et 7 registres adressables.
Pour charger des opérandes dans la pile d'opérande, l'extension x87 fournit trois instructions d'accès mémoire.
{|class="wikitable"
|-
! Instruction
! Description
|-
! FLD
| Charge un nombre flottant depuis la mémoire vers notre pile de registres vue au-dessus. Cette instruction peut charger un flottant codé sur 32 bits, 64 bits ou 80 bits
|-
! FSTP
| Déplace le contenu d'un registre vers la mémoire. Une autre instruction existe qui est capable de copier le contenu d'un registre vers la mémoire sans effacer le contenu du registre : c'est l'instruction FST
|-
! FXCH
| Échange le contenu du dernier registre non vide dans l'ordre de remplissage (celui situé au sommet de la pile) avec un autre registre
|}
D'autres instructions existent qui chargent certaines constantes (PI, 1, 0, certains logarithmes en base 2) dans le registre au sommet de la pile de registres.
===Les registres de contrôle et d'état===
Pour gérer la pseudo-pile, les registres pour les flottants sont associés à un registre d'état nommé '''''Tag Word'''''. Le registre ''Tag Word'' indique, pour chaque registre flottant, s'il est vide ou non. Avouez que c'est pratique pour gérer la pile de registres vue au-dessus ! Le registre ''tag word'' fait 16 bits, ce qui fait 2 bits pour chacun des 8 registres. Ces deux bits contiennent des informations sur le contenu du registre de données réservé.
* Si ces deux bits valent 00, le registre contient un flottant « normal » différent de zéro ;
* Si ces deux bits valent 01, le registre contient une valeur nulle : 0 ;
* Si ces deux bits valent 10, le registre contient un NAN, un infini, ou un dénormal ;
* Si ces deux bits valent 11, le registre est vide et ne contient pas de nombre flottant.
Le processeur x87 contient aussi deux registres d'état, nommés ''Control Word'' et ''Status Word''. Le registre ''Status Word'' contient quelques bits, certains utilisés pour gérer la pseudo-pile, d'autres non. Il fait lui aussi 16 bits et c'est un registre d'état qui est utilisé pour qu'un programme puisse comprendre la cause d'une exception. Il contient le numéro du registre juste au-dessus du sommet de la pile, le numéro du premier registre vide dans l'ordre de remplissage. Mais il contient surtout des bits mis à 1 en cas de débordement de flottant, de division par zéro, lorsqu'un calcul a pour résultat un dénormal, etc.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! TOP
| Trois bits, qui codent le numéro du premier registre vide dans la pile de registre
|-
! U
| Détecte les ''underflows'' : est mis à 1 en cas d'''underflows''.
|-
! O
| | Détecte les ''overflows'' : est mis à 1 en cas d'''overflows''.
|-
! Z
| Prévient qu'une division par zéro a eu lieu. Est mis à 1 si c'est le cas.
|-
! D
| Bit est mis à 1 lorsqu'un résultat de calcul est un dénormal ou lorsqu'une instruction doit être exécutée sur un dénormal
|-
! I
| Bit mis à 1 lors de certaines erreurs telles que l'exécution d'une instruction de racine carrée sur un négatif ou une division du type 0/0
|}
Le registre ''Control Word'' fait 16 bits et configure la gestion des arrondis.
{|class="wikitable"
|-
! Bit !! Utilité
|-
! Infinity Control
| S'il vaut zéro, les infinis sont tous traités comme s'ils valaient +∞. S'il vaut un, les infinis sont traités normalement
|-
! Rouding Control
| C'est un ensemble de deux bits qui détermine le mode d'arrondi utilisé
* 00 : vers le nombre flottant le plus proche : c'est la valeur par défaut ;
* 01 : vers - l'infini ;
* 10 : vers + l'infini ;
* 11 : vers zéro
|-
! Precision Control
|`Ensemble de deux bits qui détermine la taille de la mantisse de l'arrondi du résultat d'un calcul. En effet, on peut demander à notre FPU d'arrondir le résultat de chaque calcul qu'elle effectue. Cette instruction ne touche pas à l'exposant, mais seulement à la mantisse. La valeur par défaut de ces deux bits est 11 : notre FPU utilise donc des flottants double précision étendue. Les valeurs 00 et 10 demandent au processeur d'utiliser des flottants non pris en compte par la norme IEEE 754.
* 00 : mantisse codée sur 24 bits ;
* 01 : valeur inutilisée ;
* 10 : mantisse codée sur 53 bits ;
* 11 : mantisse codée sur 64 bits
|}
Les instructions x87 sont codées sur au minimum deux octets. Le premier octet commence toujours la suite de bit 11011, qui indique que c'est une instruction destinée au coprocesseur. Le 11011 était appelé le code d'échappement, sa mnémonique en assembleur était ESC. Le tout est suivi par 6 bits d'opcode, et 5 bits pour le mode d'adressage. Le tout était regroupé comme suit : 1101 1xxx aaxx xaaa, avec x les bits de l'opcode et a pour le mode d'adressage. Le tout était suivi par des opérandes, selon le mode d'adressage.
===Les doubles arrondis avec les registres x87===
Les nombres flottants sont standardisés par l'IEEE, avec le standard IEEE754. La norme impose deux formats de flottants : les flottants 32 bits et les flottants 64 bits. Cependant, la FPU x87 utilisait des flottants codés sur 80 bits, non-standardisés par l'IEEE754, ce qui posait quelques problèmes.
Les instruction FLD et FSTP sont l'équivalent des instructions LOAD et STORE, mais pour les flottants x87. Elles peuvent lire/écrire soit un flottant 32 bits, soit un flottant 64 bits, soit un flottant 80 bits. En pratique, les compilateurs préféraient utiliser des flottants 32 et 64 bits, les flottants 80 bits n'étaient pas utilisés. Les raisons sont assez diverses : une meilleure performance, des histoires de compatibilité et de standard IEEE754, etc.
Le problème est qu'utiliser des flottants 32/64 bits impose de faire des arrondis lors des accès mémoire. Lors d'une lecture d'un flottant 32/64 bit, celui-ci est étendu sur 80 bits. Et inversement pour l'écriture : les flottants 80 bits sont arrondis pour rentrer dans 32/64 bits. Et faire des calculs intermédiaires sur 80 bits avant de les arrondir ne donne pas le même résultat que si on avait fait les calculs sur 32 ou 64 bits nativement. Les résultats intermédiaires ont une précision supérieure, donc le résultat peut être différent.
[[File:Phénoméne de double arrondi sur les coprocesseurs x87.png|centre|vignette|upright=3.0|Phénomène de double arrondi sur les coprocesseurs x87]]
Pour citer un exemple, sachez que des failles de sécurité de PHP et de Java aujourd'hui corrigées étaient causées par ces arrondis supplémentaires. Pour limiter la casse, il existe une solution : sauvegarder tout résultat d'un calcul sur un flottant directement dans la mémoire RAM. Comme cela, on se retrouve avec des calculs effectués uniquement sur des flottants 32/64 bits ce qui supprime pas mal d'erreurs de calcul. Mais le cout en performance est tellement drastique que cette solution n'est que rarement utilisée.
==Les coprocesseurs x87 et leurs précurseurs==
Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.
===Les précurseurs : l'Intel 8231 et le 8232===
L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La [http://ep.homeserver.hu/PDF/AM9511A-9512.pdf documentation AMD donnait même des exemples assez variés]. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via ''pooling'', interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.
L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).
: La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
[[File:C8231A FPU PIN CONFIGURATION.png|thumb|Intel 8231 FPU PIN CONFIGURATION.]]
Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée ''chip select'' pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.
Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.
Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.
{|class="wikitable"
|-
! A0, RD, WR !! Action
|-
| 000 || Lecture de l'octet depuis les registres de données.
|-
| 010 || Écriture de l'octet dans les registres de données.
|-
| 111 || L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
|-
| 101 || Lecture du registre d'état.
|}
Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.
* READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
* END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
* EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.
Ce système pouvait être utilisé avec du ''pooling'', avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le ''pooling'', le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.
Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !
Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.
===L'intel 8087 et ses successeurs===
[[File:Intel 8087.svg|vignette|Intel 8087]]
Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.
L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.
Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.
Le processeur central lisait des instructions, en envoyant le ''program counter'' sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.
Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.
Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.
Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.
Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.
Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.
[[File:Intel 8087 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 8087.]]
Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).
De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.
L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.
[[File:Intel 387 arch.svg|centre|vignette|upright=2.5|Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.]]
L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.
Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.
Pour finir, voici quelques liens sur la microarchitecture du 8087 :
* [https://www.righto.com/2026/02/8087-instruction-decoding.html Instruction decoding in the Intel 8087 floating-point chip].
* [https://www.righto.com/2020/05/die-analysis-of-8087-math-coprocessors.html Die analysis of the 8087 math coprocessor's fast bit shifter].
* [https://www.righto.com/2025/12/8087-stack-circuitry.html The stack circuitry of the Intel 8087 floating point chip, reverse-engineered].
* [https://www.righto.com/2025/12/8087-microcode-conditions.html Conditions in the Intel 8087 floating-point chip's microcode].
* [https://www.righto.com/2020/05/extracting-rom-constants-from-8087-math.html Extracting ROM constants from the 8087 math coprocessor's die].
* [https://www.righto.com/2018/09/two-bits-per-transistor-high-density.html Two bits per transistor: high-density ROM in Intel's 8087 floating point chip].
===Les coprocesseurs x87 de Weitek===
Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.
Le '''Weitek 1067''' avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le '''Weitek 1167''' regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le '''Weitek 2167''' regroupa les trois pièces détachées dans un seul circuit imprimé.
Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.
Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.
[[File:Weitek WTL1167 arch.svg|centre|vignette|upright=2.5|Weitek WTL1167.]]
Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.
* Le signal CSL est le ''Chip Select Load'', ce qui indique qu'il est mis à 1 lors d'une lecture.
* Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
* Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
* Le registre L3 indique quel est le registre de destination : A ou B.
Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
Le '''Weitek 3167''' était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le '''Weitek 4167''' était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.
Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.
Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.
===Le Motorola 68881 et le 68882===
Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.
Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée ''Chip Select'' commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des ''coprocessor interface registers'' (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.
Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.
Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les sections critiques et le modèle mémoire
| prevText=Les sections critiques et le modèle mémoire
| next=L'accélération matérielle de la virtualisation
| nextText=L'accélération matérielle de la virtualisation
}}
</noinclude>
cay6o44finkjcut613r9wiv7bxa7rp8
Mathc initiation/005u
0
83796
763593
2026-04-13T15:33:33Z
Xhungab
23827
news
763593
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/Fichiers h : c44a4| Sommaire]]
{{Partie{{{type|}}}|'''Dérivée d'une fonction implicite'''}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c67|* Dérivée d'une fonction implicite (x,y)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c68|* Dérivée d'une fonction implicite (x,y,z)]]}}
{{AutoCat}}
ea2wgouiy2nwi3je7nmwz8dlmkifcmv
Mathc initiation/005v
0
83797
763657
2026-04-14T08:06:51Z
Xhungab
23827
news
763657
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/Fichiers h : c44a4| Sommaire]]
{{Partie{{{type|}}}|'''Dérivées partielles. Méthode de Newton (en xy)'''}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c25|* Dérivées partielles (en xy)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c24|* Méthode de Newton (en xy)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c26|* Dérivées partielles (en xyz)]]}}
{{AutoCat}}
4vfwsmdugzrqm2l1nzz05ege3hxxzxd
763670
763657
2026-04-14T08:27:12Z
Xhungab
23827
763670
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/Fichiers h : c44a4| Sommaire]]
{{Partie{{{type|}}}|'''Dérivées partielles. Méthode de Newton (en xy)'''}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c25|* Dérivées partielles (en xy)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c24|* Méthode de Newton (en xy)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c26|* Dérivées partielles (en xyz)]]}}
{{Partie{{{type|}}}|'''Quelques applications'''}}
{{Partie{{{type|}}}|[[Mathc initiation/a511|* Matrice hessienne]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c74a3|* Local minimum , local maximum , point-selle]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c66a3|* Calculer les local minimum , local maximum , point-selle]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c37a2|* Dessiner une tangente sur une fonction f(x,y)]]}}
{{AutoCat}}
rb6v7evp5bav4o0sogqn29412b18g8p
Mathc initiation/005w
0
83798
763663
2026-04-14T08:21:15Z
Xhungab
23827
news
763663
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/Fichiers h : c44a4| Sommaire]]
{{Partie{{{type|}}}|'''Calculer le gradient et quelques applications'''}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c27|* Le gradient au point p (en xy et xyz)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c28|* La dérivée directionnelle (en xy et xyz)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c29|* Plan tangent en P0 pour une fonction f(x,y,z)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c24a4|* Dessiner le vecteur orthogonal au point P]]}}
{{Partie{{{type|}}}|[[Mathc initiation/a60|* Dessiner le plan orthogonal au point P]]}}
{{AutoCat}}
co81eve37w1lhkrfobm5qz471wu7suv