PARTIE II : Architecture et rendu

Chapitre 5 : GPU, Vulkan et animation procédurale

5.1 Introduction : le GPU comme co-processeur

Le GPU (Graphics Processing Unit) n’est plus un simple accélérateur de rendu : c’est un co-processeur massivement parallèle, capable d’exécuter des milliers de threads en même temps. Comprendre son architecture, et la façon dont il dialogue avec le CPU et le noyau OS, est déterminant pour un moteur FullDive qui doit tenir une latence motion-to-photon (MTP) sous les 20 ms.1

Ce chapitre couvre toute la pile graphique, de l’écriture d’un pilote pour un noyau personnalisé jusqu’aux techniques de rendu VR avancées, en passant par l’animation procédurale, cette interface visible entre le moteur physique et l’utilisateur.

5.2 Architecture des pilotes graphiques

5.2.1 Windows : WDDM (Windows Display Driver Model)

Introduit avec Windows Vista pour remplacer le modèle XDDM désormais obsolète, le WDDM (Windows Display Driver Model) impose une ségrégation stricte entre l’espace utilisateur et le noyau, répartie en trois couches :2

Sous-système WDDMRôle
UMD (User-Mode Driver)Fourni par le constructeur (NVIDIA, AMD). Traduit les appels DirectX/Vulkan en listes de commandes GPU, gère la compilation des shaders. Un crash ne tue que l’application.
Dxgkrnl.sysCœur noyau du sous-système graphique. Routeur et pont de sécurité entre l’UMD et le KMD.
KMD (Kernel-Mode Display Miniport)Fourni par le constructeur. Seul composant autorisé à manipuler directement les registres physiques du GPU.

Le noyau s’appuie sur deux services :

  • VidMm (Video Memory Manager) : Virtualise la mémoire GPU en segments (VRAM dédiée, aperture mappée, mémoire paginable). Gère l’éviction vers la RAM système quand la VRAM est saturée.
  • VidSch (Video Scheduler) : Ordonnance les paquets de commandes entre les applications. Garantit que le DWM (Desktop Window Manager) obtient ses ressources même sous charge GPU maximale.

L’innovation de WDDM 2.0 tient au GPUVA. Avant Windows 10, les tampons soumis par l’UMD devaient être « patchés » par le noyau pour corriger les adresses physiques, une opération coûteuse en CPU. WDDM 2.0 a introduit le GPU Virtual Addressing : chaque processus a un espace virtuel GPU immuable, et le pilote utilisateur forge des commandes liées à des adresses stables, ce qui supprime le patching. WDDM 2.6 a ensuite déporté l’ordonnancement lui-même sur un microprocesseur intégré au GPU (Hardware-Accelerated GPU Scheduling), ce qui réduit la latence de buffering.

5.2.2 Linux : DRM/KMS (Direct Rendering Manager / Kernel Mode Setting)

À l’opposé du monolithe structuré de Microsoft, l’architecture graphique Linux est modulaire. Le module central du noyau est le DRM (Direct Rendering Manager), conçu comme un arbitre exposant les cartes graphiques sous /dev/dri/cardX et /dev/dri/renderDX.

Le noyau divise la gestion en composantes distinctes :

  • KMS (Kernel Mode Setting) : Initialisation et modification de la topologie d’affichage physique. KMS manipule des entités logiques : Framebuffers (tampons de pixels), Planes (plans d’affichage matériels pour superposition sans coût CPU), CRTCs (contrôleurs de synchronisation verticale), et Connectors (liens physiques HDMI/DisplayPort).
  • GEM / TTM : Deux approches de gestion mémoire. Le GEM (Graphics Execution Manager), introduit par Intel, expose une interface simple, adaptée aux architectures mémoire unifiée (UMA). Pour les GPU discrets, le TTM (Translation Table Manager) gère la migration asynchrone des tampons entre RAM et VRAM.

