Utilisation de handles DDI Context-Local
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.
Chaque objet (par exemple, ressource, nuanceur, etc.) a des handles DDI locaux contextuels.
Supposons qu’un objet soit utilisé avec trois contextes différés. Dans ce cas, quatre handles font référence au même objet (un handle pour chaque contexte différé et un autre pour le contexte immédiat). Étant donné que chaque contexte peut être manipulé simultanément par un thread, un handle local de contexte garantit que plusieurs threads d’UC ne font pas face à une mémoire similaire (intentionnellement ou involontairement). Les handles locaux contextuels sont également intuitifs, car le pilote doit probablement modifier une grande partie de ces données qui sont de toute façon associées logiquement par contexte (par exemple, l’objet peut être lié par le contexte, etc.).
Il existe toujours la distinction entre un handle de contexte immédiat et un handle de contexte différé. En particulier, le handle de contexte immédiat est garanti comme étant le premier handle alloué et le dernier handle qui est détruit. Le handle de contexte immédiat correspondant est fourni lors de l'« ouverture » de chaque handle de contexte différé pour les lier entre eux. Il n’existe actuellement aucun concept d’objet ayant un handle DDI par appareil (autrement dit, un handle qui est créé avant et détruit après le handle de contexte immédiat, et qui serait référencé uniquement dans l’ordre par la création du handle de contexte).
Certains handles ont des relations de dépendance avec d’autres handles (par exemple, les vues ont une dépendance sur leur ressource correspondante). La garantie d’ordre de création et de destruction qui existe pour le contexte immédiat est également étendue aux handles de contexte différés (autrement dit, le runtime crée un handle de ressource locale de contexte avant que le runtime ne crée des handles d’affichage local de contexte pour cette ressource, et le runtime détruit un handle de ressource locale de contexte après que le runtime a détruit tous les handles d’affichage local de contexte pour cette ressource). Lorsque le runtime crée un handle local contextuel, le runtime fournit également les handles de dépendance locaux de contexte correspondants.
Organisation des données du pilote
Certaines préoccupations concernant les données de pilote organization nécessitent une attention particulière. Comme Direct3D version 10, la localisation appropriée des données peut réduire les absences de cache entre l’API et le pilote. La localisation appropriée des données peut également empêcher le battage du cache, qui se produit lorsque plusieurs éléments de données fréquemment consultés sont tous résolus vers le même index de cache et épuisent l’association du cache. Le DDI a été conçu depuis Direct3D version 10 pour éviter que de tels problèmes ne se manifestent par le pilote informant l’API de la quantité de mémoire dont le pilote a besoin pour satisfaire un handle et l’API affectant la valeur du handle. Toutefois, les nouvelles préoccupations liées aux threads ont un impact sur la conception de DDI dans la période de Direct3D version 11.
Naturellement, les handles locaux de contexte permettent d’associer des données objet par contexte, ce qui évite les problèmes de conflit entre les threads. Toutefois, étant donné que ces données sont répliquées pour chaque contexte différé, la taille de ces données est une préoccupation majeure. Cela fournit la rationalisation naturelle pour partager des données en lecture seule entre le handle de contexte immédiat et les handles de contexte différés. Lors de la création de handles de contexte différés, le handle de contexte immédiat est fourni pour établir la connexion entre les handles. Toutefois, toutes les données qui se trouvent en dehors du contexte différé gèrent la localisation bénéficient d’avantages avec les données d’API, et le niveau d’indirection supplémentaire des données en lecture seule empêche les avantages de la localisation de s’étendre aux données en lecture seule. Certaines données en lecture seule peuvent être répliquées dans chaque région de gestion de contexte si les avantages de la localisation justifient la duplication des données. Toutefois, la mémoire qui sauvegarde chaque handle de contexte différé doit être considérée à un niveau tellement élevé qu’il peut être utile de déplacer des données qui ne sont pas adjacentes à partir du handle si ces données sont relativement volumineuses et ne sont pas accessibles aussi fréquemment que d’autres données. Dans l’idéal, le type de données qui est associé à chaque handle de contexte différé serait de toute façon toutes les données à haute fréquence ; par conséquent, les données ne seraient pas suffisamment volumineuses pour considérer la réinstallation nécessaire. Naturellement, le conducteur doit équilibrer ces motivations conflictuelles.
Pour que la conception des données du pilote soit compatible efficacement avec Direct3D version 10, mais pas divergente dans l’implémentation, les données en lecture seule doivent être situées contiguës (mais toujours séparées de et après) les données de gestion de contexte immédiat. Si le pilote utilise cette conception, le pilote doit savoir que le remplissage de ligne de cache est nécessaire entre les données de gestion de contexte immédiates et les données en lecture seule. Étant donné qu’un thread peut manipuler les données de chaque contexte fréquemment (si ce n’est pas simultané), des pénalités de faux partage se produisent entre les données de gestion de contexte immédiates et les données de contexte différées si le remplissage de ligne de cache n’est pas utilisé. La conception du pilote doit être consciente des pénalités de faux partage qui se manifestent si des pointeurs sont établis et parcourus régulièrement entre les régions de mémoire du handle de contexte.
Le runtime Direct3D utilise le DDI Direct3D 11 suivant pour les handles locaux de contexte différé :
La fonction CheckDeferredContextHandleSizes vérifie les tailles des espaces mémoire privés de pilote qui contiennent les données de handle des handles de contexte différé.
La fonction CalcDeferredContextHandleSize détermine la taille de la région de mémoire pour un contexte différé.
Pour que le runtime Direct3D récupère la taille de handle de contexte différée requise par le pilote, les fonctions DDI précédentes doivent être utilisées. Immédiatement après la création d’un objet pour le contexte immédiat, le runtime appelle CalcDeferredContextHandleSize pour interroger le pilote sur la quantité d’espace de stockage dont le pilote a besoin pour satisfaire les handles de contexte différés à cet objet. Toutefois, l’API Direct3D doit régler son allocation de mémoire CLS en déterminant le nombre de tailles de handle uniques et leurs valeurs accessibles ; le runtime appelle la fonction CheckDeferredContextHandleSizes du pilote pour obtenir ces informations. Par conséquent, lors de l’instanciation de l’appareil, l’API demande un tableau de tailles de handle de contexte différées par double interrogation. Le premier sondage consiste à demander le nombre de tailles retournées, tandis que le deuxième sondage passe dans un tableau pour récupérer la valeur de chaque taille. Le pilote doit indiquer la quantité de mémoire dont il a besoin pour satisfaire un handle, ainsi que le type de handle. Le pilote peut retourner plusieurs tailles associées à un type de handle particulier. Toutefois, il n’est pas défini pour que le pilote retourne une valeur de CalcDeferredContextHandleSize qui n’a pas été retournée de manière correspondante dans le tableau CheckDeferredContextHandleSizes .
En ce qui concerne la création des handles DDI, les méthodes create sur le contexte différé sont utilisées. Par exemple, examinez les fonctions CreateBlendState(D3D10_1) et DestroyBlendState . Le HDEVICE pointe naturellement vers le contexte différé approprié (par rapport au contexte immédiat) ; les autres pointeurs de structure CONST sont NULL (en supposant que l’objet n’a aucune dépendance) ; et, le handle D3D10DDI_HRT* est un handle D3D10DDI_H* à l’objet de contexte immédiat correspondant.
Pour les objets qui ont des dépendances (par exemple, les vues ont une relation de dépendance sur leur ressource correspondante), le pointeur de structure qui fournit le handle de dépendance n’est pas NULL. Toutefois, le seul membre valide de la structure est le handle de dépendance ; tandis que le reste des membres est rempli avec zéro. Par exemple, le pointeur D3D11DDIARG_CREATESHADERRESOURCEVIEW dans un appel à la fonction CreateShaderResourceView(D3D11) du pilote ne sera pas NULL lorsque le runtime appelle cette fonction sur un contexte différé. Dans cet appel CreateShaderResourceView(D3D11), le runtime affecte le handle local contextuel approprié pour la ressource au membre hDrvResource de D3D11DDIARG_CREATESHADERRESOURCEVIEW. Les autres membres de D3D11DDIARG_CREATESHADERRESOURCEVIEW, cependant, sont remplis avec zéro.
L’exemple de code suivant montre comment le runtime Direct3D traduit la demande de création d’une application et la première utilisation du contexte différé en appels au pilote d’affichage en mode utilisateur pour créer des contextes immédiats ou différés. L’appel de l’application à ID3D11Device::CreateTexture2D lance le code d’exécution dans la section « Création de ressources » suivante. L’appel de l’application à ID3D11Device::CopyResource lance le code d’exécution dans la section suivante « Utilisation différée des ressources de contexte ».
// Device Create
IC::pfnCheckDeferredContextHandleSizes( hIC, &u, NULL );
pArray = malloc( u * ... );
IC::pfnCheckDeferredContextHandleSizes( hIC, &u, pArray );
// Resource Create
s = IC::pfnCalcPrivateResourceSize( hIC, &Args );
pICRHandle = malloc( s );
IC::pfnCreateResource( hIC, &Args, pICRHandle, hRTResource );
s2 = IC::pfnCalcDeferredContextHandleSize( hIC, D3D10DDI_HT_RESOURCE, pICRHandle );
// Deferred Context Resource Usage
pDCRHandle = malloc( s2 );
DC::pfnCreateResource( hDC, NULL, pDCRHandle, pICRHandle );
Problèmes avec pfnSetErrorCb
Aucune des fonctions de création ne retourne de code d’erreur, qui aurait été idéal pour le modèle de thread direct3D version 11. Toutes les fonctions de création utilisent pfnSetErrorCb pour récupérer les codes d’erreur du pilote. Pour optimiser la compatibilité avec le modèle de pilote Direct3D version 10, de nouvelles fonctions de création DDI qui retournent des codes d’erreur n’ont pas été introduites. Au lieu de cela, le pilote doit continuer à utiliser l’appareil unifié/contexte immédiat D3D10DDI_HRTCORELAYER gérer avec pfnSetErrorCb pendant les fonctions de création. Lorsque le pilote prend en charge les listes de commandes, le pilote doit utiliser le pfnSetErrorCb approprié associé au contexte correspondant. Autrement dit, les erreurs de contexte différées doivent accéder à l’appel de contexte différé particulier à pfnSetErrorCb avec le handle correspondant, et ainsi de suite.
Les contextes différés peuvent retourner des E_OUTOFMEMORY via un appel à pfnSetErrorCb à partir de fonctions DDI qui n’autorisait auparavant que des D3DDDIERR_DEVICEREMOVED (comme Draw, SetBlendState, etc.), car les demandes de mémoire de contexte différée augmentent perpétuellement à chaque appel à une fonction DDI. L’API Direct3D déclenche une suppression de contexte local, pour aider le pilote à gérer un tel cas d’échec, ce qui désactive efficacement la liste de commandes partiellement créée. L’application continue de déterminer qu’elle enregistre une liste de commandes ; toutefois, lorsque l’application appelle finalement la fonction FinishCommandList , FinishCommandList retourne un code d’échec de E_OUTOFMEMORY.