Vulkan et DirectX12 sont censés être utilisables de manière thread-safe. Les gens semblent excités à ce sujet.
Pourquoi est-ce considéré comme une caractéristique si énorme? Le "vrai" traitement est de toute façon jeté sur le pont mémoire sur une unité de traitement distincte.
De plus, si elle est si grande, pourquoi n'est-ce pas maintenant qu'une API graphique sécurisée pour les threads est sortie?
vulkan
directx12
multithreading
monstre à cliquet
la source
la source
Réponses:
Le principal avantage serait qu'il serait plus facile de diviser les tâches CPU en plusieurs threads, sans avoir à résoudre tous les problèmes difficiles d'accès à l'API graphique. Normalement, vous devez soit actualiser le contexte (ce qui peut avoir de mauvaises implications en termes de performances), soit fournir une file d'attente et appeler les API graphiques dans un seul thread. Je ne pense pas que les performances soient gagnées de cette façon, car le GPU les traite de manière séquentielle de toute façon, mais cela facilite beaucoup le travail de développeur.
La raison pour laquelle cela n'a pas été fait jusqu'à présent est probablement parce que directx et opengl ont été créés à une époque où le multithreading n'était pas vraiment apparent. De plus, la carte Khronos est très conservatrice dans la modification de l'API. Leur point de vue sur Vulkan est également qu'il coexistera à côté d'OpenGL, car les deux ont des objectifs différents. Ce n'est probablement que récemment que le paralisme est devenu si important, car les consommateurs ont accès à de plus en plus de processeurs.
EDIT: Je ne veux pas dire qu'aucune performance n'est gagnée en travaillant sur plusieurs CPU, il n'est pas utile de diviser vos appels en plusieurs threads pour créer des textures / shaders plus rapidement. Au contraire, les performances sont gagnées en ayant plus de processeurs occupés et en gardant le GPU occupé avec des choses à effectuer.
la source
Il y a beaucoup de travail à faire sur le CPU pour configurer un cadre pour le GPU, et une bonne partie de ce travail se trouve dans le pilote graphique. Avant DX12 / Vulkan, ce travail de pilote graphique était essentiellement forcé d'être monothread par la conception de l'API.
L'espoir est que DX12 / Vulkan lève cette restriction, permettant au travail du pilote d'être effectué en parallèle sur plusieurs threads CPU dans un cadre. Cela permettra une utilisation plus efficace des processeurs multicœurs, permettant aux moteurs de jeu de pousser des scènes plus complexes sans devenir liés au processeur. C'est l'espoir - si cela se réalisera dans la pratique, c'est quelque chose que nous devrons attendre pour voir au cours des prochaines années.
Pour élaborer un peu: la sortie d'un moteur de rendu est un flux d'appels DX / GL API qui décrivent la séquence d'opérations pour rendre une image. Cependant, il existe une grande distance entre le flux d'appels d'API et les tampons de commandes binaires réels consommés par le matériel GPU. Le pilote doit "compiler" les appels API dans le langage machine du GPU, pour ainsi dire. Ce n'est pas un processus trivial - cela implique beaucoup de traduction des concepts de l'API dans des réalités matérielles de bas niveau, la validation pour s'assurer que le GPU n'est jamais défini dans un état invalide, la gestion des allocations de mémoire et des données, le suivi des changements d'état pour émettre le corriger les commandes de bas niveau, etc. Le pilote graphique est responsable de tout cela.
Dans DX11 / GL4 et les API antérieures, ce travail est généralement effectué par un seul thread de pilote. Même si vous appelez l'API à partir de plusieurs threads (ce que vous pouvez faire en utilisant des listes de commandes différées DX11, par exemple), cela ajoute simplement du travail à une file d'attente pour le thread du pilote à parcourir plus tard. Une grande raison à cela est le suivi de l'état que j'ai mentionné précédemment. La plupart des détails de configuration du GPU au niveau matériel nécessitent la connaissance de l'état actuel du pipeline graphique, donc il n'y a pas de bon moyen de diviser la liste de commandes en morceaux qui peuvent être traités en parallèle - chaque morceau devrait savoir exactement quel état il devrait démarrer. avec, même si le morceau précédent n'a pas encore été traité.
C'est l'une des grandes choses qui ont changé dans DX12 / Vulkan. D'une part, ils incorporent presque tous les états du pipeline graphique dans un objet, et pour un autre (au moins dans DX12) lorsque vous commencez à créer une liste de commandes, vous devez fournir un état initial du pipeline; l'état n'est pas hérité d'une liste de commandes à la suivante. En principe, cela permet au pilote de ne rien savoir des listes de commandes précédentes avant de pouvoir commencer à compiler - et cela à son tour permet à l'application de diviser son rendu en segments parallélisables, produisant des listes de commandes entièrement compilées, qui peuvent ensuite être concaténés ensemble et envoyés au GPU avec un minimum de tracas.
Bien sûr, il y a beaucoup d'autres changements dans les nouvelles API, mais en ce qui concerne le multithreading, c'est la partie la plus importante.
la source
Les GPU modernes ont généralement une section frontale unique qui traite un flux de commandes entièrement linéaire à partir du CPU. Que ce soit une conception matérielle naturelle ou qu'elle ait simplement évolué à une époque où il y avait un seul cœur de processeur générant des commandes pour le GPU est discutable, mais c'est la réalité pour l'instant. Donc, si vous générez un seul flux linéaire de commandes avec état, il est bien sûr logique de générer ce flux linéairement sur un seul thread sur le CPU! Droite?
Eh bien, les GPU modernes ont généralement un backend unifié très flexible qui peut fonctionner sur beaucoup de choses différentes à la fois. De manière générale, le GPU fonctionne sur les sommets et les pixels avec une granularité assez fine. Il n'y a pas beaucoup de différence entre un GPU traitant 1024 sommets en un seul dessin et 512 + 512 sommets en deux tirages différents.
Cela suggère un moyen assez naturel de faire moins de travail: au lieu de lancer un grand nombre de sommets sur le GPU en un seul appel, divisez votre modèle en sections, effectuez une élimination grossière à bas prix sur ces sections et soumettez chaque morceau individuellement s'il passe le test d'abattage. Si vous le faites à la bonne granularité, vous devriez obtenir une belle accélération!
Malheureusement, dans la réalité actuelle de l'API graphique, les appels de dessin sont extrêmement coûteux sur le processeur. Une explication simplifiée de pourquoi: les changements d'état sur le GPU peuvent ne pas correspondre directement aux appels de l'API graphique, donc de nombreux appels de l'API graphique définissent simplement un état dans le pilote, et l'appel de dessin qui dépendrait de ce nouvel état va et examine tous les état marqué comme ayant changé depuis le dernier tirage, l'écrit dans le flux de commandes du GPU, puis lance réellement le tirage. C'est tout le travail qui est fait dans le but d'obtenir un flux de commandes maigre et moyen pour l'unité frontale GPU.
Cela revient à dire que vous avez un budget pour les appels de tirage qui est entièrement imposé par les frais généraux du conducteur . (Je pense avoir entendu dire que ces jours-ci, vous pouvez vous en tirer avec environ 5 000 images par image pour un titre de 60 FPS.) Vous pouvez augmenter cela d'un grand pourcentage en créant ce flux de commandes en morceaux parallèles.
Il y a aussi d'autres raisons (par exemple, une distorsion temporelle asynchrone pour les améliorations de latence VR), mais c'est un gros problème pour les jeux graphiques et autres logiciels lourds (comme les packages de modélisation 3D).
la source