- 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
Aucun commentaire pour le moment.