Infrastructure graphique DirectX (DXGI) : meilleures pratiques

Microsoft DirectX Graphics Infrastructure (DXGI) est un nouveau sous-système introduit avec Windows Vista qui encapsule certaines des tâches de bas niveau requises par Direct3D 10, 10.1, 11 et 11.1. Du point de vue d’un programmeur Direct3D 9, DXGI englobe la majeure partie du code pour l’énumération, la création de chaînes d’échange et la présentation qui était précédemment emballé dans les API Direct3D 9. Lorsque vous portez une application vers DXGI et Direct3D 10.x et Direct3D 11.x, vous devez prendre en compte certaines considérations pour vous assurer que le processus s’exécute correctement.

Cet article traite des principaux problèmes de portage.

problèmes de Full-Screen

Lors du portage de Direct3D 9 vers DXGI et direct3D 10.x ou Direct3D 11.x, les problèmes liés au passage du fenêtrage au mode plein écran peuvent souvent causer des maux de tête pour les développeurs. Les problèmes main surviennent parce que les applications Direct3D 9, contrairement aux applications DXGI, nécessitent une approche plus pratique pour suivre les styles de fenêtre et les états de fenêtre. Lorsque le code de changement de mode est porté pour s’exécuter sur DXGI, cela provoque souvent un comportement inattendu.

Souvent, les applications Direct3D 9 géraient la transition en mode plein écran en définissant la résolution de la mémoire tampon avant, en forçant l’appareil en mode exclusif plein écran, puis en définissant les résolutions de la mémoire tampon d’arrière-plan pour qu’elles correspondent. Un chemin d’accès distinct a été utilisé pour les modifications apportées à la taille des fenêtres, car elles devaient être gérées à partir du processus de fenêtre chaque fois que l’application recevait un message WM_SIZE.

DXGI tente de simplifier cette approche en combinant les deux cas. Par exemple, lorsque la bordure de la fenêtre est déplacée en mode fenêtré, l’application reçoit un message WM_SIZE. DXGI intercepte ce message et redimensionne automatiquement la mémoire tampon avant. Il suffit que l’application appelle IDXGISwapChain::ResizeBuffers pour redimensionner la mémoire tampon d’arrière-mémoire à la taille qui a été passée en tant que paramètres dans WM_SIZE. De même, lorsque l’application doit basculer entre le mode plein écran et le mode fenêtré, l’application peut simplement appeler IDXGISwapChain::SetFullscreenState. DXGI redimensionne la mémoire tampon avant pour qu’elle corresponde au mode plein écran nouvellement sélectionné, et envoie un message WM_SIZE à l’application. L’application appelle à nouveau ResizeBuffers, comme si la bordure de la fenêtre était déplacée.

La méthodologie de l’explication précédente suit un chemin très particulier. DXGI définit la résolution plein écran sur la résolution du bureau par défaut. Toutefois, de nombreuses applications basculent vers une résolution plein écran préférée. Dans ce cas, DXGI fournit IDXGISwapChain::ResizeTarget. Il doit être appelé avant d’appeler SetFullscreenState. Bien que ces méthodes puissent être appelées dans l’ordre opposé (SetFullscreenState en premier, suivi de ResizeTarget), cela entraîne l’envoi d’un message WM_SIZE supplémentaire à l’application. (Cela peut également entraîner un scintillement, car DXGI peut être forcé d’effectuer deux modifications de mode.) Après avoir appelé SetFullscreenState, il est recommandé d’appeler à nouveau ResizeTarget avec le membre RefreshRate de DXGI_MODE_DESC mis à zéro. Cela équivaut à une instruction sans opération dans DXGI, mais cela peut éviter les problèmes liés à la fréquence d’actualisation, qui sont décrits ci-dessous.

En mode plein écran, le Gestionnaire de fenêtres de bureau (DWM) est désactivé. DXGI peut effectuer un retournement pour présenter le contenu de la mémoire tampon d’arrière-mémoire au lieu d’effectuer une fente, ce qu’il ferait en mode fenêtré. Toutefois, ce gain de performances peut être annulé si certaines exigences ne sont pas remplies. Pour garantir que DXGI effectue un basculement au lieu d’un blit, la mémoire tampon avant et la mémoire tampon arrière doivent être dimensionnées de la même façon. Si l’application gère correctement ses messages WM_SIZE, cela ne devrait pas être un problème. En outre, les formats doivent être identiques.

