Vers une ère sans API graphiques
(sebastianaaltonen.com)- Au cours des dix dernières années, les API graphiques bas niveau comme DirectX 12, Vulkan et Metal ont permis d’exploiter davantage les performances des GPU, mais leur complexité et leur coût de maintenance ont fortement augmenté
- Les GPU modernes prennent en charge une hiérarchie de cache complète, des pointeurs 64 bits et des ressources bindless, ce qui rend inutiles les anciens objets d’état complexes et modèles de binding
- La conception proposée simplifie radicalement le pipeline de rendu grâce à un accès mémoire fondé sur des pointeurs C/C++ et à un unique pointeur racine 64 bits
- Elle élimine l’explosion des PSO, les barrières de ressources et les API de binding complexes, et propose une architecture reliant directement la mémoire GPU et le langage de shader
- Cette approche constitue une API de nouvelle génération optimisée pour les architectures GPU modernes, et montre la direction que devraient prendre DirectX 13 ou Vulkan 2.0
L’évolution des API graphiques bas niveau
- En 2013, l’architecture AMD GCN de la Xbox One et de la PS4 est devenue la référence du développement de jeux AAA, entraînant l’émergence d’API bas niveau comme Mantle, DirectX 12, Vulkan et Metal
- En d’autres termes, elles ont été conçues en prenant pour référence les architectures GPU autour de 2013
- Les anciennes API comme DirectX 11/OpenGL montraient leurs limites avec un rendu mono-thread et un fort surcoût côté pilote
- Ces API ont réduit le coût des draw calls grâce à des objets de pipeline précompilés (PSO), mais comme cela s’intégrait mal à la structure des moteurs, la complexité a augmenté
- Résultat : une nouvelle « couche de pilote bas niveau » s’est créée à l’intérieur même des moteurs, ce qui a entraîné une spécialisation accrue du rôle des programmeurs graphiques
Contexte historique : pourquoi est-ce devenu si complexe ?
- Les premiers GPU reposaient sur une structure centrée sur une mémoire séparée et un pipeline à fonctions fixes
- OpenGL et DirectX ont adopté une conception orientée états et objets afin d’abstraire la diversité matérielle
- Jusqu’à DirectX 11, les textures et buffers étaient encore gérés via des descripteurs opaques
- Cette conception s’est ensuite maintenue par inertie dans les API suivantes
Le décalage entre GPU modernes et API
- Les GPU actuels prennent en charge une hiérarchie de cache cohérente, PCIe ReBAR, les pointeurs 64 bits et les textures bindless
- Une architecture où le CPU écrit directement dans la mémoire GPU et le GPU lit immédiatement est désormais possible
- Dans cet environnement, des structures comme les PSO, descriptor sets et tables de binding deviennent superflues
- L’explosion des caches de PSO impose des caches de plusieurs centaines de Go, ce qui provoque des retards au chargement et du stuttering
- Une nouvelle API pourrait supprimer ces structures héritées d’une autre époque et basculer vers un accès simple basé sur des pointeurs
Simplifier la gestion de la mémoire GPU
- Avec Vulkan et DirectX 12, il faut aujourd’hui interroger la compatibilité des heaps après la création des ressources, ce qui est inefficace
- L’approche proposée repose sur une API simple de type gpuMalloc/gpuFree pour allouer directement la mémoire GPU
- Le CPU peut mapper directement la mémoire GPU pour l’initialiser
- Les données volumineuses peuvent être transférées via des commandes de copie afin d’effectuer la compression DCC et le swizzle
- Les adresses de mapping CPU et les adresses GPU sont distinctes, avec conversion via gpuHostToDevicePointer
Moderniser les données et le langage de shader
- Utilisation d’un langage de shader fondé sur des pointeurs C/C++, comme CUDA, Metal ou OpenCL
- Les wide loads (128 bits ou plus) au niveau des structures permettent un accès mémoire efficace
- Le ByteAddressBuffer de DirectX ou les texel buffers ne sont plus optimaux
- GLSL et HLSL ne prenant pas en charge les pointeurs, ils n’ont pas fait émerger d’écosystème de bibliothèques de shaders réutilisables, contrairement à CUDA qui a développé un riche ensemble de bibliothèques
Arguments racine et structures de données
- Le kernel GPU reçoit en entrée un unique pointeur 64 bits, ensuite casté en structure
- Le CPU et le GPU partagent les mêmes en-têtes C/C++, garantissant la cohérence des structures de données
- Les mots-clés const/restrict guident les optimisations du compilateur et évitent la distinction inutile entre UBO et SSBO
- Cette approche exploite le préchargement dans les registres scalaires et les optimisations dynamiques des uniformes des GPU modernes
Simplifier le binding des textures
- Toutes les textures sont gérées dans un tableau (heap) de descripteurs 256 bits, accessible en écriture directe par le CPU et le GPU
- Un accès fondé sur des index 32 bits permet l’échantillonnage de textures non uniforme (non-uniform)
- Le modèle est plus simple que les descriptor heaps de DirectX 12 SM 6.6, et proche de Vulkan VK_EXT_descriptor_buffer
- La création, l’upload et l’échantillonnage des objets texture sont tous unifiés autour de pointeurs mémoire GPU
Pipeline de shaders et constantes
- La création du pipeline se résume à charger un IR de shader puis appeler gpuCreatePipeline
- Plus besoin de root signature, de descriptor sets ni de définitions de binding
- Des constantes statiques (fondées sur des structures) remplacent les constantes de spécialisation de shader, ce qui réduit l’explosion combinatoire des PSO
- La structure de constantes peut inclure des pointeurs GPU, permettant de hardcoder directement des adresses d’exécution
Simplifier les barrières et la synchronisation
- Les listes de barrières par ressource des API actuelles correspondent mal à l’architecture des GPU modernes
- Le modèle proposé n’utilise que des flags en bitfield au niveau des files et des étapes
- Il se simplifie sous la forme gpuBarrier(before, after, hazard), sans suivi de ressources
- Les commandes gpuSignalAfter / gpuWaitBefore permettent une synchronisation GPU→GPU proche des sémaphores timeline
Command buffers et rendu
- Seuls des command buffers transitoires (transient) sont utilisés, ce qui supprime le modèle complexe de réutilisation de Vulkan
- gpuBeginRenderPass / gpuEndRenderPass servent à configurer les render targets et à effectuer le clear
- Il n’y a pas de barrières automatiques entre render passes, ce qui permet le rendu parallèle et l’optimisation des depth pre-pass
Simplifier le pipeline raster
- Les vertex/pixel shaders accèdent aux données via des pointeurs, supprimant les API de binding
- GpuDepthStencilState et GpuBlendState sont séparés du PSO afin de réduire le nombre de combinaisons
- Les GPU mobiles peuvent prendre en charge le programmable blending via des intrinsics de framebuffer fetch
- Le PSO ne conserve qu’un minimum d’état : topologie, formats, nombre d’échantillons, etc.
Draw indirect et rendu piloté par le GPU
- Tous les arguments (data, arguments) sont passés via des pointeurs GPU
- gpuDrawIndexedInstancedIndirectMulti permet le multi-draw
- Le GPU peut générer lui-même les données racine et les arguments de draw, permettant un rendu entièrement GPU-driven
Outils et compatibilité
- Une structure fondée sur des pointeurs peut être inspectée via des informations de symboles, comme avec les débogueurs CUDA/Metal
- Protégée par la mémoire virtuelle, elle ne pose pas de problème de sécurité ; en cas d’accès invalide, un page fault se produit
- Comme le montrent MoltenVK et Proton, les API existantes DirectX, Vulkan et Metal peuvent rester compatibles via des couches de traduction
Configuration minimale et conclusion
- Nvidia Turing (2018), AMD RDNA1 (2019), Intel Xe1 (2022) et Apple M1 (2020) prennent tous en charge les fonctions proposées
- Les GPU actuels ont déjà évolué vers une architecture bindless, à pointeurs 64 bits et à cache cohérent
- Seule l’API reste freinée par des abstractions du passé
- La nouvelle API serait plus simple que DirectX 11, plus rapide que Vulkan et plus flexible que Metal
- Les futures Vulkan 2.0 / DirectX 13 devraient adopter cette conception totalement bindless, et élargir l’écosystème via un langage de shader à pointeurs C/C++ plutôt qu’avec HLSL/GLSL
1 commentaires
Avis sur Hacker News
Cet article montre très bien les aspects inutiles de Vulkan et DX12
Aujourd’hui, DX12 ne prend même pas en charge les pointeurs de buffer, ce qui donne l’impression d’un projet pratiquement abandonné, et Vulkan n’a toujours pas été rationalisé en 2.0, si bien que beaucoup de pilotes implémentent mal les extensions
S’il existait une nouvelle API de ce type, on pourrait probablement émuler OpenGL bien plus rapidement par-dessus, et quelque chose comme SDL3 GPU pourrait aussi obtenir un gain de performances de 3 à 4 fois
Le livre de Frank Luna ne couvre pas toutes les fonctionnalités récentes, et il faut fouiller le site Learn, les exemples GitHub et la documentation de référence
Vulkan est tout aussi complexe et, même si une 2.0 sortait, on peut se demander comment elle pourrait réellement être utilisée sur des plateformes majeures comme Android
À part Intel Arc, la plupart des GPU fonctionnent sans reBAR, mais Microsoft ou Intel devraient probablement l’imposer via l’UEFI pour pouvoir utiliser de manière fiable des fonctions comme les bindless textures
En contrepartie, cela fixerait un seuil matériel minimal, et la prise en charge sur les cartes mères d’avant 2020 est très irrégulière
Il manque la motivation centrale de l’article
D’après l’index du blog, l’idée est que « la complexité des API graphiques et des langages de shader a fortement augmenté au cours des dix dernières années, et qu’il faut désormais simplifier les couches d’abstraction pour améliorer l’efficacité du développement et les performances, tout en préparant les futures charges de travail GPU »
Au début, les SSD réutilisaient les interfaces IDE/SATA, mais il a fallu abandonner les hypothèses liées aux disques mécaniques et créer un nouveau mode de transfert pour obtenir de vraies performances
L’idée est que les API graphiques sont peut-être arrivées au moment où elles doivent elles aussi abandonner ces contraintes héritées
Je suis peut-être biaisé parce que je suis le travail de Sebastian Aaltonen depuis longtemps, mais cet article est vraiment excellent
Je pense que la direction à venir est un retour vers le rendu logiciel
La différence, cette fois, c’est que les algorithmes et structures de données bénéficient d’une accélération matérielle
Cette tendance est déjà en cours dans l’industrie des VFX, et le portage d’OctaneRender sur CUDA par OTOY il y a environ cinq ans en est un bon exemple
Les différents types de shaders servent à faire le lien entre ces pipelines, donc une bascule complète vers le logiciel n’est pas réaliste
Par exemple, Nanite d’Unreal Engine utilise un rasterizer logiciel exécuté via les compute shaders du GPU pour traiter les petits triangles
Les détails de l’article étaient impressionnants
Je n’en ai compris qu’une partie, mais cela ressemble à un futur ouvrage de référence pour la conception des API graphiques
Pour la plupart des joueurs, Vulkan/DX12 n’ont pas apporté de grands bénéfices, et de nombreux jeux ont souffert de problèmes de PSO
Vulkan s’améliore, mais WebGPU a hérité tel quel des contraintes de la conception initiale de Vulkan
Avec un matériel qui évolue rapidement, c’était peut-être une erreur d’aller vers des API aussi bas niveau
Une approche centrée sur le calcul générique, à la manière de CUDA, serait peut-être finalement une meilleure direction
Mantle me manque
Il avait ses défauts, mais donnait vraiment l’impression de manipuler directement le matériel, et l’époque du développement sur Xbox 360 était la plus amusante
Elle a été conçue par Nvidia et Nintendo, et c’est selon moi l’API la plus intuitive parmi les consoles
En lisant cet article, j’ai eu l’impression d’assister à un moment historique
Cela m’a fait penser à Google Toucan, qui semble être un projet connexe
Cet article m’a rappelé beaucoup de souvenirs
Parmi les éléments supplémentaires que les concepteurs d’API doivent prendre en compte, il y a :
Je me demande pourquoi Microsoft ne sort pas une nouvelle version de DirectX
DirectX Ultimate ou 12.2 sont en pratique identiques à DX12
Est-ce que l’importance des API maison a diminué à cause de middlewares comme Unreal Engine, ou bien est-ce à EPIC de proposer une nouvelle API ?
Les vrais développeurs de jeux créent une RHI (Rendering Hardware Interface) et se concentrent sur le développement du jeu
Le ray tracing et les mesh shaders ont été les plus grandes innovations, mais ils restent encore peu exploités, ce qui semble freiner toute avancée supplémentaire
La centralisation autour de moteurs comme Unreal ou Unity a réduit l’intérêt pour l’innovation côté API, et les progrès se poursuivent surtout du côté GPU
Les API CPU en sont toujours à un simple niveau de mappage de buffers
Comme à l’époque de l’arrivée des tessellation shaders, il faudra probablement un nouveau changement matériel pour relancer les choses
Je me demande si Valve pourrait créer sa propre API graphique pour SteamOS
J’ai entendu dire qu’au début du développement de Vulkan, Valve faisait partie des principaux acteurs moteurs