Côté pipeline Vulkan sous Linux : l’application s’adresse au loader libvulkan.so, qui charge un pilote Mesa en mode utilisateur (RADV pour AMD, ANV pour Intel). Ce pilote compile le SPIR-V en assembleur GPU et construit les command buffers. Contrairement à Windows, pas de Dxgkrnl intrusif : les tampons partent directement au module noyau DRM via libdrm. Le compositeur Wayland, lui, utilise l’Atomic Modesetting : un commit atomique de propriétés d’affichage qui garantit l’absence d’états transitoires défectueux.

5.2.3 VirtIO-GPU pour les environnements virtualisés

Pour un noyau personnalisé s’exécutant dans QEMU/KVM, VirtIO-GPU fournit un GPU virtuel standardisé communiquant via des files d’attente circulaires partagées (Virtqueues). Le dispositif (Device ID 16) expose une interface PCI ou MMIO avec deux files principales : la controlq (requêtes de rendu) et la cursorq (curseur matériel).

La communication passe par trois zones de mémoire partagée : la table des descripteurs (adresses physiques des tampons), l’anneau disponible (nouvelles requêtes du pilote invité), et l’anneau utilisé (achèvements signalés par le dispositif).

Le cycle de vie complet de l’affichage 2D via VirtIO-GPU suit 5 étapes :

  1. Récupération de la topologie : VIRTIO_GPU_CMD_GET_DISPLAY_INFO → résolutions des scanouts.
  2. Création de la ressource : VIRTIO_GPU_CMD_RESOURCE_CREATE_2D avec format pixel (ex: B8G8R8A8_UNORM).
  3. Attachement du backing storage : VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING avec une liste scatter-gather de pages physiques invitées.
  4. Transfert des données : VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D copie les pixels modifiés vers l’hôte.
  5. Présentation : VIRTIO_GPU_CMD_RESOURCE_FLUSH déclenche l’affichage du rectangle de mise à jour.

Pour l’accélération 3D, deux backends sont disponibles : Virglrenderer (traduction OpenGL via Gallium3D/TGSI/NIR) et le protocole Venus (sérialisation directe des commandes Vulkan depuis le registre vk.xml de Khronos via VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB avec mémoire partagée hostmem).3

5.2.4 Pourquoi Linux a rattrapé Windows en gaming

L’analyse comparative des architectures WDDM et DRM/KMS permet de rationaliser une dichotomie historique longtemps justifiée :

Le fardeau de X11, d’abord. Pendant des décennies, le serveur X11 (X.org), conçu dans les années 1980 pour des stations de travail en réseau, monopolisait l’affichage Linux sans aucune notion de GPU 3D ni de synchronisation verticale. Le screen tearing était endémique : X11 ne coordonnait pas le rendu applicatif avec le balayage physique de l’écran. Les compositeurs externes (Compton, Picom, KWin) tentaient de résoudre le problème via l’extension XComposite, mais induisaient une surcharge massive et un input lag catastrophique. Pire : X11 voyait tous les moniteurs comme une seule surface logique, ce qui rendait les configurations multi-écran à taux de rafraîchissement différents fonctionnellement impossibles.

Le renversement est venu de Wayland, conçu précisément pour éliminer ces défauts. Le compositeur est le serveur d’affichage. L’Atomic Modesetting est imposé par design, ce qui éradique les déchirures nativement. Et le Direct Scan-out pour les applications plein écran contourne complètement la composition, ce qui ramène la latence au minimum théorique du matériel. Couplé à Proton (traduction DirectX→Vulkan), un système Linux moderne avec l’ordonnanceur EEVDF offre désormais des gains de 5-15 % de FPS par rapport à Windows sur les jeux intensifs, en éliminant les interférences des services de télémétrie et de la composition forcée du DWM.

5.3 Pipeline Vulkan et intégration noyau

5.3.1 Vulkan : philosophie bas niveau

Vulkan est une API graphique explicite. Là où OpenGL cache la gestion de la mémoire et la synchronisation derrière des abstractions automatiques, Vulkan expose tout au développeur : allocation de mémoire GPU, barrières de synchronisation, command buffers, descriptor sets. Cette explicité coûte cher en complexité de code, mais elle donne un contrôle total de la performance.4

Le pipeline de rendu Vulkan se décompose en :