Le problème pour la plupart des applications est la fréquence d’actualisation. La fréquence d’actualisation spécifiée dans l’appel à ResizeTarget doit être une fréquence d’actualisation énumérée par l’objet IDXGIOutput utilisé par la chaîne d’échange. DXGI peut calculer automatiquement cette valeur si l’application met à zéro le membre RefreshRate de DXGI_MODE_DESC passé dans ResizeTarget. Il est important de ne pas supposer que certaines fréquences d’actualisation seront toujours prises en charge et de coder simplement en dur une valeur. Souvent, les développeurs choisissent 60 Hz comme fréquence d’actualisation, sans savoir que la fréquence d’actualisation énumérée à partir du moniteur est d’environ 60 000 / 1 001 Hz du moniteur. Si la fréquence d’actualisation ne correspond pas à la fréquence d’actualisation attendue de 60, DXGI est obligé d’effectuer un blit en mode plein écran au lieu d’un retournement.

Le dernier problème auquel les développeurs sont souvent confrontés est de savoir comment modifier les résolutions en plein écran tout en restant en mode plein écran. L’appel de ResizeTarget et SetFullscreenState réussit parfois, mais la résolution en plein écran reste la résolution du bureau. En outre, les développeurs peuvent créer une chaîne de permutation en plein écran et donner une résolution spécifique, uniquement pour découvrir que DXGI est défini par défaut sur la résolution du bureau, quels que soient les nombres transmis. Sauf indication contraire, DXGI utilise par défaut la résolution du bureau pour les chaînes de permutation en plein écran. Lors de la création d’une chaîne de permutation en plein écran, le membre Flags de la structure DXGI_SWAP_CHAIN_DESC doit être défini sur DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH pour remplacer le comportement par défaut de DXGI. Cet indicateur peut également être passé à ResizeTarget pour activer ou désactiver cette fonctionnalité de manière dynamique.

Plusieurs moniteurs

Lorsque vous utilisez DXGI avec plusieurs moniteurs, il existe deux règles à suivre.

La première règle s’applique à la création de deux chaînes d’échange en plein écran ou plus sur plusieurs moniteurs. Lors de la création de ces chaînes d’échange, il est préférable de créer toutes les chaînes d’échange comme fenêtrés, puis de les définir en mode plein écran. Si des chaînes d’échange sont créées en mode plein écran, la création d’une deuxième chaîne d’échange entraîne l’envoi d’un changement de mode à la première chaîne d’échange, ce qui peut entraîner l’arrêt du mode plein écran.

La deuxième règle s’applique aux sorties. Soyez vigilant sur les sorties utilisées lors de la création de chaînes d’échange. Avec DXGI, l’objet IDXGIOutput contrôle les contrôles qui surveillent la chaîne d’échange utilisée lors de l’affichage en mode plein écran. Contrairement à DXGI, Direct3D 9 n’avait aucun concept de sorties.

Styles de fenêtre et DXGI

Les applications Direct3D 9 avaient beaucoup de travail à faire lors du basculement entre le mode plein écran et le mode fenêtré. Une grande partie de ce travail consistait à modifier les styles de fenêtre pour ajouter et supprimer des bordures, ajouter des barres de défilement, etc. Lorsque des applications sont transférées vers DXGI et Direct3D 10.x ou Direct3D 11.x, ce code est souvent laissé en place. Selon les modifications apportées, le basculement entre les modes peut entraîner un comportement inattendu. Par exemple, lorsque vous passez en mode fenêtré, l’application peut ne plus avoir de cadre de fenêtre ou de bordure de fenêtre malgré le code qui définit spécifiquement ces styles. Cela se produit parce que DXGI gère désormais une grande partie de ce style qui change de lui-même. Le paramétrage manuel des styles de fenêtre peut interférer avec DXGI, ce qui peut entraîner un comportement inattendu.

Le comportement recommandé consiste à faire le moins de travail possible et à laisser DXGI gérer la plupart des interactions avec les fenêtres. Toutefois, si l’application doit gérer son propre comportement de fenêtrage, IDXGIFactory::MakeWindowAssociation peut être utilisé pour indiquer à DXGI de désactiver une partie de sa gestion automatique des fenêtres.

Multithreading et DXGI

Une attention particulière doit être prise lors de l’utilisation de DXGI dans une application multithread pour s’assurer que les interblocages ne se produisent pas. En raison de l’interaction étroite de DXGI avec le fenêtrage, il envoie parfois des messages de fenêtre à la fenêtre d’application associée. DXGI a besoin que les modifications de fenêtrage se produisent avant de pouvoir continuer, de sorte qu’il utilise SendMessage, qui est un appel synchrone. L’application doit traiter le message de fenêtre avant que SendMessage ne retourne.

Dans une application où les appels DXGI et la pompe de messages se trouvent sur le même thread (ou une application monothread), peu de choses doivent être effectuées. Lorsque l’appel DXGI se trouve sur le même thread que la pompe de messages, SendMessage appelle le WindowProc de la fenêtre. Cela contourne la pompe de messages et permet la poursuite de l’exécution après l’appel à SendMessage. N’oubliez pas que les appels IDXGISwapChain , tels que IDXGISwapChain::P resent, sont également considérés comme des appels DXGI ; DXGI peut différer le travail de ResizeBuffers ou ResizeTarget jusqu’à l’appel de Present .

