Programmation asynchrone (DirectX et C++)
Cette rubrique traite de différents points à prendre en compte lorsque vous utilisez la programmation et le thread asynchrones avec DirectX.
Programmation asynchrone et DirectX
Si vous venez d’apprendre directX, ou même si vous en avez l’expérience, envisagez de placer tout votre pipeline de traitement graphique sur un thread. Dans une scène donnée d’un jeu, il existe des ressources courantes telles que des bitmaps, des nuanceurs et d’autres ressources qui nécessitent un accès exclusif. Ces mêmes ressources nécessitent que vous synchronisez n’importe quel accès à ces ressources sur les threads parallèles. Le rendu est un processus difficile à paralléliser sur plusieurs threads.
Toutefois, si votre jeu est suffisamment complexe ou si vous cherchez à améliorer les performances, vous pouvez utiliser la programmation asynchrone pour paralléliser certains des composants qui ne sont pas spécifiques à votre pipeline de rendu. Les fonctionnalités matérielles modernes de plusieurs processeurs cœur et hyperthreaded, et votre application doit tirer parti de cela ! Vous pouvez vous en assurer en utilisant la programmation asynchrone pour certains des composants de votre jeu qui n’ont pas besoin d’un accès direct au contexte d’appareil Direct3D, par exemple :
- E/S de fichier
- physics
- Intelligence artificielle
- networking
- audio
- controls
- Composants d’interface utilisateur XAML
Votre application peut gérer ces composants sur plusieurs threads simultanés. Les E/S de fichiers, en particulier le chargement des ressources, bénéficient considérablement du chargement asynchrone, car votre jeu ou votre application peut être dans un état interactif alors que plusieurs (ou plusieurs centaines) mégaoctets de ressources sont chargés ou diffusés en continu. Le moyen le plus simple de créer et de gérer ces threads consiste à utiliser la bibliothèque de modèles parallèles et le modèle de tâche , comme contenu dans l’espace de noms de concurrence défini dans PPLTasks.h. L’utilisation de la bibliothèque de modèles parallèles tire directement parti de plusieurs processeurs cœurs et hyperthreads, et peut améliorer tout, des temps de charge perçus aux hitches et aux retards qui sont fournis avec des calculs de processeur ou un traitement réseau intensifs.
Remarque Dans une application plateforme Windows universelle (UWP), l’interface utilisateur s’exécute entièrement dans un appartement à thread unique (STA). Si vous créez une interface utilisateur pour votre jeu DirectX à l’aide de l’interopérabilité XAML, vous ne pouvez accéder aux contrôles qu’à l’aide de l’outil STA.
Multithreading avec des appareils Direct3D
Le multithreading pour les contextes d’appareil est disponible uniquement sur les appareils graphiques qui prennent en charge un niveau de fonctionnalité Direct3D de 11_0 ou version ultérieure. Toutefois, vous souhaiterez peut-être optimiser l’utilisation du GPU puissant dans de nombreuses plateformes, telles que les plateformes de jeux dédiées. Dans le cas le plus simple, vous souhaiterez peut-être séparer le rendu d’une superposition d’affichage tête-haut (HUD) à partir du rendu et de la projection de la scène 3D et faire en sorte que les deux composants utilisent des pipelines parallèles distincts. Les deux threads doivent utiliser le même ID3D11DeviceContext pour créer et gérer les objets de ressource (textures, maillages, nuanceurs et autres ressources), mais, qui est monothread, et qui nécessite que vous implémentez un mécanisme de synchronisation (comme des sections critiques) pour y accéder en toute sécurité. Et, si vous pouvez créer des listes de commandes distinctes pour le contexte de l’appareil sur différents threads (pour le rendu différé), vous ne pouvez pas lire ces listes de commandes simultanément sur la même instance ID3D11DeviceContext .
À présent, votre application peut également utiliser ID3D11Device, qui est sécurisé pour le multithreading, pour créer des objets de ressources. Pourquoi ne pas toujours utiliser ID3D11Device au lieu d’ID3D11DeviceContext ? Actuellement, la prise en charge du pilote pour le multithreading peut ne pas être disponible pour certaines interfaces graphiques. Vous pouvez interroger l’appareil et déterminer s’il prend en charge le multithreading, mais si vous cherchez à atteindre le public le plus large, vous pouvez vous en tenir à l’ID3D11DeviceContext à thread unique pour la gestion des objets de ressources. Cela dit, lorsque le pilote de périphérique graphique ne prend pas en charge la multithreading ou les listes de commandes, Direct3D 11 tente de gérer l’accès synchronisé au contexte de l’appareil en interne ; et si les listes de commandes ne sont pas prises en charge, elle fournit une implémentation logicielle. Par conséquent, vous pouvez écrire du code multithread qui s’exécutera sur des plateformes avec des interfaces graphiques qui ne prennent pas en charge le pilote pour l’accès au contexte de périphérique multithread.
Si votre application prend en charge des threads distincts pour le traitement des listes de commandes et pour l’affichage des images, vous souhaitez probablement conserver le GPU actif, en traitant les listes de commandes tout en affichant des images en temps opportun sans téglage ou décalage perceptibles. Dans ce cas, vous pouvez utiliser un ID3D11DeviceContext distinct pour chaque thread et partager des ressources (telles que des textures) en les créant avec l’indicateur D3D11_RESOURCE_MISC_SHARED. Dans ce scénario, ID3D11DeviceContext ::Flush doit être appelé sur le thread de traitement pour terminer l’exécution de la liste de commandes avant d’afficher les résultats du traitement de l’objet de ressource dans le thread d’affichage.
Rendu différé
Le rendu différé enregistre les commandes graphiques d’une liste de commandes afin qu’elles puissent être lues à un autre moment et sont conçues pour prendre en charge le rendu sur un thread lors de l’enregistrement des commandes pour le rendu sur des threads supplémentaires. Une fois ces commandes terminées, elles peuvent être exécutées sur le thread qui génère l’objet d’affichage final (mémoire tampon de trame, texture ou autre sortie graphique).
Créez un contexte différé à l’aide de l’ID3D11Device ::CreateDeferredContext (au lieu de D3D11CreateDevice ou D3D11CreateDeviceAndSwapChain, qui crée un contexte immédiat). Pour plus d’informations, consultez Rendu immédiat et différé.
Rubriques connexes