graph LR
    A["Command Buffer Recording
(CPU)"] --> B["Queue Submission
(CPU → GPU)"] --> C["GPU Execution
(GPU)"] --> D["Presentation
(GPU → Display)"]
  • Command Buffers : Enregistrés à l’avance (potentiellement dans un thread secondaire), puis soumis en batch au GPU. L’enregistrement multi-threadé est un avantage majeur de Vulkan sur OpenGL.
  • Render Pass : Définit les attachements (color, depth, stencil) et les sous-passes. Le driver peut optimiser les transitions de layout mémoire.
  • Pipeline State Objects : Toute la configuration du pipeline (shaders, rasterizer, blending) est pré-compilée en un objet immuable, éliminant les changements d’état coûteux.

5.3.2 Synchronisation CPU-GPU

La synchronisation entre le CPU et le GPU est le point le plus critique pour la latence :

MécanismeDirectionUsage
FenceGPU → CPULe CPU attend que le GPU ait terminé un batch de commandes
SemaphoreGPU → GPUSynchronisation entre queues GPU (graphics → compute)
EventCPU ↔ GPUSignalisation fine dans un command buffer
Timeline SemaphoreBidirectionnelSynchronisation monotone avec compteur incrémental

Pour le FullDive, la technique du Late Latching (ou Late Update) est décisive. Le CPU établit un tampon persistant (persistently-mapped buffer) en VRAM. Un thread de suivi capteur indépendant met à jour les matrices de pose chaque milliseconde dans ce tampon, même après la soumission des commandes de rendu. Quand le GPU atteint le TimeWarp, il lit « en direct » les dernières matrices, ce qui ramène la latence MTP sous la milliseconde entre la dernière lecture capteur et l’utilisation GPU effective.5

5.4 CUDA et les limites du bare-metal

5.4.1 Architecture NVCC et fatbinary

Le compilateur NVIDIA nvcc orchestre un flux de travail en phases distinctes :

  1. Séparation du code : Les fonctions GPU (marquées __global__, __device__) sont extraites du code hôte. Le code hôte est compilé par GCC/MSVC.
  2. Compilation GPU en deux étapes : Le code GPU est traduit en PTX (assembleur textuel pour GPU virtuel générique), puis en SASS (binaire optimisé pour une architecture réelle : Turing, Ampere, Hopper). Les flags -arch et -code contrôlent cette génération.
  3. Encapsulation Fatbinary : Le PTX et les multiples SASS sont assemblés dans un conteneur unique (Fatbinary), intégré comme blob dans le fichier objet ELF (section .nv_fatbin).
  4. Transformation des appels : Les lancements <<<grid, block>>> sont remplacés par des appels à la Runtime API : cudaConfigureCall, cudaSetupArgument, cudaLaunch.

À l’exécution, __cudaRegisterFatBinary (identifié par le magic 0x466243b1) charge le binaire cubin approprié au GPU détecté. Si aucun SASS ne correspond, le PTX embarqué est compilé JIT par le pilote.

5.4.2 L’impasse bare-metal

Vouloir exécuter ce flux sans noyau OS hôte échoue : libcudart.a ne touche jamais directement les registres GPU. Toute opération (cudaMalloc, cudaMemcpy, cudaLaunchKernel) se traduit par des ioctl vers les nœuds /dev/nvidia0, /dev/nvidiactl, /dev/nvidia-uvm, fournis par le module noyau nvidia.ko.6 Ce pilote gère la GMMU (GPU Memory Management Unit), les interruptions matérielles, le contexte de sécurité et l’ordonnancement temporel : rien de tout cela ne se délègue à une bibliothèque statique.

5.4.3 L’alternative LibreCUDA et RMAPI

Le projet LibreCUDA démontre néanmoins qu’une interaction directe avec le GPU est possible, en contournant la Runtime API. Le pilote NVIDIA est architecturé autour de la RMAPI (Resource Manager API), un composant OS-indépendant. Sur les architectures modernes (Turing+), cette logique est déportée sur le GSP (GPU System Processor, RISC-V intégré au GPU).

LibreCUDA forge manuellement des ioctl (NV_ESC_RM_ALLOC, NV_ESC_RM_CONTROL) pour allouer la mémoire GPU, puis exploite les QMD (Queue Meta Data), des structures de commandes MMIO, pour lancer du code :

  1. Téléverser le binaire ELF CUDA dans la mémoire GPU allouée via RMAPI.
  2. Configurer les descripteurs matériels (mémoire partagée, stack, paramètres).
  3. Rédiger un paquet QMD et déclencher un Doorbell, une écriture dans un registre MMIO qui ordonne au processeur de commandes du GPU de lire la file et d’exécuter la charge.

Cette approche, fascinante académiquement, nécessite un volume de rétro-ingénierie prohibitif pour un OS d’apprentissage. La stratégie retenue pour LplKernel est d’utiliser CUDA dans l’espace utilisateur Linux pour le traitement BCI (Chapitre 7), et Vulkan Compute pour le moteur.

5.5 Motion Matching et animation procédurale

5.5.1 Le problème de l’animation traditionnelle

L’animation traditionnelle (blend trees, state machines) repose sur des graphes prédéfinis de transitions entre clips d’animation. L’approche a une limite : chaque transition doit être conçue explicitement par un animateur, et leur nombre croît quadratiquement avec le nombre d’états. Le système de « Magic Parkour » du Luminous Engine (Forspoken) le montre bien : une locomotion à très haute vélocité dans un environnement géométriquement dense est hors de portée de ces approches, parce que l’interaction entre les appuis, la posture, l’inertie et la topologie du terrain produit une combinatoire infinie.

Le Motion Matching, popularisé par Ubisoft (For Honor) et déployé industriellement dans Forspoken, est un algorithme qui sélectionne dynamiquement la meilleure pose d’animation dans une base de données, à chaque frame.7

5.5.2 L’algorithme

À chaque tick, le système :

  1. Extrait la feature vector courante : position, vitesse, direction du personnage, positions des pieds, trajectoire future souhaitée (générée par le joystick du joueur).
  2. Recherche le voisin le plus proche dans la base de données de motion capture, en minimisant une fonction de coût pondérée :

C(p,q)=Wti=1Ntdes,itdb,i2+Wpj=1Mpcur,jpdb,j2+Wvvcurvdb2C(p, q) = W_t \sum_{i=1}^{N} \|\mathbf{t}_{des,i} - \mathbf{t}_{db,i}\|^2 + W_p \sum_{j=1}^{M} \|\mathbf{p}_{cur,j} - \mathbf{p}_{db,j}\|^2 + W_v \|\mathbf{v}_{cur} - \mathbf{v}_{db}\|^2

tdes,i\mathbf{t}_{des,i} est la trajectoire future désirée, tdb,i\mathbf{t}_{db,i} la trajectoire encodée dans la base, p\mathbf{p} et v\mathbf{v} les positions et vitesses des articulations clés (pieds, hanches), et les facteurs WW permettent de privilégier la réactivité (trajectoire) ou le réalisme (position des pieds).

  1. Transitionne vers la pose sélectionnée avec un blend court (2-4 frames) et une synchronisation de phase.

5.5.3 Optimisation : k-D trees et phase matching

La recherche exhaustive dans une base de 100 000+ poses à 60 Hz est prohibitive. Deux optimisations s’imposent :

  • k-D Tree : Structure d’arbre partitionnant l’espace des features pour une recherche en O(logn)O(\log n) au lieu de O(n)O(n). L’alternative est la quantification vectorielle (clustering non-supervisé) qui réduit le nombre de candidats à évaluer.
  • Phase Matching : Ajouter un paramètre de phase cyclique (cycle de marche) qui synchronise la transition avec le rythme naturel de la locomotion, évitant les « glissements » où les pieds semblent patiner sur le sol (foot sliding).

5.5.4 Cinématique inverse (IK) procédurale

Le Motion Matching fournit la pose de base, et l’IK procédurale l’ajuste au terrain en temps réel :

  • FABRIK (Forward And Backward Reaching Inverse Kinematics) : Algorithme itératif qui résout la chaîne cinématique par allers-retours entre l’effecteur final et la racine. Contrairement aux méthodes jacobiennes (calculs trigonométriques lourds, singularités), FABRIK itère sur des lignes droites basées sur les distances inter-articulaires. Significativement plus rapide pour des dizaines de personnages simultanément.
  • Dual Quaternions : Représentation mathématique qui combine rotation et translation en un seul objet algébrique, éliminant les artefacts de « candy wrapper » (déformation en bonbon) du skinning linéaire classique. Les ingénieurs de Square Enix ont présenté des modèles utilisant les Dual Quaternions pour garantir que les volumes corporels se plient organiquement lors des contorsions du parkour.8
  • Foot Placement : Raycast vers le bas depuis la cheville pour détecter le sol, ajustement de la position du pied par IK, rotation du pied pour suivre la normale de la surface. La hauteur pelvienne (Pelvis Offset) baisse dynamiquement pour absorber les chocs, préservant l’impression d’élan.

5.5.5 Marquage environnemental et animation AI

Les brevets de Square Enix révèlent une méthodologie élégante pour les interactions contextuelles. Le brevet US8976184B2 décrit un système de tagging par volumes de métadonnées invisibles superposés aux obstacles. Plutôt que d’exiger du sous-système de parkour qu’il analyse la géométrie de collision brute, le moteur attache des volumes d’influence aux objets environnementaux. Lorsque le volume de collision du personnage intersecte un tel volume, le moteur extrait instantanément la normale de surface et la hauteur, résout l’IK de manière anticipée, et ajuste la chaîne squelettique, sans lancer de rayons coûteux.

L’Animation AI opère indépendamment du framerate pour orchestrer des micro-ajustements posturaux :

  • Attention Procédurale (Procedural Look-At) : Le moteur calcule le produit scalaire et le produit vectoriel entre le vecteur avant de la tête du personnage et le vecteur vers le centre d’intérêt (point d’ancrage, précipice, ennemi). Des rotations procédurales fluides sont appliquées sur les chaînes cou/vertèbres avec des limites biomécaniques (clamping), conférant au personnage une conscience spatiale anticipatoire.
  • Ragdoll Partiel : L’Animation AI module dynamiquement la rigidité des chaînes de ragdoll pour absorber l’énergie cinétique lors des atterrissages, rendant les impacts visuellement tangibles.

5.5.6 Trajectoires de Bézier pour le grappin

L’implémentation d’un système de grappin (type « Zip » de Forspoken) exige une courbe de Bézier cubique tridimensionnelle en temps réel, ce qui évite les collisions aberrantes d’une force d’attraction linéaire (loi de Hooke) :

C(u)=(1u)3P0+3(1u)2uP1+3(1u)u2P2+u3P3C(u) = (1-u)^3 P_0 + 3(1-u)^2 u P_1 + 3(1-u) u^2 P_2 + u^3 P_3

u[0,1]u \in [0,1] est le temps normalisé, P0P_0 le personnage, P3P_3 la cible évaluée, et les points de contrôle P1,P2P_1, P_2 sont projetés pour simuler la gravité et l’arc hyperbolique. Le moteur déplace la racine (Root Motion) le long de cette courbe, tandis qu’une fonction d’adoucissement u=f(u)u' = f(u) gère la décélération non-linéaire avant l’impact.

5.6 Techniques de rendu VR

5.6.1 Variable Rate Shading (VRS)

Le VRS permet de varier la résolution de shading par pixel selon les régions de l’image. En VR, tout se joue sur la gestion de la vision périphérique :

  • Centre de l’image (zone fovéale) : Résolution maximale (1×1 par pixel).
  • Périphérie : Résolution réduite (2×2 ou 4×4 par pixel). L’œil humain ne perçoit pas les détails en vision périphérique.

En concentrant la puissance maximale sur le centre de l’écran, le GPU réduit la latence de rendu, ce qui garantit que les inputs physiques du parkour sont pris en compte à l’image suivante, sans délai perceptible.

5.6.2 Foveated Rendering

Avec un eye-tracker intégré au casque (Varjo, Apple Vision Pro), le Foveated Rendering pousse le VRS à l’extrême : seule la zone exacte du regard est rendue en haute résolution, ce qui réduit la charge GPU de 30-50 %. L’implémentation matérielle repose sur des fonctions à noyau polynomial intégrant des transformations log-polaires gérées par des Compute Shaders.9

5.6.3 Async Compute et DirectStorage

  • Async Compute : Le GPU exécute des compute shaders en parallèle avec le pipeline graphique, exploitant les unités de calcul inactives pendant les phases de rasterization. Dans le Luminous Engine, les sillages magiques, la physique de la cape et les déformations aqueuses sont expédiés dans des queues de calcul indépendantes.
  • DirectStorage (Windows) / io_uring (Linux) : transfert direct des données compressées du SSD NVMe vers la VRAM via DMA, sans passer par le CPU pour la décompression (algorithme GDeflate). Le Luminous Engine a opéré plus de cent ajustements bas niveau pour exploiter DirectStorage : ce sont ces cycles CPU rendus libres qui ont permis au sous-système de parkour d’exister.10

5.6.4 LOD et Ray-Traced Shadows

Une vélocité massive impose de traverser les niveaux de détail (LOD) en fractions de seconde. Les transitions brusques entre modèles basse et haute résolution déchirent l’immersion. Le Dithered Crossfade (fondu temporel matriciel) appliqué lors des changements de LOD assure un lissage topologique parfait. Les ombres en ray-tracing, elles, donnent un ancrage géométrique absolu de l’ombre au sol, supérieur aux cascades de shadow maps qui crénellent lors des mouvements de caméra brusques, et fournissent la perception de profondeur dont le joueur a besoin pour chronométrer ses atterrissages.

5.7 Synthèse

Le pipeline de rendu de LplKernel combine :

  1. Vulkan pour le contrôle explicite de la synchronisation et de la mémoire GPU.
  2. Motion Matching pour une animation naturelle sans graphe de transitions manuel.
  3. IK procédurale (FABRIK + Dual Quaternions) pour l’adaptation au terrain.
  4. Marquage environnemental et Animation AI pour les interactions contextuelles procédurales.
  5. VRS / Foveated Rendering pour maximiser la qualité visuelle dans le budget de frame VR.
  6. Late Latching pour minimiser la latence MTP sous la milliseconde.
  7. DirectStorage + Async Compute pour libérer le CPU au profit de la simulation.

Notes de bas de page du chapitre 5


Footnotes

  1. La latence MTP (Motion-to-Photon) est le temps entre un mouvement de tête de l’utilisateur et l’affichage du frame correspondant. Au-delà de 20 ms, l’utilisateur ressent un décalage provoquant des nausées.

  2. Microsoft Developer Blog, « Evolving the Windows Display Driver Model », 2020. WDDM Overview, Microsoft Learn.

  3. Architecture VirtIO-GPU : OASIS VirtIO Specification 1.1-1.3. QEMU virtio-gpu.c source. Protocole Venus : Khronos vk.xml.

  4. Khronos Group, « Vulkan 1.3 Specification », spécification officielle de l’API Vulkan.

  5. NVIDIA GDC 2015, « VR Direct », Late Latching via persistently-mapped buffers. Analogie conceptuelle avec le contrôle JIT des impulsions micro-ondes quantiques.

  6. Les Tensor Cores NVIDIA (matmul 4×4 en un cycle) ne sont accessibles que via CUDA ou les extensions Vulkan coopérative matrices (VK_KHR_cooperative_matrix). Pilote noyau NVIDIA : nvidia.ko, nvidia-open.ko, documentation GSP RISC-V.

  7. Simon Clavet, « Motion Matching and The Road to Next-Gen Animation », GDC 2016 (Ubisoft). Square Enix, « Forspoken Magic Parkour », GDC 2022.

  8. Kavan et al., « Skinning with Dual Quaternions », 2007. Isamu Hasegawa, Square Enix CEDEC presentations.

  9. Patney et al., « Towards Foveated Rendering for Gaze-Tracked Virtual Reality », NVIDIA Research, 2016. Kernel Foveated Rendering, ResearchGate.

  10. Microsoft Developer Blog, « DirectStorage 1.1, GPU Decompression Performance ». GDC 2022, « Breaking Down the World of Athia: The Technologies of Forspoken ».