Changements par rapport à Direct3D 10

Cette section s’applique uniquement à Windows 7 et versions ultérieures, et Windows Server 2008 R2 et versions ultérieures du système d’exploitation Windows.

Les sections suivantes décrivent comment Direct3D 11 a changé à partir de Direct3D 10.

Fonctions de rappel de pilote vers les services Kernel-Mode

Les fonctions de rappel spécifiques à l’appareil que le runtime Direct3D version 11 fournit dans la structure D3DDDI_DEVICECALLBACKS lorsque le runtime appelle la fonction CreateDevice(D3D10) du pilote d’affichage en mode utilisateur isole le pilote des handles de noyau et des signatures de fonction noyau. Le runtime Direct3D version 11 modifie la sémantique de rappel et, par conséquent, l’implémentation des fonctions de rappel pour prendre en charge un mode d’opération thread libre, tandis que les runtimes de version Direct3D précédents ne prenaient pas en charge un mode d’opération sans threads. Les règles d’opération en mode thread libre s’appliquent après que le pilote indique qu’il prend en charge le mode thread libre (D3D11DDICAPS_FREETHREADED) ; sinon, les règles fortement restreintes précédentes s’appliquent. Pour plus d'informations sur comment le pilote indique qu’il prend en charge le mode à threads libres, consultez Threading et Listes de commandes. Les restrictions suivantes existent toujours pour Direct3D version 11 :

  • Un seul thread peut fonctionner sur un HCONTEXT à la fois. Les fonctions de rappel existantes qui utilisent actuellement un HCONTEXT sont pfnPresentCb, pfnRenderCb, pfnEscapeCb, pfnDestroyContextCb, pfnWaitForSynchronizationObjectCb et pfnSignalSynchronizationObjectCb. Par conséquent, si plusieurs threads appellent ces fonctions de rappel et utilisent le même HCONTEXT, le pilote doit synchroniser les appels aux fonctions de rappel. La satisfaction de cette exigence est tout à fait naturelle, car ces fonctions de rappel sont susceptibles d’être appelées uniquement à partir du thread qui manipule le contexte immédiat.

  • Le pilote doit appeler les fonctions de rappel suivantes uniquement pendant les appels aux fonctions de pilote suivantes à l’aide des mêmes threads que ceux qui ont appelé ces fonctions de pilote :

  • La fonction de rappel pfnDeallocateCb mérite une mention spéciale, car le pilote n’est pas requis pour appeler pfnDeallocateCb avant que le pilote ne retourne de sa fonction DestroyResource(D3D10) pour la plupart des types de ressources. Étant donné que DestroyResource(D3D10) est une fonction à thread libre, le pilote doit différer la destruction de l’objet jusqu’à ce que le pilote puisse s’assurer efficacement qu’aucune référence de contexte immédiate existante reste (autrement dit, le pilote doit appeler pfnRenderCb avant pfnDeallocateCb). Cette restriction s’applique même aux ressources partagées, aux primaires partagées ou à toute autre fonction de rappel qui utilise HRESOURCE pour compléter l’utilisation de HRESOURCE avec pfnAllocateCb. Cette restriction ne s’applique pas aux primaires. Pour plus d’informations sur les exceptions principales, consultez Exceptions primaires. Étant donné que certaines applications peuvent nécessiter l’apparence d’une destruction synchrone, le pilote doit s’assurer qu’il appelle pfnDeallocateCb pour toutes les ressources partagées précédemment détruites lors d’un appel à sa fonction Flush(D3D10). Un pilote doit également nettoyer les objets précédemment détruits (uniquement ceux qui ne bloquent pas le pipeline) pendant un appel à sa fonction Flush(D3D10) ; le pilote doit le faire pour s’assurer que le runtime appelle Flush(D3D10) comme mécanisme officiel pour nettoyer les objets détruits différés pour ces quelques applications qui peuvent nécessiter un tel mécanisme. Pour plus d’informations sur ce mécanisme, consultez Destruction différée et Vidage (D3D10). Le pilote doit également s’assurer que tous les objets pour lesquels la destruction a été différée sont entièrement détruits avant que la fonction DestroyDevice(D3D10) du pilote retourne pendant le nettoyage.

Rendre obsolète la possibilité d’autoriser la modification d'Free-Threaded DDIs

Pour Direct3D version 11, le concept de niveau API d’un appareil d’affichage et un contexte immédiat sont toujours regroupés au niveau DDI par le concept hérité d’un appareil d’affichage. Ce regroupement d’appareils d’affichage et de contexte immédiat optimise la compatibilité avec les DDI de version antérieure (par exemple, Direct3D version 10 DDI) et réduit les remplacements de pilotes lors de la prise en charge de plusieurs versions d’API via plusieurs versions de DDI. Toutefois, ce regroupement d’appareils d’affichage et de contexte immédiat entraîne une DDI plus déroutante, car les domaines de threading ne sont pas extrêmement explicites. Au lieu de cela, pour comprendre les exigences de threading de plusieurs interfaces et les fonctions de ces interfaces, les développeurs de pilotes doivent faire référence à la documentation.