Si l’appel DXGI et la pompe de messages se trouvent sur des threads différents, vous devez veiller à éviter les interblocages. Lorsque la pompe de messages et SendMessage se trouvent sur des threads différents, SendMessage ajoute un message à la file d’attente de messages de la fenêtre et attend que la fenêtre traite ce message. Si la procédure de fenêtre est occupée ou n’est pas appelée par la pompe de messages, le message peut ne jamais être traité et DXGI attendra indéfiniment.

Par exemple, si une application qui a sa pompe de messages sur un thread et son rendu sur un autre, elle peut vouloir changer de mode. Le thread de pompe de messages indique au thread de rendu de modifier les modes et attend que le changement de mode soit terminé. Toutefois, le thread de rendu appelle des fonctions DXGI, qui à leur tour appellent SendMessage, qui se bloque jusqu’à ce que la pompe de message traite le message. Un interblocage se produit car les deux threads sont maintenant bloqués et s’attendent l’un sur l’autre. Pour éviter cela, ne bloquez jamais la pompe de messages. Si un bloc est inévitable, toutes les interactions DXGI doivent se produire sur le même thread que la pompe de messages.

Gamma et DXGI

Bien que gamma puisse être mieux géré dans Direct3D 10.x ou Direct3D 11.x à l’aide de textures SRGB, la rampe gamma peut toujours être utile pour les développeurs qui veulent une valeur gamma différente de 2.2 ou qui utilisent un format cible de rendu qui ne prend pas en charge SRGB. Tenez compte de deux problèmes lors de la définition de la rampe gamma via DXGI. Le premier problème est que les valeurs de la rampe passées dans IDXGIOutput::SetGammaControl sont des valeurs float, et non des valeurs WORD . Vérifiez également que le code porté à partir de Direct3D 9 n’essaie pas de convertir en valeurs WORD avant de les transmettre à SetGammaControl.

Le deuxième problème est que, après avoir passé en mode plein écran, SetGammaControl peut ne pas sembler fonctionner, en fonction de l’objet IDXGIOutput utilisé. Lorsque vous passez en mode plein écran, DXGI crée un objet de sortie et utilise l’objet pour toutes les opérations suivantes sur la sortie. Si vous appelez SetGammaControl sur une sortie énumérée avant un basculement en mode plein écran, l’appel n’est pas dirigé vers la sortie que DXGI utilise actuellement. Pour éviter cela, appelez IDXGISwapChain::GetContainingOutput pour obtenir la sortie actuelle, puis appelez SetGammaControl hors de cette sortie pour obtenir le comportement correct.

Pour plus d’informations sur l’utilisation de la correction gamma, consultez Utilisation de la correction gamma.

DXGI 1.1

Le runtime Direct3D 11 inclus dans Windows 7 et installé sur Windows Vista inclut la version 1.1 de DXGI. Cette mise à jour ajoute des définitions pour un certain nombre de nouveaux formats (en particulier BGRA, biais X2 10 bits et compression de texture BC6H et BC7 de Direct3D 11), ainsi qu’une nouvelle version de la fabrique DXGI et des interfaces adaptateurs (CreateDXGIFactory1, IDXGIFactory1, IDXGIAdapter1) pour l’énumération des connexions Bureau à distance.

Lorsque vous utilisez Direct3D 11, le runtime utilise DXGI 1.1 par défaut lors de l’appel de D3D11CreateDevice ou D3D11CreateDeviceAndSwapChain avec un pointeur IDXGIAdapter NULL. La combinaison de l’utilisation de DXGI 1.0 et de DXGI 1.1 dans le même processus n’est pas prise en charge. Le mélange d’instances d’objet DXGI de différentes fabriques dans le même processus n’est pas non plus pris en charge. Par conséquent, lorsque vous utilisez DirectX 11, toute utilisation explicite des interfaces DXGI utilise un IDXGIFactory1 créé par le point d’entrée CreateDXGIFactory1 dans « DXGI.DLL » pour garantir que l’application utilise toujours DXGI 1.1.

DXGI 1.2

Le runtime Direct3D 11.1 inclus dans Windows 8 inclut également la version 1.2 de DXGI.

DXGI 1.2 active les fonctionnalités suivantes :

  • rendu stéréo

  • Formats de 16 bits par pixel

    • DXGI_FORMAT_B5G6R5_UNORM et DXGI_FORMAT_B5G5R5A1_UNORM sont désormais entièrement pris en charge
    • un nouveau format DXGI_FORMAT_B5G5R5A1_UNORM a été ajouté
  • formats vidéo

  • nouvelles interfaces DXGI

Pour plus d’informations sur les fonctionnalités de DXGI 1.2, consultez Améliorations de DXGI 1.2.