Skip to content

Niveau 0 · Chapitre 0.2

De la touche au pixel

Une frappe, de l’interrupteur aux pixels : matrice de touches, rapports HID USB et scrutation, interruption et pilote, serveur de fenêtres, boucle d’événements, rendu du texte, framebuffer et rafraîchissement de l’écran, avec un budget de latence fait uniquement de chiffres vérifiables.

Vous tapez H dans un éditeur de texte et un H apparaît. Le trajet complet prend quelques dizaines de millisecondes, et il passe par un microcontrôleur dans le clavier, un contrôleur hôte USB, une interruption, un pilote du noyau, un processus système qui possède l’écran, la boucle principale de votre application, un moteur de rendu de polices, le GPU, et une dalle qui se redessine des dizaines de fois par seconde.

Ce chapitre suit la frappe de bout en bout, une étape à la fois, et se termine par le temps que chaque étape peut prendre. La même route, à quelques détails près, transporte chaque clic de souris et chaque toucher.

Le chemin complet

ÉtapeOùCe qui se passe
1. Interrupteurclavierun contact se ferme dans une grille de lignes et de colonnes
2. Contrôleur du clavierclavierun microcontrôleur balaie la grille et construit un rapport
3. USBbusle contrôleur hôte interroge le clavier et reçoit le rapport
4. Interruption et pilotenoyaule CPU est interrompu ; le pilote HID transforme les rapports en événements clavier
5. Serveur de fenêtresprocessus systèmel’événement va à la fenêtre active, traduit en caractère
6. Boucle d’événementsapplicationl’application insère le caractère et marque une partie de sa fenêtre à redessiner
7. Rendu du texteapplicationle contour de la lettre dans la police devient des pixels
8. Compositionserveur de fenêtres + GPUtoutes les fenêtres sont combinées en une image, le framebuffer
9. Balayageécranle contrôleur d’affichage envoie le framebuffer à la dalle, ligne par ligne

Voici le même trajet en animation. Chaque étape indique quel niveau de la machine fait le travail, et à quoi ressemble la frappe à ce moment-là : un contact fermé, quelques octets, un événement, un caractère, des pixels, de la lumière.

Voyage · Appuyer sur une touche

À essayer : Appuyez sur Lecture pour suivre l’action à travers les niveaux, ou avancez à votre rythme avec les flèches et les points.

  1. 0
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
1 / 9Niveau 8 · Composants et puces

Un contact se ferme

Sous la touche, un interrupteur ferme un contact au croisement d’une ligne et d’une colonne d’une grille de fils.

ligne × colonne

1–2. Dans le clavier

Un clavier n’a pas un fil par touche. Les interrupteurs sont placés aux croisements d’une matrice de lignes et de colonnes, et un petit microcontrôleur la balaie en permanence, une ligne à la fois. Les contacts mécaniques rebondissent pendant quelques millisecondes après un appui, donc le contrôleur fait de l’anti-rebond (debouncing) : il ne croit à un nouvel état qu’une fois celui-ci stable depuis un moment. Le chapitre claviers et écrans démonte la matrice et l’anti-rebond, démo à l’appui.

Sur le clavier de PC de l’époque IBM, chaque appui et chaque relâchement provoquaient une interruption, et le gestionnaire lit le numéro de la touche, de 1 à 102, dans le contrôleur du clavier. C’est toujours la bonne image de l’information : l’ordinateur apprend que des touches physiques s’enfoncent et se relèvent, et toute l’interprétation, y compris les combinaisons avec Maj et Ctrl, est faite par le logiciel. Mais les claviers USB et Bluetooth d’aujourd’hui n’interrompent personne.

3. USB : c’est l’ordinateur qui demande

