Barrières améliorées D3D12
L’interface DDI pour les barrières améliorées est disponible dans le WDK de Windows 11, version 22H2 (WDDM 3.0). Pour utiliser les barrières améliorées sur la version 22H2 (ou sur des versions antérieures du système d’exploitation), vous devez installer le Agility SDK 1.706.4-preview.
Les barrières améliorées D3D12 donnent aux développeurs un contrôle indépendant sur la synchronisation des travaux GPU, les transitions de mise en page des textures et le vidage du cache (accès à la mémoire des ressources). Cette fonctionnalité offre un ensemble d’APIs et de DDIs Direct3D qui permettent aux développeurs de contrôler indépendamment la synchronisation des travaux GPU, les transitions de mise en page des textures et le vidage du cache (accès à la mémoire des ressources).
Les barrières améliorées remplacent les barrières de ressources héritées par des types de barrières plus expressifs. Elles possèdent les caractéristiques suivantes :
- Moins de latence de synchronisation.
- Une réduction des vidages excessifs de cache.
- Aucune règle mystérieuse de Promotion et Déclin (Promotion and Decay).
- Aliasing rapide et flexible des ressources (topologies d’aliasing diversifiées).
- Mise au rebut pendant la transition de barrière.
- Prise en charge de la lecture/écriture concurrente, y compris la copie de la même ressource (auto-copie).
- Prise en charge des commandes de mise au rebut asynchrone, de copie, de résolution et de nettoyage.
Les barrières améliorées ne sont pas plus simples que les barrières de ressources héritées, mais elles sont moins ambiguës et donc plus faciles à utiliser pour les développeurs.
Rapport de prise en charge des barrières améliorées
La fonctionnalité des barrières améliorées n’est pas actuellement une exigence matérielle ou de pilote. Un pilote indique sa prise en charge en définissant l’élément EnhancedBarriersSupported de D3D12DDI_D3D12_OPTIONS_DATA_0089 à TRUE.
- D3D12DDI_FEATURE_VERSION_VIDEO_0088_0 est le numéro de version qui définit la mise en œuvre préliminaire des étapes importantes des barrières améliorées D3D12 introduites dans Windows 11.
Fonctions de rappel des barrières améliorées D3D12
Les pilotes qui indiquent la prise en charge des barrières améliorées implémentent les fonctions de rappel suivantes :
- PFND3D12DDI_BARRIER_0088
- PFND3D12DDI_CREATEHEAPANDRESOURCE_0088
- PFND3D12DDI_CALCPRIVATEHEAPANDRESOURCESIZES_0088
- PFND3D12DDI_CHECKRESOURCEALLOCATIONINFO_0088
Détails de conception
Les pilotes gèrent généralement les barrières de ressources héritées à l’aide de trois opérations distinctes :
- Synchroniser le travail GPU.
- Effectuer les vidages de cache nécessaires.
- Effectuer les changements de mise en page nécessaires.
Les barrières améliorées donnent aux développeurs la possibilité de contrôler chacune de ces opérations séparément.
Types de barrières améliorées
Il existe trois types de barrières améliorées :
Les barrières à plage remplacent les barrières de ressources héritées. Les barrières à plage sont fournies afin que les barrières de ressources héritées puissent être entièrement mises en œuvre sans perte de performances notable.
Tous les types de barrières contrôlent la synchronisation des travaux GPU et les types d’accès en lecture ou en écriture avant et après la barrière.
Les barrières de texture gèrent également la mise en page des sous-ressources de texture. La sélection de sous-ressources peut être exprimée comme une plage de tranches mip, d’array et de plan, en plus de l’option familière une-ou-toutes utilisée par les barrières de ressources héritées.
Les barrières de tampon et les barrières globales ne contrôlent que la synchronisation et l’accès aux ressources et n’affectent pas la mise en page des ressources (les tampons n’ont pas de mise en page). Les barrières globales affectent toute la mémoire en cache, elles peuvent donc être coûteuses et ne doivent être utilisées que lorsqu’une barrière plus ciblée est insuffisante.
Barrières de texture
- Elles contrôlent le vidage de cache, la mise en page de la mémoire et la synchronisation des sous-ressources de texture.
- Elles ne doivent être utilisées qu’avec des ressources de texture.
- Elles permettent la sélection d’une sous-ressource unique, de toutes les sous-ressources ou d’une plage cohérente de sous-ressources (c’est-à-dire une plage mip et une plage d’array).
- Elles doivent fournir un pointeur de ressource valide et non NULL.
Barrières de tampon
- Elles contrôlent le vidage de cache et la synchronisation des ressources de tampon.
- Elles ne doivent être utilisées qu’avec des ressources de tampon.
- Contrairement aux textures, les tampons n’ont qu’une seule sous-ressource et n’ont pas de mise en page pouvant être transitionnée.
- Elles doivent fournir un pointeur de ressource valide et non NULL.
Barrières globales
- Elles contrôlent le vidage de cache et la synchronisation pour tous les types d’accès aux ressources indiqués dans une seule file de commandes.
- Elles n’ont aucun effet sur la mise en page des textures.
- Elles sont nécessaires pour fournir une fonctionnalité similaire aux anciennes barrières NULL UAV et aux barrières d’aliasing NULL/NULL.
Étant donné que les barrières globales ne changent pas la mise en page des textures, elles ne peuvent pas être utilisées dans des transitions qui nécessiteraient autrement un changement de mise en page. Par exemple, une barrière globale ne peut pas être utilisée pour faire passer une texture à accès non simultané de D3D12DDI_BARRIER_ACCESS_RENDER_TARGET à D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE, car cela nécessiterait également un changement de D3D12DDI_BARRIER_LAYOUT_RENDER_TARGET à D3D12DDI_BARRIER_LAYOUT_SHADER_RESOURCE.
Synchronization
Les processeurs graphiques sont conçus pour exécuter autant de travaux que possible en parallèle. Tout travail GPU qui dépend de travaux GPU précédents doit être synchronisé avant d’accéder aux données dépendantes.
L’interface des barrières améliorées utilise des valeurs explicites SyncBefore et SyncAfter comme masques de champ de bits logiques. Une barrière doit attendre que tous les SyncBefore des commandes précédentes soient terminés avant d’exécuter la barrière. De même, une barrière doit bloquer tous les SyncAfter suivants jusqu’à ce que la barrière soit terminée. D3D12DDI_BARRIER_SYNC spécifie la portée de synchronisation du travail GPU par rapport à la barrière.
Pour plus d’informations, veuillez consulter la spécification des barrières améliorées.
Transitions de mise en page
Les sous-ressources de texture peuvent utiliser différentes mises en page pour diverses méthodes d’accès. Par exemple, les textures sont souvent compressées lorsqu’elles sont utilisées comme cible de rendu ou comme pochoir de profondeur et sont souvent décompressées pour les commandes de lecture ou de copie du shader. Les barrières de texture utilisent les valeurs LayoutBefore et LayoutAfter D3D12DDI_BARRIER_LAYOUT pour décrire les transitions de mise en page.
Les transitions de mise en page ne sont nécessaires que pour les textures, elles sont donc exprimées uniquement dans la structure de données D3D12DDI_TEXTURE_BARRIER.
Les valeurs LayoutBefore et LayoutAfter doivent toutes deux être compatibles avec le type de file exécutant la barrière. Par exemple, une file de calcul ne peut pas faire passer une sous-ressource dans ou hors de D3D12DDI_BARRIER_LAYOUT_RENDER_TARGET.
Pour fournir un ordre de barrières bien défini, la mise en page d’une sous-ressource après l’achèvement d’une séquence de barrières est la dernière valeur LayoutAfter de la séquence.
Transitions d’accès
Étant donné que de nombreuses opérations d’écriture GPU sont mises en cache, toute barrière entre un accès en écriture et un autre accès en écriture, ou un accès en lecture seule, peut nécessiter un vidage du cache. Les APIs de barrières améliorées utilisent les transitions d’accès pour indiquer que la mémoire d’une sous-ressource doit être rendue visible pour un nouveau type d’accès spécifique. Comme pour les transitions de mise en page, certaines transitions d’accès peuvent ne pas être nécessaires si l’on sait que la mémoire de la sous-ressource associée est déjà accessible pour l’utilisation souhaitée.
Les transitions d’accès sont exprimées comme suit :
- Pour les textures, dans le cadre de la structure D3D12DDI_TEXTURE_BARRIER.
- Pour les tampons, dans le cadre de la structure D3D12DDI_BUFFER_BARRIER.
Les transitions d’accès ne réalisent pas de synchronisation. Il est attendu que la synchronisation entre les accès dépendants soit gérée en utilisant les valeurs appropriées SyncBefore et SyncAfter dans la barrière.
Un AccessBefore rendu visible pour un AccessAfter spécifié ne garantit pas que la mémoire de la ressource soit également visible pour un autre type d’accès. Par exemple :
MyTexBarrier.AccessBefore=D3D12DDI_BARRIER_ACCESS_UNORDERED_ACCESS;
MyTexBarrier.AccessAfter=D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE;
Cette transition d’accès indique qu’un accès en lecture par shader dépend d’une écriture précédente sans ordre. Cependant, si le matériel est capable de lire les ressources shader directement depuis le cache UAV, le pilote peut ne pas réellement vider le cache UAV.
D3D12DDI_BARRIER_ACCESS_COMMON
D3D12DDI_BARRIER_ACCESS_COMMON est un type d’accès spécial qui indique tout accès compatible avec la mise en page. La transition vers D3D12DDI_BARRIER_ACCESS_COMMON signifie que les données de sous-ressource doivent être disponibles pour tout accès compatible avec la mise en page après une barrière. Étant donné que les tampons n’ont pas de mise en page, D3D12DDI_BARRIER_ACCESS_COMMON signifie simplement tout accès compatible avec les tampons.
Spécifier D3D12DDI_BARRIER_ACCESS_COMMON comme AccessBefore dans une barrière implique l’ensemble de tous les types d’accès en écriture. L’utilisation de D3D12DDI_BARRIER_ACCESS_COMMON comme AccessBefore est déconseillée car cela pourrait entraîner des vidages de cache coûteux et non souhaités. Il est plutôt recommandé aux développeurs d’utiliser uniquement les bits d’accès en écriture les plus strictement nécessaires pour limiter correctement la surcharge des barrières. Un avertissement de couche de débogage est émis lorsque AccessBefore est défini sur D3D12DDI_BARRIER_ACCESS_COMMON.
Accès simultané à une seule file
Les barrières améliorées permettent des opérations de lecture/écriture concurrentes sur le même tampon ou une texture à accès simultané dans la même file de commandes.
Les tampons et les ressources à accès simultané ont toujours pris en charge l’accès en écriture depuis une file avec des accès en lecture concurrente et non dépendants depuis une ou plusieurs autres files. Cette prise en charge existe parce que ces ressources utilisent toujours la mise en page COMMON et n’ont pas de risques de lecture/écriture puisque les lectures ne doivent pas dépendre des écritures concurrentes. (Les règles des barrières de ressources héritées interdisent la combinaison des bits d’état d’écriture avec d’autres bits d’état. Par conséquent, les ressources ne peuvent pas être lues et écrites simultanément dans la même file en utilisant des barrières de ressources héritées).
La politique d’un seul rédacteur à la fois s’applique toujours, car deux régions d’écriture apparemment non superposées peuvent tout de même avoir des lignes de cache qui se chevauchent.
Plages de sous-ressources
Il est courant que les développeurs souhaitent faire passer une plage de sous-ressources. Un exemple est la transition d’une chaîne mip complète pour un tableau de textures donné ou un seul niveau mip pour toutes les tranches d’array. Les barrières améliorées permettent aux développeurs de faire passer des plages logiquement adjacentes de sous-ressources en utilisant la structure D3D12DDI_BARRIER_SUBRESOURCE_RANGE. (Les barrières de transition d’état de ressource héritées n’offrent aux développeurs que la possibilité de faire passer tous les états de sous-ressources ou un seul état de sous-ressource de manière atomique).
Mises en page des files de calcul et directes
Les mises en page de barrières améliorées suivantes sont garanties identiques pour les files directes et de calcul :
- D3D12DDI_BARRIER_LAYOUT_GENERIC_READ
- D3D12DDI_BARRIER_LAYOUT_UNORDERED_ACCESS
- D3D12DDI_BARRIER_LAYOUT_SHADER_RESOURCE
- D3D12DDI_BARRIER_LAYOUT_COPY_SOURCE
- D3D12DDI_BARRIER_LAYOUT_COPY_DEST
Une sous-ressource dans l’une de ces mises en page peut être utilisée dans les files directes ou de calcul sans transition de mise en page.
Sur certains matériels, les barrières de transition de mise en page dans les files directes peuvent être nettement plus rapides si les accès précédents ou suivants se font également sur des files directes. Il est fortement recommandé d’accéder aux ressources sur les files directes en utilisant les mises en page suivantes :
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST
Les variantes de mise en page DIRECT_QUEUE ne sont pas compatibles avec les files de calcul et ne peuvent pas être utilisées dans les barrières de listes de commandes de calcul. Cependant, elles sont compatibles avec les opérations de calcul dans les files directes.
Accès sans barrière
Puisqu’il ne doit y avoir aucune commande en attente ou opération de vidage de cache entre les limites de ExecuteCommandLists, les tampons peuvent être initialement accédés dans un périmètre ExecuteCommandLists sans barrière. De même, les sous-ressources de texture peuvent également être initialement accédées sans barrière dans les conditions suivantes :
- La mise en page de la sous-ressource est compatible avec le type d’accès.
- Les métadonnées de compression nécessaires sont initialisées.
Les sous-ressources de texture dans la mise en page D3D12DDI_BARRIER_LAYOUT_COMMON (ou une mise en page commune spécifique à la file, comme D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_COMMON) qui n’ont pas d’opérations de lecture ou d’écriture potentiellement en cours peuvent être accédées dans un flux de commandes ExecuteCommandLists sans barrière en utilisant l’un des types d’accès suivants :
- D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE
- D3D12DDI_BARRIER_ACCESS_COPY_SOURCE
- D3D12DDI_BARRIER_ACCESS_COPY_DEST
De plus, un tampon ou une texture utilisant une mise en page commune spécifique à la file peut utiliser D3D12DDI_BARRIER_ACCESS_UNORDERED_ACCESS sans barrière.
Les tampons et les textures à accès simultané (textures créées avec l’indicateur D3D12DDI_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS) peuvent être initialement accédés dans un flux de commandes ExecuteCommandLists sans barrière en utilisant l’un des types d’accès suivants :
- D3D12DDI_BARRIER_ACCESS_VERTEX_BUFFER
- D3D12DDI_BARRIER_ACCESS_CONSTANT_BUFFER
- D3D12DDI_BARRIER_ACCESS_INDEX_BUFFER
- D3D12DDI_BARRIER_ACCESS_RENDER_TARGET
- D3D12DDI_BARRIER_ACCESS_UNORDERED_ACCESS
- D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE
- D3D12DDI_BARRIER_ACCESS_STREAM_OUTPUT
- D3D12DDI_BARRIER_ACCESS_INDIRECT_ARGUMENT
- D3D12DDI_BARRIER_ACCESS_COPY_DEST
- D3D12DDI_BARRIER_ACCESS_COPY_SOURCE
- D3D12DDI_BARRIER_ACCESS_RESOLVE_DEST
- D3D12DDI_BARRIER_ACCESS_RESOLVE_SOURCE
- D3D12DDI_BARRIER_ACCESS_PREDICATION
Les accès ultérieurs peuvent également être effectués sans barrière avec un seul type d’accès en écriture au maximum. Cependant, à l’exception de D3D12DDI_BARRIER_ACCESS_RENDER_TARGET, des barrières doivent être utilisées pour vider les écritures séquentielles vers la même ressource.
Copie de la même ressource
Bien que non exclusivement lié aux barrières améliorées, la possibilité d’autoriser les copies d’une région d’une sous-ressource à une autre région non intersectante est une fonctionnalité très demandée. Avec les barrières améliorées, une sous-ressource avec une mise en page commune peut être utilisée à la fois comme source et destination dans le même appel CopyBufferRegion ou CopyTextureRegion. Les copies entre des régions mémoire sources et destinations qui se chevauchent produisent des résultats non définis. La couche de débogage DOIT valider contre de tels résultats. (Le design des barrières de ressources héritées ne permet pas à une sous-ressource d’être à la fois dans l’état D3D12DDI_RESOURCE_STATE_COPY_SOURCE et D3D12DDI_RESOURCE_STATE_COPY_DEST en même temps, et ne peut donc pas se copier elle-même).
Initialisation des métadonnées des ressources placées
Le design des barrières de ressources héritées nécessite Clear, Copy, ou Discard pour initialiser les ressources de texture aliasées nouvellement placées et activées avant d’être utilisées comme ressource de cible de rendu ou de pochoir de profondeur. Cette exigence est due au fait que les ressources de cible de rendu et de pochoir de profondeur utilisent généralement des métadonnées de compression qui doivent être initialisées pour que les données soient valides. Il en va de même pour les textures réservées avec une cartographie de tuiles nouvellement mise à jour.
Les barrières améliorées prennent en charge une option de Discard dans le cadre d’une barrière. Les transitions de mise en page de D3D12DDI_BARRIER_LAYOUT_UNDEFINED vers toute mise en page potentiellement compressée (par exemple, D3D12DDI_BARRIER_LAYOUT_RENDER_TARGET, D3D12DDI_BARRIER_LAYOUT_DEPTH_STENCIL, D3D12DDI_BARRIER_LAYOUT_UNORDERED_ACCESS) DOIVENT initialiser les métadonnées de compression lorsque D3D12DDI_TEXTURE_BARRIER_FLAG_DISCARD est présent dans le membre Flags de D3D12DDI_TEXTURE_BARRIER.
En plus des ressources de cible de rendu et de profondeur/pochoir, il existe des optimisations similaires de compression de texture UAV que le modèle de barrière héritée ne supportait pas.
Ordonnancement des barrières
Les barrières sont mises en file dans l’ordre croissant (ordre des appels API, index du groupe de barrières, index du tableau de barrières). Plusieurs barrières sur la même sous-ressource doivent fonctionner comme si les barrières étaient exécutées dans l’ordre mis en file.
Les barrières mises en file avec des portées SyncAfter correspondantes qui écrivent potentiellement dans la même mémoire doivent terminer toutes les écritures dans l’ordre mis en file. Cette exigence permet d’éviter les courses de données sur les barrières qui supportent l’aliasing des ressources. Par exemple, une barrière qui désactive une ressource doit vider les caches avant qu’une autre barrière n’active une ressource différente sur la même mémoire, en effaçant éventuellement les métadonnées.