- L’API graphique expérimentale sysgpu du moteur Mach avait besoin de sorties de shaders pour Direct3D 12, et a donc reconstruit Microsoft DXC sous une forme plus facile à utiliser de manière statique et cross-platform
- Le DXIL de Direct3D 12 est plus proche d’un LLVM bitcode post-traité émis par un fork LLVM/Clang 3.7 de Microsoft que d’une véritable spécification, avec une structure qui dépend davantage de la « sortie réelle du compilateur » que du standard
- Les runtimes de la famille WebGPU convertissent généralement le WGSL en HLSL avant de produire du DXBC/DXIL via FXC ou DXC, mais le coût de distribution de DXC pousse souvent à garder FXC, plus ancien et plus lent, comme choix par défaut
- DXC existant est difficile à lier statiquement, et le binaire propriétaire de signature/validation
dxil.dll n’est distribué que principalement pour Windows et Linux x86, ce qui bloque la compilation hors ligne de shaders DirectX sur macOS ou dans des CI Arm Linux
mach-dxcompiler réécrit la build CMake en Zig build.zig et supprime la dépendance aux DLL pour fournir une bibliothèque statique dxcompiler et un CLI dxc, même si des limites subsistent sur le support de l’ABI MSVC, les tests musl, la sortie SPIR-V et Shader Model 6.7
Pourquoi Mach a reconstruit DXC
- Le moteur Mach développe en Zig une API graphique expérimentale appelée sysgpu, avec pour objectif de prendre en charge des backends Metal, Vulkan, Direct3D et OpenGL
- Sur le backend Direct3D 12, il faut compiler les programmes de shaders dans un format que Direct3D 12 peut consommer
- C’est dans ce processus qu’il est apparu que le compilateur de shaders DirectX de Microsoft, DXC, impose aux développeurs de jeux une expérience de distribution complexe et peu pratique
La compilation des shaders DirectX, de FXC à DXC
- L’API graphique DirectX utilise HLSL comme langage de shading
- Avant Direct3D 11, le compilateur HLSL s’appelait FXC, pour effects compiler
- FXC est connu chez les développeurs de jeux comme un compilateur lent et de qualité moyenne en génération de code
- Il peut être défavorable à la fois en vitesse de compilation des shaders et en performance à l’exécution
- Avec Direct3D 12 et Shader Model 6.0, Microsoft a officiellement marqué comme deprecated FXC, fourni avec Windows, et a introduit DXC, un fork basé sur LLVM/Clang v3.7
- DXC est publié sur Microsoft/DirectXShaderCompiler, et Microsoft distribue aussi des binaires précompilés
- Dans le fork LLVM de Microsoft, les modifications liées à HLSL sont indiquées par les commentaires
// HLSL Change Start et // HLSL Change End
DXBC et DXIL, consommés par les pilotes Direct3D
- Comme chaque fabricant de GPU a une architecture matérielle et des exigences différentes, le binaire natif exécuté au final à partir de HLSL varie aussi selon les GPU Intel, NVIDIA ou AMD
- Microsoft fournit des API front-end comme Direct3D et HLSL, tandis que des IHV comme Intel, AMD ou NVIDIA écrivent les pilotes qui relient cela à une forme proche de l’ISA matérielle
- Dans DirectX 9 à 11, les pilotes consomment du DXBC
- Les développeurs de jeux compilent HLSL en DXBC avec le CLI
fxc.exe ou l’API d3dcompiler
- Les pilotes convertissent ensuite DXBC en binaire réellement exécutable sur le GPU
- DXBC était un format propriétaire non public utilisé entre Microsoft et les fabricants de pilotes GPU
- Depuis DirectX 12 et Shader Model 6.0, DXIL est devenu le format officiel consommé par les fabricants de pilotes DirectX 12
- DXIL est proche d’un bitcode LLVM 3.7 après les passes de génération de code et d’optimisation, auquel s’ajoute un petit conteneur/wrapper personnalisé
- La documentation DXIL dépend moins d’une spécification autonome que du bitcode effectivement émis par le fork LLVM 3.7 de Microsoft après modifications HLSL et optimisations
Le projet DXIR abandonné et la transition vers l’amont LLVM
- À l’époque de DirectX 12 et Shader Model 6.0, Microsoft envisageait de créer DXIR, un IR de haut niveau non optimisé, que DXC aurait abaissé vers un DXIL optimisé
- En 2021, les formulations laissant entendre la possibilité de DXIR ont été supprimées
- En 2019, un employé de Microsoft a expliqué qu’il existait très peu de documentation sur le passage de DXIR à DXIL, et que DXIR n’était pas vraiment un format officiel mais ressemblait davantage au premier LLVM IR après CodeGen
- En 2023, un employé de Microsoft a indiqué que le fork LLVM de DXC avait supprimé ou détérioré une grande partie de la couche de génération de code et de l’infrastructure LLVM
- Ajouter la génération DXBC à DXC nécessiterait de restaurer des fonctionnalités LLVM cassées, ce qu’ils ne comptent pas faire dans DXC
- Depuis mars 2022, Microsoft propose et poursuit un travail d’upstream du support de compilation HLSL vers l’amont LLVM/Clang
- Ce plan de transition inclut aussi le rétablissement, dans un LLVM/Clang moderne, de la prise en charge de l’écriture de bitcode hérité LLVM v3.7
Le poids de la distribution de DXC pour WebGPU et les moteurs de jeu
- Une couche d’abstraction graphique qui veut unifier des API modernes comme Metal, Direct3D 12 et Vulkan a aussi besoin d’un langage de shading unifié
- Certaines implémentations WebGPU visent à long terme un chemin qui émet directement du DXIL, mais en pratique la plupart ne le font pas
- Le chemin WebGPU typique est le suivant
- Le langage texte WGSL est converti en HLSL à l’exécution
- Le HLSL est compilé en DXBC ou DXIL via un compilateur HLSL
- Le DXBC/DXIL optimisé est transmis au pilote graphique, qui le convertit en représentation intermédiaire spécifique au fournisseur puis en code machine
- Vulkan/SPIR-V repose aussi sur un modèle où le pilote doit compiler SPIR-V en binaire natif
- Certains pilotes peuvent supposer que SPIR-V est optimisé, mais cela varie selon les GPU mobiles ou desktop
- Fossilize de Valve maintient des caches des binaires réellement compilés par les pilotes pour chaque combinaison GPU/version du pilote
- DXIL est toujours un LLVM bitcode après les passes d’optimisation, alors que SPIR-V peut être optimisé ou non
- Seul Apple Metal fournit une API qui compile directement vers un format binaire natif du matériel cible réel
Les choix imposés par dxcompiler.dll et dxil.dll
- Les runtimes WebGPU effectuent la transformation WGSL→HLSL→DXIL à l’exécution, et doivent donc choisir entre le nouveau DXC et l’ancien FXC
- D’après la documentation de Bevy, FXC est ancien, lent et non maintenu, mais ne nécessite pas de distribuer de DLL supplémentaires
- À l’inverse, DXC est plus moderne, plus rapide et maintenu, mais impose de distribuer
dxcompiler.dll et dxil.dll avec l’application
- Ce problème de choix ne touche pas seulement Bevy, mais aussi les utilisateurs Rust de
wgpu et les utilisateurs WebGPU de Dawn
- En conséquence, beaucoup de logiciels finissent par adopter FXC par défaut, bien qu’il soit ancien, lent et non maintenu
Pourquoi le lien statique de DXC est difficile
- Le fork LLVM de Microsoft ne prend pas en charge le lien statique
- Si l’on remplace
SHARED par STATIC dans les fichiers CMake, on obtient environ 15 bibliothèques statiques, mais l’expérience de linkage est pire qu’avec une seule bibliothèque
- Si l’on essaie d’utiliser des bibliothèques CMake
OBJECT, les modifications HLSL de Microsoft révèlent des interdépendances implicites au-delà des dépendances logiques
- Une partie de l’implémentation des interfaces COM de DXC est conçue pour charger dynamiquement
dxcompiler.dll et dxil.dll afin de s’appeler elle-même
- Il est donc difficile de produire un DXC statique en se contentant de modifier la configuration de build
Le dxil.dll propriétaire et la signature des shaders
dxil.dll n’est pas généré même lorsqu’on compile DirectXShaderCompiler depuis les sources, mais il est distribué dans les releases GitHub pour Windows x86/Arm et Linux x86
- D’après la spécification de l’API D3D12 Shader Cache, D3D12 n’accepte que des shaders signés, et si une optimisation ou un patch est appliqué à l’exécution, le shader doit être revalidé et resigné
- Dans la release preview de Shader Model 6.8,
dxil.dll/libdxil.so n’est pas fourni
- Le DXIL ciblant SM6.8 généré par ce compilateur n’est pas final et ne peut pas être validé
- Sa distribution et son exécution ne sont pas prises en charge sur des machines qui ne sont pas en mode développeur
- Sans
dxil.dll, les shaders ne sont ni signés ni validés
- Des shaders non signés/non validés ne peuvent pas s’exécuter sur une machine Windows si le Developer Mode n’est pas activé
Compilation hors ligne et contraintes de plateforme
- Mach veut éviter, quand c’est possible, la lourde distribution de dépendances DXC et effectuer une compilation hors ligne des shaders
- Microsoft ne distribue
dxil.dll que pour Windows x86/Arm et Linux x86
- Aucun binaire Linux aarch64 ni macOS n’est fourni
- Il est donc impossible de produire sur macOS un build cross-platform de jeu pour Windows, ou d’effectuer une compilation hors ligne de shaders DirectX dans un pipeline CI Arm Linux
- Exécuter ce binaire propriétaire de signature exige une machine Windows ou Linux x86_64
Ce que change mach-dxcompiler
- Environ 10,5 k lignes du système de build CMake existant ont été réécrites en Zig
build.zig
- La build ne cible plus que les deux éléments dont les consommateurs ont principalement besoin : la bibliothèque
dxcompiler.dll et le binaire de compilation/test hors ligne dxc.exe
- Au final, cela a été condensé en environ 1 k lignes de logique
build.zig
- Le code source Microsoft a été forké pour modifier l’architecture où DXC suppose la présence de
dxcompiler.dll et dxil.dll
- Simulation de l’entrypoint de DLL
- Désactivation de la fonction d’affichage des informations de version du compilateur issue de la DLL
- Émulation du chargement de pointeurs de fonctions de bibliothèques dynamiques
mach-dxcompiler est configuré de façon à ne pas dépendre de dxil.dll
- Sur une machine macOS, il est possible de compiler des shaders HLSL sans
dxil.dll propriétaire et de produire un fichier byte-for-byte identique au bytecode DXIL qui s’exécute sur une machine Windows standard
Livrables et utilisation
- Les releases incluent des binaires précompilés pour la bibliothèque statique
dxcompiler et le CLI dxc
- Il n’y a pas de dépendance au
dxil.dll propriétaire
- Les cibles construites dans le pipeline CI sont les suivantes
- macOS : Apple Silicon aarch64 et Intel x86_64
- Linux : musl et glibc, en aarch64 et x86_64
- Windows : x86_64 et aarch64, avec aussi l’ABI MinGW/GNU
- La bibliothèque expose une petite API C comme alternative à l’API COM existante
- Les développeurs de jeux Zig peuvent utiliser l’API Zig du dépôt, avec des exemples visibles dans les tests de
src/main.zig
- Par défaut, on télécharge et utilise les binaires précompilés
- La compilation depuis les sources peut se faire depuis le dépôt mach-dxcompiler avec seulement
zig et git, à condition d’utiliser la version de Zig spécifiée
git clone https://github.com/hexops/mach-dxcompiler
cd mach-dxcompiler/
zig build -Dfrom_source -Dtarget=aarch64-macos
zig build -Dfrom_source -Dtarget=x86_64-windows-gnu
zig build -Dfrom_source -Dtarget=x86_64-linux-gnu
Limites actuelles et conditions de maintenance
- Les binaires Windows ABI MSVC ne se compilent pas actuellement à cause d’un petit bug dans les bindings C
- Les binaires Linux musl se compilent, mais n’ont pas encore été testés
- Le moteur Mach prévoit d’utiliser Zig lui-même comme langage de shading plutôt que HLSL, donc le support de sortie SPIR-V n’est pas compilé et aucun ajout n’est prévu
- Il n’existe actuellement aucun plan de mise à jour pour la prise en charge récemment publiée de SM6.7
- Une partie du système de build CMake de LLVM n’a pas encore été totalement migrée, et des détails associés subsistent dans
generated-include/
- Ce projet existe pour résoudre les problèmes de Mach, et la gestion des issues repose actuellement sur une seule personne
- Si un meilleur chemin est trouvé, le projet pourra être marqué deprecated
1 commentaires
Avis sur Hacker News
Un article qui résume bien à quel point les fondations de la compilation de shaders entre API 3D sont catastrophiques
Il se concentre sur D3D et Microsoft, mais les autres API 3D ne font pas beaucoup mieux. Par exemple, sur un hôte Linux, on ne peut pas cross-compiler des shaders Metal ; ce n’est possible que sur macOS et sur des versions assez récentes de Windows
Si l’équipe Mach parvient à rendre son idée d’utiliser Zig comme compilateur de shaders multi-API 3D aussi fluide que « Zig comme toolchain de cross-compilation », cela pourrait être la plus grande avancée en infographie depuis environ 1995
Cela concerne aussi Godot
Il est dit que « la raison pour laquelle cela a été rendu optionnel est que la prise en charge de Direct3D 12 dépend actuellement de la distribution, avec Godot, de la bibliothèque propriétaire dxil.dll du DirectX Shader Compiler, et que distribuer du logiciel propriétaire va à l’encontre de la mission du projet Godot »
https://godotengine.org/article/dev-snapshot-godot-4-3-dev-3...
Je veux que mon GPU, mon Wi-Fi et mon Bluetooth fonctionnent, donc j’aimerais que ce soit coché par défaut
À propos du passage « il n’est pas nécessaire de distribuer une .dll supplémentaire avec l’application », beaucoup de jeux vidéo distribuent déjà de cette façon des middlewares propriétaires comme Bink, SpeedTree ou PhysX
La plupart des launchers comme Steam, GOG ou Epic exigent aussi leurs propres .DLL, et de nombreux jeux utilisent D3D11On12. Il y a aussi beaucoup de jeux sortis dont la liste des fichiers installés contient dxil.dll
Donc, honnêtement, je me demande : quel est le problème à distribuer une DLL de plus ? Le travail de rétro-ingénierie et de réimplémentation de la signature de code ici est excellent, et le fait que la sortie soit identique bit pour bit à celle de dxil.dll est particulièrement impressionnant. Mais je suis absurdement paresseux, et j’aurais probablement choisi la voie la plus simple : distribuer la DLL
Par exemple, le moteur Mach peut l’utiliser lorsqu’il crée un compilateur qui compile du code Zig en shaders pour différentes plateformes cibles, et l’utilisateur final peut l’utiliser directement dans les fonctionnalités cœur de Mach, sans configuration ni dépendance supplémentaire
Même dans les jeux AAA, et aussi dans d’autres jeux et logiciels, cela ajoute une dépendance supplémentaire susceptible de casser hors de votre contrôle. Dans une ancienne entreprise où je travaillais, nous recevions un middleware uniquement sous forme de DLL et de bibliothèques à lier, ce qui nous obligeait à consacrer davantage d’efforts aux mises à niveau de Visual Studio et à récupérer aussi de nouvelles versions. Comme l’éditeur de ce middleware n’avait pas fait sa propre mise à niveau, nous nous sommes retrouvés à assurer nous-mêmes la QA de compatibilité avec la nouvelle version de VS
Bien sûr, disposer du code source ne rend pas les mises à jour sans friction, mais cela réduit beaucoup cette friction et évite d’avoir à attendre quelqu’un d’autre. Les versions récentes de Visual Studio semblent avoir tenté d’assurer une compatibilité descendante des bibliothèques C++ binaires, mais je ne pense pas que ce soit une propriété sur laquelle on puisse compter à long terme
Et tout cela suppose que le code reste sur la même plateforme et avec la même cible. À un moment donné, on peut vouloir prendre en charge une autre plateforme comme hôte ou comme cible, et sans le code source cela peut devenir extrêmement difficile, voire impossible. Pour quelque chose de spécifique à une plateforme comme DXIL, cela peut ne pas sembler être un gros problème, mais l’article dit justement qu’à cause de la nature de blob binaire de DXIL, la précompilation de shaders était impossible en dehors des architectures Windows et Linux précises pour lesquelles Microsoft fournissait une DLL
La « signature »[1] effectuée par DXIL.dll n’est-elle au final qu’un MD5 modifié ?
1: https://github.com/hexops/DirectXShaderCompiler/blob/4190bb0...
Quelqu’un qui aurait lu l’article attentivement jusque-là pouvait comprendre qu’il devait s’agir d’un hash de base, ou de quelque chose d’approchant ; et dans le pire des cas, il suffisait que quelqu’un fasse du reverse engineering de l’assembleur.
Après s’être donné autant de mal, on peut tout à fait critiquer Microsoft publiquement. Surtout quand il s’agit de code open source dans lequel toute personne intéressée peut fouiller pour le retrouver ; merci à msk de l’avoir trouvé.
https://github.com/baldurk/renderdoc/blob/4a620bb5a16b4de4e2...
J’aime bien cette citation[0] côté Microsoft. Elle explique que, comme le fork LLVM de DXC a supprimé ou cassé une grande partie de la couche de génération de code et de l’infrastructure de LLVM, prendre en charge la génération de DXBC dans DXC nécessiterait un gros travail pour corriger et restaurer les fonctionnalités LLVM cassées.
Vu l’ampleur du problème et les ressources limitées de l’équipe, ils ne résoudront pas cela dans le nouveau compilateur DXC ; à l’avenir, Clang pourrait prendre en charge la génération de DXBC, mais ils se concentrent pour l’instant sur la prise en charge de la génération de DXIL et de SPIR-V, donc il sera difficile de commencer avant plusieurs années. C’est rafraîchissant de voir clairement indiqué ce qui ne sera pas fait.
[0] https://github.com/microsoft/DirectXShaderCompiler/issues/57...
Je recommande vivement de jeter un œil à l’écosystème Mach. En particulier, mach-sysgpu est une réimplémentation complète de WebGPU, dont la majeure partie a été écrite par Ali Chraghi, qui a 17 ans.
Côté SDL, ils créent pour SDL3 un langage de shader sous la forme de SDL_gpu, différent de l’existant. Comme cela pourrait devenir une approche cross-platform pour gérer les graphismes 3D dans les jeux, je le suis attentivement depuis un moment.
La différence, c’est que SDL_gpu en est encore à ses débuts, tandis que WebGPU dispose déjà de deux bonnes implémentations publiques.
L’approche la moins pénible serait sans doute quelque chose comme HLSL/GLSL → SPIR-V ↔ DXIL, ou bien écrire directement les shaders en SPIR-V.
vkd3d de Wine semble avoir un convertisseur DXIL → SPIR-V, et comme il s’agit d’un langage intermédiaire bien plus simple qu’un convertisseur de langages de shading de haut niveau, cela pourrait être plus robuste.
Cela dit, je me demande s’il existe, à part ce monstre LLVM, un compilateur HLSL → DXIL pur et simple basé sur C99, compilable sans GCC ni Clang.
Pour répondre à la question : non, il n’y en a pas. La conversion de HLSL vers DXIL est de fait verrouillée par Microsoft, et il y a eu très peu d’efforts pour s’en affranchir.
Utiliser Zig lui-même comme langage de shading, c’est génial. Zig est vraiment un langage unique pour tout. C’est aussi un système de build, et aussi un langage de shading !