Une fonctionnalité principale de l’API Direct3D version 11 est qu’elle permet à plusieurs threads d’entrer simultanément des fonctions de création et de destruction. Une telle fonctionnalité est incompatible avec la possibilité de permettre au pilote de remplacer les pointeurs de la table de fonctions pour créer et détruire, comme le permettaient la sémantique Direct3D version 10 DDI pour les fonctions spécifiées dans D3D10DDI_DEVICEFUNCS et D3D10_1DDI_DEVICEFUNCS. Par conséquent, une fois que le pilote a transmis les pointeurs de fonction pour les créations (CreateDevice(D3D10)), le pilote ne doit pas tenter de modifier le comportement en modifiant ces pointeurs de fonction particuliers lorsque le pilote s’exécute sous direct3D version 11 DDI et pendant que le pilote prend en charge le thread DDI. Cette restriction s’applique à toutes les fonctions d’appareil qui commencent par pfnCreate, pfnOpen, pfnDestroy, pfnCalcPrivate et pfnCheck. Tous les autres fonctions de l’appareil sont fortement associées au contexte immédiat. Étant donné qu’un thread unique manipule le contexte immédiat à la fois, il est bien défini pour continuer à autoriser le pilote à permuter à chaud les entrées de la table de fonction de contexte immédiat.

pfnRenderCb contre pfnPerformAmortizedProcessingCb

Les fonctions d’API Direct3D version 10 ont connecté la fonction de rappel du noyau pfnRenderCb du runtime Direct3D pour effectuer un traitement amorti (autrement dit, au lieu d’exécuter certaines opérations pour chaque appel de fonction API, le pilote a effectué des opérations amorties pour chaque tant d’appels de fonction API). L’API utilise généralement cette opportunité pour réduire les seuils élevés et purger sa file d'attente de destruction d'objets différée, parmi d'autres opérations.

Pour permettre aux fonctions de rappel du noyau d’être aussi libres que possible pour le pilote, l’API Direct3D n’utilise plus pfnRenderCb lorsque le pilote prend en charge direct3D version 11 DDI. Par conséquent, les pilotes qui prennent en charge la DDI version Direct3D 11 doivent appeler manuellement la fonction de rappel du noyau pfnPerformAmortizedProcessingCb à partir du même thread que celui qui a appelé la fonction DDI du pilote, après l’envoi par le pilote d’une mémoire tampon de commande dans le contexte immédiat (ou à une fréquence similaire). Étant donné que l’opération doit réduire les filigranes élevés, il serait avantageux de le faire avant que le pilote génère des préambules de mémoire tampon de commande lors de l’utilisation des fonctions de rappel DDI d’actualisation de l’état.

En outre, le pilote doit être conscient du problème d’amortissement de l’API et chercher à équilibrer la fréquence d'utilisation de la fonction de rappel du noyau pfnPerformAmortizedProcessingCb. À l'un des extrêmes, le pilote peut entraîner un sur-traitement. Par exemple, si le pilote a toujours appelé pfnPerformAmortizedProcessingCb deux fois (back-to-back), éventuellement en raison de l’utilisation de plusieurs moteurs, il serait plus efficace pour le pilote d’appeler pfnPerformAmortizedProcessingCb qu’une seule fois. À l’autre extrême, le pilote peut ne pas autoriser l’API Direct3D à effectuer un travail pour une image entière si le pilote n’a jamais appelé pfnPerformAmortizedProcessingCb, éventuellement en raison d’une conception de rendu d’images alternée. Le pilote n’est pas obligé d’appeler pfnPerformAmortizedProcessingCb plus souvent qu’il ne le ferait naturellement, car ce serait excessif ; par exemple, si le pilote n’a pas appelé pfnPerformAmortizedProcessingCb dans un délai de 1 milliseconde, cela doit indiquer qu’il est temps d’activer l’API. Le pilote doit simplement déterminer quels appels pfnRenderCb existants doivent être accompagnés par pfnPerformAmortizedProcessingCb et, naturellement, conforme à la sémantique de threading de l’opération.

Pour les pilotes qui prennent en charge les listes de commandes, ceux-ci doivent également appeler pfnPerformAmortizedProcessingCb à partir de contextes différés chaque fois que ces pilotes atteignent leur capacité maximale (à une fréquence similaire à chaque vidage de contexte immédiat). Le runtime Direct3D version 11 s’attend à diminuer au moins ses jalons élevés pendant une telle opération. Étant donné que la sémantique de threading liée à pfnRenderCb a été assouplie pour Direct3D version 11, les problèmes de concurrence doivent être résolus pour permettre à Direct3D version 11 de continuer à raccorder pfnRenderCb, sans restriction.

Nouveau code d’erreur DDI

