Des changements en profondeur
Paru le 23 novembre 2006
Guillaume Randon – Microsoft Services
Sur cette page
Présentation
Des changements en profondeur
A suivre…
Présentation
DirectX est une API graphique qui permet d’accéder aux fonctionnalités des cartes graphique. La version 10 sera disponible sous peu sur Windows Vista. Cette dernière version est spécialement intéressante car les changements qu’elle induit ne se limitent pas aux modèles-objets proposés aux développeurs mais touchent en profondeur le modèle de driver utilisé pour communiquer avec la carte ainsi que les fonctionnalités de celle-ci.
Dans ce document, je me propose de donner un avant goût de ces changements intervenant au niveau le plus bas, de décrire plus en profondeur le modèle-objet associé à DirectX 10 et de détailler un exemple de code.
Ce court document présente les changements qui ont été effectués au niveau du modèle de driver pour permettre à cette nouvelle déclinaison de DirectX d’être plus performante et plus efficace. Il est suivi d’un autre document DirectX 10 - Le point de vue du développeur qui décrit plus en détails ce SDK du point de vue du développeur et présente un exemple de code. C’est deux documents reprennent plus en détails une présentation effectuée au mercredi du développement qui sera sous peu disponible en ligne à l’adresse suivante http://www.microsoft.com/france/vision/.
Ce document, bien qu’étant une introduction à DirectX 10, se destine à des personnes qui sont déjà familières avec DX9, les Shaders, et le système d’effets de DirectX. Pour les personnes intéressées par le sujet mais qui n’auraient pas encore eu le loisir de découvrir ces concepts, ils peuvent se reporter entre autre aux documents ci-dessous :
DirectX Managed : Rappel des concepts de bases
Introduction aux Shaders avec Managed DirectX
DirectX est un SDK qui couvre aussi les domaines de l’audio ainsi que la gestion des entrées utilisateur. Dans ce document, je ne décrirais pas ces parties et je me concentrerais essentiellement sur les fonctionnalités touchant à l’utilisation des cartes graphiques.
Des changements en profondeur
Cette nouvelle déclinaison de DirectX s’appuie sur un nouveau modèle de driver WDDM introduit avec Windows Vista. Elle ne sera, de ce fait, pas disponible sur des versions antérieures de Windows. Avant de découvrir DirectX 10 du point de vue du programmeur, il est intéressant de s’attarder un peu sur ce nouveau modèle de driver, ses raisons d’être et les avancés qu’il permet.
L’ancien modèle de driver était conçu pour permettre à une application d’utiliser directement les capacités 3D de la carte graphique. Elle se trouvait limitée lorsque plusieurs applications souhaitaient utiliser celles-ci de manière concomitante. Comme le nouveau modèle de desktop s’appuie lui-même sur DirectX, il s’agissait d’adresser ces points.
Des études approfondies ont été mené pour identifier les goulots d’étranglement les plus importants au niveau des performances des applications DX9. La pluparts d’entre elles sont essentiellement limitées par le CPU et ce, pour plusieurs raisons :
-
D’abord il n’y a pas une correspondance de type exact entre les informations manipulées par le CPU en mémoire système et les types de données finalement manipulés par les cartes. Celles-ci utilisaient d’ailleurs des formats qui étaient souvent spécifiques à chaque vendeur de carte. Aussi le driver de chaque carte consommait un nombre important de cycles CPU pour reformater les données qui lui était transmises dans un format intelligible par la carte.
-
Avec le temps, le nombre de variables d’états du pipeline avait grossi et il était nécessaire de régulièrement vérifier avant d’effectuer des rendus que les valeurs spécifiées par l’utilisateur étaient toutes compatibles entre elles.
-
Enfin le design du modèle de driver engendrait un nombre important de transitions entre le mode utilisateur et le mode Kernel. Ce qui est assez couteux lorsque cela est effectué trop souvent (c’est une des raisons pour lesquelles il était régulièrement suggéré pour optimiser les applications DX9 de trouver des solutions algorithmiques pour regrouper le maximum d’appels à des méthodes Draw en un nombre d’appels plus restreints).
Le nouveau modèle de driver adresse chacun de ces points :
-
D’abord, un effort important a été mené avec les constructeurs de carte graphique pour faire en sorte que le format des données manipulées nativement par celles-ci soit consistant avec le format de ces mêmes données lorsqu’elles sont manipulées par le CPU et réside en mémoire système.
-
Ensuite, les constantes sont communiquées au pipeline par blocks. Ce qui évite d’effectuer un travail important pour une valeur et de répéter ce même travail pour communiquer une autre valeur. De façon plus générale on peut dire qu’un effort important a été fait pour permettre un niveau de granularité dans la communication de ces valeurs qui permet de meilleures performances. Et comme nous le verrons, le design même de l’API DirectX donne jusqu’à un certain point au programmeur une forme de contrôle sur le niveau de granularité qui va être utilisé.
-
Enfin, une partie du driver de la carte vidéo réside en mode utilisateur et va effectuer un travail important pour grouper les commandes et les blocks de données à passer à la carte de façon à éviter de faire trop d’aller retour entre le mode utilisateur et le mode kernel.
Le diagramme ci-dessous extrait de la documentation du WDK illustre certains de ces points:
On y voit bien que le driver pour la carte vidéo comprend un “User mode Display Driver” et un “Display Miniport Driver” qui sont tout deux fournis par le vendeur de carte.
Cette nouvelle architecture permet de minimiser la quantité de code exécuté en mode Kernel, ce qui est intéressant pour des questions de stabilité. Par ailleurs, c’est bel et bien le fait de disposer de cette partie du driver en mode utilisateur qui comme indiqué précédemment permet de grouper les commandes à passer à la carte sans passer en mode Kernel et ainsi d’éviter un trop grand nombre de transitions entre ces deux modes de fonctionnement du CPU et de l’OS.
J’indiquais plus haut qu’un des axes de réflexion pour améliorer les performances avait été de limiter autant que possible les opérations effectuées par le CPU et en particulier les conversions que celui-ci avait à effectuer pour transformer des valeurs qui avait été passé à l’API dans un format donné en un format consommable par la carte. Il faut savoir qu’avec DirectX, les cartes doivent maintenant manipuler des données exprimées sous une forme proche de ce qui est défini par les normes IEEE. Une des limitations avec DX9 et les familles de GPU DX9 était entre autre que les types de données traitées par la carte étaient différents de ceux manipulés par le CPU et étaient largement méconnus du programmeur. Le fabricant de carte ayant la possibilité de le changer à loisir et n’ayant pas à nécessairement documenter ces points. Ceci a entre autre pu rendre plus difficile l’utilisation du GPU dans des domaines ou pour les calculs ; il reste nécessaire de bien connaitre le mode de représentation des données que l’on manipule pour pouvoir maîtriser ou tout du moins connaitre les marges d’erreur sur les résultats obtenus. Même si les cartes DX9 récentes au moins « haut de gamme » pouvaient manipuler les données dans un format proche des spécifications IEEE, le développeur n’avait pas moyen de le savoir ou de s’assurer au moment de l’exécution d’un programme de cet état de fait. Potentiellement, le travail effectué de ce côté-là avec DirectX 10 et le modèle de driver WDDM, devraient permettre d’ouvrir le GPU à un plus large éventail d’emplois. Les version futures du modèle de driver WDDM devraient aller encore plus loin dans ce sens et il est prévu à plus ou moins brèves échéances d’être complètement conforme aux normes IEEE.
Toujours dans le registre de la prédictibilité du résultat obtenu sur n’importe quelle carte il faut par ailleurs savoir que le programme de certification des cartes DX10 prévoit justement des étapes de validation de la conformité du résultat obtenu sur la carte avec le résultat obtenu sur le Device de référence (l’implémentation logicielle de Microsoft simulant une carte graphique en s’appuyant uniquement sur le CPU).
Pour revenir un peu sur la répartition du travail entre la partie en mode utilisateur du driver graphique et la partie en mode Kernel, je me permets de reprendre le diagramme ci-dessous extrait de la présentation suivante : « Win HEC presentation pri103_pronovost_moreton_052506.ppt »
Le « User Mode Driver » construit en fait des paquets appelés DMA Buffers. Ces paquets contiennent tous les ordres à passer à la carte ainsi que toutes les valeurs à lui communiquer. Ensuite les paquets sont transmis au « Kernel Mode Driver » (KMD), on a donc une seule transition pour toutes les commandes contenues dans un même paquet au lieu d’une transition sur certains appels individuels de méthodes. Le KMD ajoute les paquets reçus à des contextes correspondants à ce qui est affiché par une application donnée et à l’état du pipeline pour cette application, ensuite le driver en conjonction avec la carte ordonne ces contextes et exécutent les paquets de commandes associés en changeant de contextes à certain moment clé. A ce jour, le passage de l’exécution des commandes associées à un contexte à un autre ne peut se faire qu’à la fin de l’exécution du DMA Buffer. Il est prévu que cela change avec les versions successives des drivers WDDM.
On voit bien que cette nouvelle architecture permet de faire en sorte que l’application n’interagisse finalement plus aussi directement et aussi souvent avec le GPU par le biais du driver en mode Kernel de celle-ci et qu’au contraire les interactions n’interviennent qu’à des moments clé ou l’on a suffisamment à communiquer pour qu’il soit judicieux de le faire.
Avec le modèle de driver WDDM 1.0, le travail du GPU pour un contexte donné est interruptible à chaque fin d’exécution de buffer DMA. Pour la version 2.0, il est prévu qu’il soit interruptible pour chaque commande et même par triangle. On parle même pour WDDM 3.0 de la possibilité de changer de contexte pendant le rendu d’un pixel donné.
Il est intéressant de bien souligner que les commandes émises par le « User Mode Driver » sont spécifiques à la carte. Il peut s’agir des commandes qui sont directement comprises par celles-ci, il n’est pas nécessaire d’avoir une représentation intermédiaire supplémentaire.
Avec la version 1.0 c’est le KDM qui se charge de copier ces « command buffer » de la mémoire système vers une zone de mémoire accessible par le GPU, par contre il est prévu pour des déclinaisons ultérieures du système de driver WDDM que le UDM puisse écrire directement ces buffer dans une telle zone mémoire.
A propos de la gestion de la mémoire plusieurs points importants sont à noter. Toutes les surfaces référencées par un DMA buffer donné doivent être présentes dans une zone mémoire accessible par le GPU (dans la mémoire vidéo) au moment ou le DMA buffer lui est soumis pour exécution. C’est le KMD qui garde une trace des références présentes dans un DMA buffer. Comme ceux-ci ont été crées à un moment où les surfaces référencées n’étaient pas encore nécessairement en mémoire vidéo, le KMD doit patcher le buffer avec toutes les nouvelles adresses avant l’exécution de celui-ci. Par ailleurs comme toutes les ressources nécessaires ne peuvent pas forcément résider toutes en mémoire vidéo simultanément, le driver émet des « split point » dans les « command buffers » qui permettent de traiter ceux-ci par blocks, chaque block étant traité une fois que les ressources qu’il référence ont bien été chargées dans une zone mémoire accessible par le GPU.
Avec ce système, il subsiste un risque de générer un nombre d’erreurs de pages (page fault) importantes ce qui aurait un effet négatif sur les performances.
Dans le futur, il est prévu que le « paging engine » puisse travailler en parallèle avec la partie 3D et que le GPU puisse utiliser des adresses virtuelles pour référencer les ressources à utiliser. Cela aurait l’avantage de ne plus avoir à générer les « splits points » dont il est question plus haut et par ailleurs aucun travail ne serait plus nécessaire pour patcher les blocks de commandes. En effet les adresses virtuelles peuvent être directement écrites par le UMD et elles pourront être utilisées tel quel par le GPU. Pour finir, comme l’UMD aura la possibilité d’écrire directement les buffers DMA dans une zone mémoire accessible par le GPU, le travail effectué par le CPU devrait être encore réduit dans des versions ultérieures du système de driver.
Pour aller un peu plus loin sur ces sujets, la lecture de la documentation MSDN est extrêmement intéressante. En particulier des articles comme :
“Windows Vista Display Driver Model Operation Flow”
(http://msdn.microsoft.com/library/en-us/Display_d/hh/Display_d/DisplayDriverModel_Guide_de7f80f4-0676-4325-86bf-1f47bc3efd6b.xml.asp ).
Cet article détaille toutes la suite des opérations effectuées par le code de l’application puis le runtime D3D, la partie du driver en mode utilisateur et la partie en mode Kernel (parfois appelée Display Miniport driver).
A suivre…
Je n’ai décris ici que les changements intervenu au niveau du système de driver sur lequel s’appuie DirectX 10. Ces informations sont importantes à connaitre pour avoir une bonne compréhension de la façon dont fonctionne une application 3D sur Windows mais finalement pour le développeur il est tout aussi nécessaire de bien connaitre le pipeline graphique induit par une version donnée de DirectX ainsi que de savoir quels sont les objets et les fonctions de l’API qui vont pouvoir lui permettre d’utiliser effectivement toutes ces fonctionnalités. Comme je l’indiquai au début de ce document, ces aspects seront traités dans un autre article qui sera publié très prochainement au même endroit (http://www.microsoft.com/france/msdn/directx).
Pour tout commentaire ou question concernant ces documents et ces sujets n’hésitez pas à me contacter à l’adresse suivante guillara@microsoft.com .