Un clavier USB est un périphérique HID (human interface device) ; les claviers Bluetooth utilisent les mêmes rapports HID sur une liaison radio. Un périphérique USB ne parle jamais le premier : le contrôleur hôte, un circuit de l’ordinateur, lui demande des nouvelles à un intervalle fixe que le périphérique réclame lors du branchement. Le rapport clavier de base fait 8 octets : un octet de bits de modificateurs (Ctrl, Maj, Alt et ⌘ ou ⊞, à gauche et à droite), un octet réservé, et jusqu’à six touches actuellement enfoncées, sous forme de codes d’usage : 0x04 pour A, 0x05 pour B… 0x0B pour H. Appuyer sur Maj+H produit 02 00 0B 00 00 00 00 00. Le rapport dit quelles touches sont enfoncées, pas quelle touche vient d’être pressée : l’ordinateur découvre un appui en comparant chaque rapport au précédent.

À quelle fréquence l’hôte demande-t-il ? Le clavier de ce Mac est un clavier Bluetooth, mais un récepteur USB sans fil Logitech est branché aussi, et il se présente, entre autres, comme un clavier. ioreg, l’outil de macOS qui liste le matériel, montre l’intervalle qu’il a demandé :

"Product" = "USB Receiver"
"PrimaryUsage" = 6            ← 6 = keyboard
"ReportInterval" = 1000       ← microseconds: polled every 1 ms

On dit parfois qu’un clavier USB est interrogé toutes les 50 ms. En pratique, les claviers demandent des intervalles bien plus courts (1 ms ici, et en général entre 1 et 10 ms), et la scrutation est faite par le contrôleur hôte, en matériel, pas par le système d’exploitation. L’USB lui-même a évolué : l’USB 3.0 fonctionnait à 5 Gbit/s, alors que l’USB4 atteint aujourd’hui 40 ou 80 Gbit/s sur le même genre de câble. Un clavier n’en a nul besoin : la plupart utilisent encore les vitesses les plus lentes de l’USB.

4. Interruption et pilote

Quand un rapport arrive avec du nouveau, le contrôleur hôte l’écrit en mémoire et déclenche une interruption. Sur les machines actuelles, c’est une interruption signalée par message (MSI), une écriture mémoire spéciale, comme le décrit le chapitre sur les déroutements et les interruptions. Le CPU arrête ce qu’il exécutait, sauvegarde son état et saute dans le noyau.

Le pilote HID du noyau compare le nouveau rapport au précédent et produit des événements : « l’usage 0x0B vient de s’enfoncer, avec Maj gauche maintenue ». Ces événements vont dans une file que le reste du système viendra lire, et le programme interrompu reprend sans se douter de rien. Si vous maintenez la touche, le clavier continue simplement de la signaler comme enfoncée ; la répétition automatique est produite par le logiciel, qui décide combien de temps attendre avant la première répétition et à quel rythme répéter ensuite.

Sous macOS, le service d’événements HID du système transmet ensuite l’événement au serveur de fenêtres. Sous Linux, le pilote publie l’événement sur un fichier de périphérique sous /dev/input/, où le serveur d’affichage le lit.

5. Le serveur de fenêtres aiguille l’événement

Une frappe ne sait pas à quel programme elle est destinée. Le serveur de fenêtres en décide : sous macOS, c’est un processus nommé WindowServer ; sous Linux, le compositeur Wayland (ou le serveur X) ; sous Windows, le gestionnaire de fenêtres intégré au système. Il sait quelle fenêtre a le focus clavier et lui remet l’événement.

Quelque part en chemin, une touche physique devient un caractère. L’usage 0x0B est toujours « la touche à l’emplacement du H » ; sur une disposition AZERTY ou Dvorak, cette touche produit une autre lettre. La disposition du clavier associe touches et modificateurs à des caractères, et une méthode de saisie peut combiner plusieurs frappes en un seul caractère : c’est ainsi que les touches mortes produisent é, et qu’on tape le chinois ou le japonais.

6. La boucle d’événements de l’application