Le code d’erreur D3DDDIERR_APPLICATIONERROR est créé pour permettre aux pilotes de s'impliquer dans la validation, là où l’API Direct3D version 11 n'était pas impliqué. Auparavant, si le pilote retournait le code d’erreur E_INVALIDARG, l’API déclencherait une exception. La présence de la couche de débogage génèrerait des sorties de débogage et indiquerait que le pilote a retourné une erreur interne. La sortie de débogage indiquerait au développeur qu'il existe un bogue dans le pilote. Si le pilote retourne D3DDDIERR_APPLICATIONERROR, la couche de débogage détermine que l’application est en panne, à la place.

Exiger rétroactivement des DDI CalcPrivate à threading libre

Direct3D version 11 nécessite rétroactivement que les fonctions de pilote commençant par pfnCalcPrivate sur les fonctions DDI de Direct3D version 10 soient thread-safe. Cette exigence rétroactive correspond au comportement de Direct3D version 11 DDI, qui nécessite toujours que les fonctions pfnCalcPrivate* et pfnCalcDeferredContextHandleSize soient libres de thread, même si le pilote indique qu’il ne prend pas en charge le threading DDI. Pour plus d'informations sur cette exigence rétroactive, consultez Exigence rétroactive pour CalcPrivate DDIs Free-Threaded.

Destruction différée et vidage D3D10

Étant donné que toutes les fonctions de destruction sont désormais multithreadées, l'environnement d'exécution Direct3D ne peut pas vider un tampon de commandes lors de la destruction. Par conséquent, les fonctions de destruction doivent différer la destruction réelle d’un objet jusqu’à ce que le pilote puisse s’assurer que le thread qui manipule le contexte immédiat n’est plus dépendant de cet objet pour survivre. Chaque méthode contextuelle immédiate discrète ne peut pas utiliser efficacement la synchronisation pour résoudre ce problème de destruction ; par conséquent, le pilote doit utiliser la synchronisation uniquement lorsqu’il vide une mémoire tampon de commande. Le runtime Direct3D utilise également cette même conception quand il doit traiter des problèmes similaires.

En raison de la ratification de la destruction différée, le runtime Direct3D préconise que les applications qui ne peuvent pas tolérer les solutions de contournement de destruction différée utilisent plutôt des mécanismes explicites. Par conséquent, le pilote doit traiter sa file d’attente de destruction différée pendant les appels à sa fonction Flush(D3D10) (même si la mémoire tampon de commande est vide) pour s’assurer que ces mécanismes fonctionnent réellement.

Les applications qui nécessitent une forme de destruction synchrone doivent utiliser l’un des modèles suivants, en fonction de la façon dont elles nécessitent une destruction lourde :

  • Une fois que l’application garantit que toutes les dépendances de cet objet sont publiées (autrement dit, les listes de commandes, les vues, les intergiciels, et ainsi de suite), l’application utilise le modèle suivant :

    Object::Release(); // Final release
    ImmediateContext::ClearState(); // Remove all ImmediateContext references as well.
    ImmediateContext::Flush(); // Destroy all objects as quickly as possible.
    
  • Le modèle suivant est une destruction plus lourde :

    Object::Release(); // Final release
    ImmediateContext::ClearState(); // Remove all ImmediateContext references as well.
    ImmediateContext::Flush();
    ImmediateContext::End( EventQuery );
    while( S_FALSE == ImmediateContext::GetData( EventQuery ) ) ;
    ImmediateContext::Flush(); // Destroy all objects, completely.
    

Exceptions principales

Les primaires sont des ressources que le runtime crée dans les appels à la fonction CreateResource(D3D11) du pilote. Le runtime crée un principal en définissant le membre pPrimaryDesc de la structure D3D11DDIARG_CREATERESOURCE sur un pointeur valide vers une structure DXGI_DDI_PRIMARY_DESC . Les primaires partagées suivent les règles de toutes les ressources partagées. Les primaires non partagées ont les exceptions notables suivantes en ce qui concerne les modifications précédentes de Direct3D 10 à Direct3D 11 :

  • Les fonctions du pilote CreateResource(D3D11) et DestroyResource(D3D10) pour les primaires ne sont pas librement threadées et partagent le domaine de thread du contexte immédiat. La concurrence peut toujours exister avec des fonctions qui commencent par pfnCreate et pfnDestroy, qui incluent CreateResource(D3D11) et DestroyResource(D3D10). Toutefois, la concurrence ne peut pas exister avec CreateResource(D3D11) et DestroyResource(D3D10) pour les primaires. Par exemple, le pilote peut détecter qu’un appel à sa fonction CreateResource(D3D11) ou DestroyResource(D3D10) est destiné à un serveur principal et détermine ainsi qu’il peut utiliser ou toucher en toute sécurité la mémoire contextuelle immédiate pendant la durée de l’appel de fonction.

  • La destruction principale ne peut pas être différée par le runtime Direct3D, et le pilote doit appeler la fonction pfnDeallocateCb de manière appropriée dans un appel à la fonction DestroyResource(D3D10) du pilote.