Modifications de Direct3D 10
Cette section s’applique uniquement à Windows 7 et versions ultérieures, ainsi qu’à Windows Server 2008 R2 et versions ultérieures du système d’exploitation Windows.
Les sections suivantes décrivent comment Direct3D 11 a changé de Direct3D 10.
Fonctions de rappel de pilote vers Kernel-Mode Services
Les fonctions de rappel spécifiques aux appareils 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 isolent le pilote des handles de noyau et des signatures de fonction de 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 de fonctionnement à thread libre, alors que les runtimes de version Direct3D précédents ne prenaient pas en charge un mode de fonctionnement avec thread libre. Les règles pour l’opération en mode thread libre s’appliquent une fois que le pilote indique qu’il prend en charge le mode à thread libre (D3D11DDICAPS_FREETHREADED) ; dans le cas contraire, les règles très restreintes précédentes s’appliquent. Pour plus d’informations sur la façon dont le pilote indique la prise en charge du mode thread libre, consultez Listes de threads et 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. Satisfaire cette exigence est tout à fait naturel, 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 en utilisant les mêmes threads que ceux qui ont appelé ces fonctions de pilote :
pfnAllocateCb : le pilote doit appeler pfnAllocateCb sur le thread qui a appelé la fonction CreateResource(D3D11) du pilote lors de la création de ressources partagées. Les allocations non partagées régulières avec l’appareil sont entièrement à thread libre.
pfnPresentCb : le pilote doit appeler pfnPresentCb uniquement pendant les appels à la fonction PresentDXGI du pilote.
pfnSetDisplayModeCb : le pilote doit appeler pfnSetDisplayModeCb uniquement pendant les appels à la fonction SetDisplayModeDXGI du pilote.
pfnRenderCb : le pilote doit appeler pfnRenderCb sur le thread qui a appelé la fonction Flush(D3D10) du pilote. Cette restriction est tout à fait naturelle en raison des restrictions HCONTEXT.
La fonction de rappel pfnDeallocateCb mérite une mention spéciale, car le pilote n’est pas obligé d’appeler pfnDeallocateCb avant que le pilote ne retourne à partir 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 ne reste (autrement dit, le pilote doit appeler pfnRenderCb avant pfnDeallocateCb). Cette restriction s’applique même aux ressources partagées ou à toute autre fonction de rappel qui utilise HRESOURCE pour compléter l’utilisation de HRESOURCE avec pfnAllocateCb. Toutefois, cette restriction ne s’applique pas aux primaires. Pour plus d’informations sur les exceptions principales, consultez Exceptions principales. Étant donné que certaines applications peuvent nécessiter l’apparition 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 tous les objets précédemment détruits (uniquement ceux qui ne bloquent pas le pipeline) lors d’un appel à sa fonction Flush(D3D10) ; Le pilote doit le faire pour s’assurer que le runtime appelle Flush(D3D10) en tant que mécanisme officiel pour nettoyer les objets détruits différés pour les 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 ne retourne pendant le nettoyage.
Déprécier la possibilité d’autoriser la modification de Free-Threaded DDIs
Pour Direct3D version 11, le concept au niveau de l’API d’un périphérique d’affichage et un contexte immédiat sont toujours regroupés au niveau DDI par le concept hérité d’un périphérique d’affichage. Ce regroupement du périphérique d’affichage et du contexte immédiat optimise la compatibilité avec les DDIs de version antérieure (par exemple, direct3D version 10 DDI) et réduit l’attrition des pilotes lors de la prise en charge de plusieurs versions d’API via plusieurs versions de DDIs. Toutefois, ce regroupement d’appareils d’affichage et de contexte immédiat entraîne un DDI plus confus, car les domaines de thread ne sont pas extrêmement explicites. Au lieu de cela, pour comprendre les exigences de thread de plusieurs interfaces et les fonctions au sein de ces interfaces, les développeurs de pilotes doivent se reporter à 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 l’autorisation du pilote d’échanger les pointeurs de table de fonctions pour créer et détruire, car la sémantique DDI direct3D version 10 pour les fonctions spécifiées dans D3D10DDI_DEVICEFUNCS et D3D10_1DDI_DEVICEFUNCS autorisée. Par conséquent, une fois que le pilote a renvoyé 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 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. Toutes 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 de continuer à autoriser le pilote à permuter à chaud les entrées de la table de fonction de contexte immédiat.
pfnRenderCb versus pfnPerformAmortizedProcessingCb
Les fonctions de l’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 filigranes élevés et vider sa file d’attente de destruction d’objets différée, entre autres choses.
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 direct3D version 11 DDI doivent appeler manuellement la fonction de rappel du noyau pfnPerformAmortizedProcessingCb à partir du thread qui est entré dans la fonction DDI du pilote après que le pilote a envoyé une mémoire tampon de commandes sur 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 d’état.
En outre, le pilote doit être conscient du problème d’amortissement de l’API et essayer d’équilibrer la fréquence à laquelle il utilise la fonction de rappel du noyau pfnPerformAmortizedProcessingCb . À l’extrême, le pilote peut entraîner un surtraitment. Par exemple, si le pilote a toujours appelé pfnPerformAmortizedProcessingCb deux fois (dos à dos), peut-être en raison de l’utilisation de plusieurs moteurs, il serait plus efficace pour le pilote d’appeler pfnPerformAmortizedProcessingCb 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, peut-être en raison d’une conception de rendu d’image alternative. Le pilote n’est pas obligé d’appeler pfnPerformAmortizedProcessingCb plus souvent qu’il ne le ferait naturellement, car cela est excessif (par exemple, si le pilote n’a pas appelé pfnPerformAmortizedProcessingCb dans une période de 1 milliseconde, il doit être temps de pomper l’API). Le pilote doit uniquement déterminer quels appels pfnRenderCb existants doivent être accompagnés de pfnPerformAmortizedProcessingCb et, naturellement, conformes à la sémantique de thread de l’opération.
Pour les pilotes qui prennent en charge les listes de commandes, ces pilotes doivent également appeler pfnPerformAmortizedProcessingCb à partir de contextes différés chaque fois que ces pilotes sont à court de place (une fréquence similaire à chaque vidage de contexte immédiat). Le runtime Direct3D version 11 s’attend à, au moins, à réduire ses filigranes é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 d’accès concurrentiel doivent être résolus afin de 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 participer à la validation là où l’API Direct3D version 11 ne l’a pas fait. Auparavant, si le pilote renvoyait le code d’erreur E_INVALIDARG, l’API lessait une exception. La présence de la couche de débogage entraînerait une sortie de débogage et indiquerait que le pilote a retourné une erreur interne. La sortie de débogage suggère au développeur que le pilote a un bogue. Si le pilote retourne D3DDDIERR_APPLICATIONERROR, la couche de débogage détermine plutôt que l’application est défaillante.
Exiger rétroactivement Free-Threaded DDIs CalcPrivate
Direct3D version 11 nécessite rétroactivement que les fonctions de pilote qui commencent par pfnCalcPrivate sur Direct3D version 10 des fonctions DDI soient libres. Cette exigence rétroactive correspond au comportement de Direct3D version 11 DDI pour exiger toujours que les fonctions pfnCalcPrivate* et pfnCalcDeferredContextHandleSize soient threadées librement, même si le pilote indique qu’elle ne prend pas en charge le thread DDI. Pour plus d’informations sur cette exigence rétroactive, consultez Exigences rétroactives Free-Threaded DDIs CalcPrivate.
Destruction différée et vidage D3D10
Étant donné que toutes les fonctions de destruction sont désormais à thread libre, le runtime Direct3D ne peut pas vider une mémoire tampon de commande pendant 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 ne dépend plus de cet objet pour survivre. Chaque méthode de contexte immédiat discret 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 commandes. Le runtime Direct3D utilise également cette même conception lorsqu’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 des 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, selon la façon dont elles nécessitent une destruction lourde :
Une fois que l’application a assuré que toutes les dépendances sur cet objet sont libérées (c’est-à-dire, listes de commandes, vues, middleware, 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 présentent les exceptions notables suivantes en ce qui concerne les modifications précédentes de Direct3D 10 à Direct3D 11 :
Les fonctions CreateResource(D3D11) et DestroyResource(D3D10) du pilote pour les primaires ne sont pas à thread libre et partagent le domaine de thread de contexte immédiat. L’accès concurrentiel peut toujours exister avec des fonctions qui commencent par pfnCreate et pfnDestroy, notamment CreateResource(D3D11) et DestroyResource(D3D10). Toutefois, l’accès concurrentiel 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 ainsi déterminer 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.