Toute application graphique est construite autour d’une boucle d’événements. Le fil d’exécution principal demande au système l’événement suivant, le traite, et redemande ; quand il n’y a rien à faire, il dort dans un appel système, sans rien coûter :

for (;;) {
    Event e = wait_for_next_event();   /* sleeps until something happens */
    handle(e);                         /* e.g. insert a character */
    if (something_changed) redraw();
}

Chaque framework l’habille à sa façon (la run loop de NSApplication sous macOS, GetMessage et DispatchMessage sous Windows, la boucle principale de GTK ou de Qt sous Linux, la boucle d’événements qui exécute chaque fonction de rappel JavaScript dans un navigateur), mais c’est toujours cette boucle. Le gestionnaire d’une frappe insère le caractère dans le document et marque la partie de la fenêtre qui a changé comme à redessiner. Il ne dessine pas tout de suite : le dessin a lieu une fois par image, pour tous les changements à la fois.

C’est aussi pourquoi une application se « fige ». Si un gestionnaire prend deux secondes (une lecture de fichier lente sur le fil principal, un long calcul), la boucle ne revient pas chercher l’événement suivant, et rien ne change à l’écran avant qu’elle le fasse.

7. Le rendu du texte

Transformer « H » en pixels est un petit pipeline à part entière, qui travaille sur des caractères Unicode comme le décrit le chapitre caractères et texte :

  1. Choix de la police : trouver une police qui a un glyphe pour ce caractère, en se rabattant sur d’autres polices pour les emoji ou les écritures que la police principale ne couvre pas.
  2. Mise en forme (shaping) : transformer la suite de caractères en une suite de glyphes positionnés. Pour le texte latin, ce sont surtout des largeurs d’avance et du crénage (« AV » est plus serré que « AH ») ; en arabe ou en devanagari, la forme d’un caractère dépend de ses voisins.
  3. Rastérisation : un glyphe est stocké sous forme de contours mathématiques (des courbes) et converti en une petite grille de pixels à la taille voulue, avec anticrénelage : les pixels du bord reçoivent une couverture partielle, pour que les courbes paraissent lisses.
  4. Mise en cache : les glyphes rastérisés sont gardés dans un cache de glyphes, souvent une texture sur le GPU, si bien que le H suivant à la même taille ne coûte qu’une copie.

8. La composition et le framebuffer

L’application ne possède pas l’écran. Elle dessine sa fenêtre dans son propre tampon, et le compositeur du serveur de fenêtres combine toutes les fenêtres visibles, avec leurs ombres, leur transparence et leurs animations, en une image finale, généralement sur le GPU. Cette image finale est le framebuffer (tampon d’image) : une valeur de couleur par pixel, en général 4 octets (rouge, vert, bleu, et alpha ou remplissage).

Ce Mac pilote deux écrans de 5120 × 1440 pixels (le chapitre claviers et écrans fait les mêmes calculs). Une image pour l’un d’eux fait 5120 × 1440 × 4 = 29 491 200 octets, environ 29,5 Mo, et à 60 images par seconde le contrôleur d’affichage lit 1,77 Go/s en mémoire rien que pour garder un écran allumé.

Un exemple classique en 1080p est bien plus petit, et sa mémoire est ailleurs : une image de 1920 × 1080 à 3 octets par pixel, 6,2 Mo, stockée dans une mémoire vidéo sur la carte du contrôleur d’affichage. Les cartes graphiques séparées ont toujours leur propre mémoire, mais sur puce Apple, et sur la plupart des portables et des téléphones, le framebuffer vit dans la même mémoire principale que tout le reste, partagée par le CPU, le GPU et le contrôleur d’affichage.

Pour ne jamais montrer une image à moitié dessinée, le système utilise au moins deux tampons : l’écran affiche l’un pendant que l’image suivante est dessinée dans l’autre, et on les échange entre deux images. C’est le double tampon (double buffering), et l’échange est calé sur le rafraîchissement de l’écran : la synchronisation verticale (vsync).

9. Le balayage et l’écran

L’écran n’affiche pas une image d’un seul coup. Le contrôleur d’affichage l’envoie ligne par ligne, de haut en bas, à une fréquence de rafraîchissement fixe ; quand la dernière ligne est partie, il recommence. system_profiler SPDisplaysDataType montre les écrans de ce Mac à 60,00 Hz :

LG HDR DQHD:
  Resolution: 5120 x 1440
  UI Looks like: 5120 x 1440 @ 60.00Hz

Les écrans ont longtemps été repeints 60 fois par seconde, avec des LCD à cristaux nématiques torsadés (TN) et l’OLED comme nouveauté prometteuse. Le 60 Hz reste courant, mais les téléphones, les portables et les écrans de jeu tournent désormais à 120 Hz ou plus, souvent avec un rafraîchissement variable qui baisse quand rien ne bouge, pour économiser l’énergie. Et les dalles OLED sont maintenant la norme sur les téléphones et fréquentes sur les portables et les moniteurs.

Le budget de latence

Voici les étapes dont le coût est fixe et connaissable sur cette machine :

ÉtapeDuréeSource
scrutation USB (le récepteur ci-dessus)jusqu’à 1 ms d’attenteReportInterval = 1 000 µs
attente de l’image suivante à 60 Hzde 0 à 16,7 ms1 / 60 s = 16,7 ms par image
la même à 120 Hzde 0 à 8,3 ms1 / 120 s = 8,3 ms par image
balayage d’une image à 60 Hzenviron 16,7 ms de la ligne du haut à celle du basla période de rafraîchissement

Tout le reste varie trop pour qu’on lui donne un seul chiffre : le balayage et l’anti-rebond du clavier, le protocole radio d’une liaison sans fil, l’occupation de la boucle d’événements de l’application, le nombre d’images que le compositeur met en tampon, et le temps de réponse de la dalle elle-même. Mesuré de bout en bout, de la touche qui touche le fond à la lumière qui change à l’écran, un ordinateur typique met quelques dizaines de millisecondes, et bien plus quand un maillon de la chaîne est lent. L’horloge de l’écran domine la partie fixe : à 60 Hz, attendre l’image suivante puis la balayer peut coûter jusqu’à environ 33 ms, ce qui explique qu’un rafraîchissement plus rapide rende la frappe et le défilement plus agréables même quand rien d’autre ne change.

À retenir

  • Une frappe va matrice de touches → microcontrôleur du clavier → USB → interruption → pilote HID → serveur de fenêtres → boucle d’événements → rendu du texte → compositeur → framebuffer → écran.
  • Les claviers USB sont scrutés par le contrôleur hôte : un rapport HID de 8 octets donne les modificateurs et jusqu’à six touches enfoncées ; le récepteur de ce Mac demande à être interrogé toutes les 1 ms, pas toutes les 50 ms.
  • Les touches deviennent des caractères tard : la disposition du clavier et la méthode de saisie sont du logiciel, du ressort du serveur de fenêtres.
  • Toute application graphique fait tourner une boucle d’événements ; un gestionnaire lent fige l’application parce que la boucle ne peut pas passer à l’événement suivant.
  • Le rendu du texte, c’est choix de la police, mise en forme, rastérisation et mise en cache ; le résultat est composé dans un framebuffer (29,5 Mo par image en 5120 × 1440) en mémoire principale sur puce Apple, pas dans une mémoire vidéo séparée.
  • L’écran se rafraîchit à fréquence fixe (16,7 ms par image à 60 Hz, 8,3 ms à 120 Hz), ce qui fixe un plancher au délai d’apparition de toute frappe.

Dans ce niveau

  1. 0.1Ce qui se passe quand on ouvre une application
  2. 0.2De la touche au pixel
  3. 0.3Ce qui se passe quand une page web se charge
  4. 0.4Applications, processus et fichiers
  5. 0.5Pourquoi mon ordinateur est-il lent ?