Table des matières
- Introduction
- Synchronisation GPU sur XBOX Series X|S
- Les barrières en détail
- Mise en place
- Différences avec la spécification PC
- Exemples d’Enhanced Barriers
- Compatibilité avec les Resource Barriers héritées
- Prise en charge et validation dans PIX
Introduction
Les Enhanced Barriers (EB) sont un nouvel ensemble d’API D3D12 conçues pour résoudre certains des problèmes introduits par l’API Resource Barrier héritée (concepts déroutants comme la promotion et la dégradation des états de ressources, problèmes de conception des Aliasing Barriers, opérations de vidage excessives, etc.), tout en donnant aux développeurs un contrôle indépendant sur la synchronisation du travail du GPU. Elles offrent un type de barrière plus expressif que ce qui était possible avec les Resource Barriers héritées. Les fonctionnalités des Enhanced Barriers comprennent :
- Réduction de la latence de synchronisation et des vidages de cache excessifs.
- Aucune règle mystérieuse de promotion et de dégradation.
- Aliasing de ressources rapide et flexible.
- Abandon (discard) pendant la transition de barrière.
- Lecture/écriture simultanées et copie sur soi-même.
Synchronisation GPU sur XBOX Series X|S
Avant d’aborder les Enhanced Barriers, une introduction à la synchronisation sur XBOX Series X|S est nécessaire. Il est important de comprendre les concepts et mécanismes expliqués dans cette section, car ils constituent les éléments de base des barrières. Synchronisation GPU sur XBOX Series X/S Nous pouvons maintenant nous plonger dans les Enhanced Barriers et expliquer leur fonctionnement plus en détail.
Les barrières en détail
Les Enhanced Barriers introduisent trois concepts utiles pour décider comment effectuer la transition d’une ressource (ou d’une mémoire) donnée. Il s’agit de Sync, Access et Layout. D’une certaine manière, ils remplacent le concept précédent de Resource State utilisé dans les Resource Barriers. Ils ne sont toutefois pas équivalents, car il est toujours possible d’exprimer un état de ressource comme une combinaison de Sync, Access et Layout, mais il n’est pas toujours possible d’exprimer une combinaison de Sync, Access et Layout comme un état de ressource. La nouvelle API est plus expressive. Sync (Before-After) sert à indiquer au pilote quelle étape du pipeline nous devons attendre jusqu’à son achèvement, et quel moteur doit être empêché de s’exécuter jusqu’à ce que nous en ayons terminé avec les opérations de barrière.
- Le tableau suivant montre comment certains indicateurs de synchronisation se traduisent en exigences de drainage ou de blocage des étapes (reportez-vous à la section sur le pipelining). Il convient de noter que certains indicateurs de synchronisation de barrière font référence à une étape générale (comme la copie), mais qu’en interne, et en fonction des données fournies par l’accès et la disposition, ils sont mappés à des étapes spécifiques (source de copie/destination de copie), ce qui génère des exigences de drainage/blocage plus adaptées. Par ailleurs, le tableau ci-dessous examine le cas des textures (pour les tampons, certaines étapes seraient différentes).
- Ces exigences sont ensuite analysées et transmises, avec d’autres primitives, aux fonctions SyncBottomOfPipe/SyncTopOfPipe afin de déterminer les actions de synchronisation finales (par exemple D3D12XBOX_SYNC_FLAG_PS_PARTIAL_FLUSH ou D3D12XBOX_SYNC_FLAG_IDLE). Ces indicateurs ne doivent pas être confondus avec les indicateurs D3D12_BARRIER_SYNC, qui sont utilisés pour les appels Enhanced Barrier.
- Ces exigences ne connaissent pas les mécanismes finalement utilisés pour les satisfaire ; elles indiquent donc explicitement quel élément matériel doit être drainé/bloqué. Des composants ultérieurs peuvent par exemple se rendre compte que plusieurs drainages peuvent être réalisés par un seul événement EoP.
- Dans le contexte des barrières, il est utile de savoir quelles ressources correspondent à quels caches. Par exemple, si nous avons un AccessBefore de D3D12_ACCESS_RENDER_TARGET et un accès après de D3D12_ACCESS_SHADER_RESOURCE, nous voulons savoir quels caches invalider et/ou vider (si nécessaire). Le tableau suivant présente certains types de ressources fréquemment utilisés et la mémoire qu’ils utilisent.
- Les types de ressources ne sont pas le seul facteur déterminant les caches utilisés par la ressource. Le type d’opération (scalaire ou vectorielle) compte également, et c’est le compilateur qui en décide. Une lecture depuis un tampon de constantes, par exemple, peut passer par le chemin $K-L2 s’il s’agit d’une opération scalaire (SMEM), ou par L0-L1-L2 s’il s’agit d’une opération vectorielle (VMEM).
Layout (before-after) décrit la disposition mémoire de la ressource. Elle détermine si les ressources devront être décompressées/compressées lors de l’opération de barrière, ainsi que si une initialisation des métadonnées est nécessaire (pour les rendertargets et les depthstencils). Par exemple, une rendertarget avec DCC activé peut devoir effectuer un DCC-Eliminate lors de sa transition vers SRV, et donc subir une décompression.
- Certains indicateurs de ressource et de barrière modifient les exigences de compression/décompression qui en résultent :
- Le pilote conserve deux tables qui mappent le type de ressource aux opérations de compression/décompression. L’une de ces tables concerne les ressources créées avec D3D12XBOX_RESOURCE_FLAG_FORCE_TEXTURE_COMPATIBILITY, tandis que l’autre concerne les ressources non TC.
- Les EB introduisent D3D12_TEXTURE_BARRIER_FLAG_DISCARD qui, lorsqu’il est passé en argument à la méthode Barrier, force l’initialisation des métadonnées. Avant les EB, cela pouvait être fait avec une opération d’effacement effectuée avant la première utilisation de la ressource.
- Les rendertargets et les depthstencils doivent être créées respectivement avec D3D12_RESOURCE_FLAGS_ALLOW_RENDER_TARGET et D3D12_RESOURCE_FLAGS_ALLOW_DEPTH_STENCIL pour que les surfaces de métadonnées soient correctement allouées.
- Si vous souhaitez utiliser DCC, la ressource doit être créée avec l’indicateur D3D12XBOX_RESOURCE_FLAG_ALLOW_DCC. DCC implique des opérations de décompression lors de la transition d’un état d’écriture vers un état de lecture, mais pas si la ressource est également créée avec D3D12XBOX_RESOURCE_FLAG_FORCE_TEXTURE_COMPATIBILITY.
- Le tableau suivant, qui associe les indicateurs de disposition aux indicateurs d’état de compression, aide à mieux comprendre comment les transitions entrantes et sortantes peuvent entraîner des compressions/décompressions : par exemple, passer de rendertarget (rt) à shader resource (sr) sur des ressources non TC nécessite une décompression, car rt possède plusieurs indicateurs de compression tandis que sr est marqué comme uncompressedAll. En revanche, la même transition pour une ressource TC ne déclencherait pas de décompression, étant donné que sr possède un sur-ensemble des indicateurs d’état de compression de rt.
Définir un avant et un après pour layout, access et sync nous offre une grande flexibilité pour faire des choses que nous ne pouvions pas faire avec les ResourceBarriers (à l’exception des barrières fractionnées, nous y reviendrons), tout en améliorant les cas où les Resource Barriers pouvaient entraîner des vidages partiels inutiles (barrières UAV) et, dans le pire des cas, provoquer une corruption de la mémoire (barrières d’aliasing, lors d’une tentative de décompression sans connaître avec certitude la disposition actuelle de la mémoire). Cela correspond également mieux au fonctionnement de la synchronisation sur le GPU, offrant ainsi au développeur un contrôle plus précis.
La spécification définit également trois types de barrières :
- Barrières globales :
- S’appliquent à toute la mémoire. La définition ne prend pas de pointeur de ressource.
- Aucune information de disposition n’est passée en argument.
- Barrières de tampon
- Comme leur nom l’indique, les barrières de tampon sont utilisées pour effectuer la transition des ressources de type tampon.
- Aucune disposition ni sous-ressource n’est spécifiée en paramètre (puisque les tampons n’utilisent ni l’une ni l’autre).
- Barrières de texture
- Transition des ressources de texture (rendertargets, depthstencils, textures SRV, textures UAV, etc.).
- La disposition est obligatoire, de sorte que cette barrière peut déclencher des décompressions et des compressions.
- Permettent de spécifier une sous-ressource précise ou une plage.
- Pour les sous-ressources, il existe des limitations en matière de décompression, car il n’est pas possible de décompresser seulement une partie d’une ressource.
- LayoutBefore, LayourAfter disposition mémoire avant et après la barrière. Détermine ce que nous allons compresser/décompresser. Utilisé uniquement par D3D12_TEXTURE_BARRIER.
- SyncBefore, SyncAfter étape dont il faut attendre le drainage et étape à bloquer avant de continuer.
- AccessBefore, AccessAfter indique quels caches nous cessons d’utiliser et quels caches nous allons commencer à utiliser.
- Subresource range — Permet de définir une plage de sous-ressources dont la barrière effectuera la transition. Utilisé uniquement par D3D12_TEXTURE_BARRIER.
- Resource — Le propriétaire de la mémoire dont nous effectuons la transition. Utilisé par D3D12_TEXTURE_BARRIER et D3D12_BUFFER_BARRIER.
- Texture Barrier flags — Les valeurs possibles sont D3D12_TEXTURE_BARRIER_FLAG_NONE et D3D12_TEXTURE_BARRIER_FLAG_DISCARD. Utilisé uniquement par D3D12_TEXTURE_BARRIER.
- Nous utilisons l’indicateur discard lors de l’activation d’une ressource, afin que ses métadonnées soient correctement initialisées.
- L’utilisation de cet indicateur permet également de regrouper l’initialisation des ressources. Les effacements peuvent se chevaucher et les opérations de synchronisation de barrière sont regroupées.
Mise en place
Pour utiliser les Enhanced Barriers, vous avez besoin des éléments suivants :
- GDK de mars 2023 ou version ultérieure.
- ID3D12GraphicsCommandList7 : nécessaire pour appeler la méthode Barrier. Prend une structure D3D12_BARRIER_GROUP et le numéro de groupe.
- Conformément à la spécification, l’interopérabilité entre les API Enhanced Barrier et les D3D12_RESOURCE_STATES hérités est prise en charge. Toutefois, les sous-ressources se trouvant dans un état hérité autre que D3D12_RESOURCE_STATE_COMMON doivent faire l’objet d’une transition vers D3D12_RESOURCE_STATE_COMMON avant d’être référencées à l’aide d’une Enhanced Barrier.
- Créez un fichier vide dans le dossier Loose (répertoire de déploiement) et nommez-le D3D__DisableLegacyResourceBarrierTranslation.
- Déployez le jeu et assurez-vous que le fichier a bien été déployé sur la console.
- Remarque : dans ce mode, le pilote ne prend pas en charge le mélange de barrières héritées et d’Enhanced Barriers.
Différences avec la spécification PC
- Sur XBOX, le vidage à la limite d’ExecuteCommandList n’est pas émis ; l’accès sans barrière n’est donc pas pris en charge.
- Cela s’explique par des raisons de performances, car un vidage complet du GPU est coûteux.
- Accès sans barrière dans la spécification
- Sous-ressources. Le PC suit les métadonnées par sous-ressource, ce qui n’est pas notre cas. Cela signifie que l’état de compression par sous-ressource ne peut pas être suivi. Par conséquent, les changements de disposition impliquant une compression ou une décompression auront des résultats différents selon que la plage de ressources couvre (ou non) la ressource entière.
- Si une ressource dans une disposition compressée a une ou plusieurs sous-ressources (mais pas toutes) qui effectuent une transition vers une disposition non compressée, la ressource entière sera décompressée.
- Si une ressource dans une disposition non compressée a une ou plusieurs sous-ressources (mais pas toutes) qui effectuent une transition vers une disposition compressée, la ressource entière reste non compressée.
- Nous prenons en charge des états de compression indépendants pour la profondeur et le stencil, ce qui signifie que nous pouvons avoir une profondeur compressée et un stencil non compressé, ou inversement.
- Les barrières fractionnées fonctionnent, mais elles ne sont pas implémentées comme des barrières fractionnées dans le pilote. Elles sont plutôt converties en barrières immédiates. Nous effectuons cette conversion parce que les barrières fractionnées, telles que définies par D3D12, imposent un coût mémoire par ressource et sont inutilement inefficaces sur notre matériel. Toutefois, la conversion en barrières immédiates n’est pas aussi efficace qu’elle pourrait l’être.
- Cela est dû au fait que le pilote ne reçoit pas d’informations complètes concernant la synchronisation avant et après. La conversion en barrière immédiate a lieu du côté proche de la barrière (le côté éloigné est ignoré) ; le pilote doit donc être pessimiste et bloquer à la fois le CE et le PFP, ce qui peut rendre les barrières fractionnées plus lentes que les barrières nativement immédiates.
- Par ailleurs, historiquement, très peu de personnes utilisent les barrières fractionnées avec l’API héritée.
- Une façon de résoudre le coût mémoire par ressource à l’avenir pourrait être de permettre aux développeurs de fournir eux-mêmes cette mémoire.
- Il est prévu de revoir ultérieurement l’implémentation des barrières fractionnées.
- Il est recommandé aux utilisateurs d’implémenter leurs propres copies de textures dans des nuanceurs de calcul. Ils devront bien sûr gérer les dépendances, mais cela permettrait un travail non sérialisé et plus efficace.
- L’une des raisons pour lesquelles les copies de textures gérées par le pilote peuvent être moins performantes est qu’elles sont susceptibles d’utiliser les caches CB/DB. Cela impose d’effectuer des WB et des invalidations supplémentaires des caches CB et DB lors des transitions entrantes ou sortantes des copies de textures.
Exemples d’Enhanced Barriers
Bien que des améliorations automatiques des performances d’exécution puissent être obtenues grâce à la capacité du pilote à traduire les appels Resource Barrier hérités en équivalents Enhanced Barrier, des gains supplémentaires peuvent être obtenus en utilisant directement les Enhanced Barriers. L’expressivité de l’API offre des possibilités de réduction des latences de synchronisation et d’élimination des vidages inutiles. Les exemples de code suivants montrent comment exprimer des appels Resource Barrier hérités avec l’API Enhanced Barriers afin de tirer parti de ces avantages supplémentaires.
- Barrières de ressources de transition
- Barrières de ressources à accès non ordonné
- Copie sur soi-même
- Barrières de ressources d’aliasing
Barrières de ressources de transition
Dans les applications graphiques, les ressources mémoire sont souvent accédées dans différents contextes, que ce soit en tant que cibles de rendu, vues de ressources de nuanceur, vues de tampons de sommets ou dans d’autres types d’états. Cela crée un besoin fréquent de barrières de ressources de transition pour garantir une synchronisation et une cohérence des caches correctes. Toutefois, dans certains cas, les barrières de ressources de transition peuvent introduire des ralentissements inutiles, comme un vidage excessif du pipeline. Considérez l’extrait de code de barrière de ressource héritée suivant, qui montre l’état d’une ressource passant d’une vue de ressource de nuanceur (SRV) à une vue de cible de rendu (RTV) :


Barrières de ressources à accès non ordonné
Les Resource Barriers héritées créées dans le but de synchroniser les lectures et les écritures dans une vue d’accès non ordonné (UAV) sont également sujettes à des vidages superflus du pipeline GPU, pour des raisons liées au pilote similaires à celles des barrières de ressources de transition héritées. Considérez un scénario dans lequel des textures mipmappées sont générées à partir d’un nuanceur de calcul, toutes écrites et lues dans une UAV de texture, et où le code de barrière de ressource à accès non ordonné suivant est utilisé pour éviter les risques :

Copie sur soi-même
Auparavant, une sous-ressource ne pouvait pas être écrite et lue simultanément, car la conception héritée des Resource Barriers interdit de combiner les bits de lecture seule et d’écriture. Cela signifiait que le déplacement de données au sein d’une ressource entre deux régions qui ne se chevauchent pas nécessitait la création d’une texture temporaire, davantage de barrières de transition pour passer de D3D12_RESOURCE_STATE_COPY_SOURCE à D3D12_RESOURCE_STATE_COPY_DEST, et davantage d’opérations de copie de sous-ressources. C’est ce qu’illustre l’exemple de code ci-dessous, qui présente le travail nécessaire pour copier le quart supérieur gauche d’une texture vers son quart inférieur droit.

Barrières de ressources d’aliasing
Comme le montre le code ci-dessous, tiré de la documentation MSDN publique de D3D12, l’aliasing de ressources placées dans l’API Resource Barrier héritée consistait historiquement à spécifier uniquement les deux ressources qui partageaient la mémoire du tas. L’API Enhanced Barrier peut aider à éviter de devoir bloquer tout le travail du GPU et vider toutes les écritures, tout en fournissant des états de disposition explicites pour garantir que le pilote ne décompresse pas de la mémoire non compressée (consultez ce lien pour plus d’informations sur ce dernier problème, sous « Aliasing Barriers Are Very Expensive »).Compatibilité avec les Resource Barriers héritées
L’API de barrières précédente (Resource Barriers) est toujours prise en charge, mais le pilote les traduit en Enhanced Barriers en arrière-plan. Plusieurs tests ont été effectués sur des captures PIX pour mesurer l’impact de la traduction (par rapport à l’implémentation précédente). Les résultats montrent que la traduction en EB apporte des gains significatifs.

Il convient de noter que les gains dépendront fortement de la façon dont le moteur graphique est construit en termes de synchronisation et de placement des barrières.
Exemples où la traduction a apporté des améliorations par rapport aux Resource Barriers héritées :
Exemple 1 - Génération de vidages partiels pour les barrières UAV
Resource Barrier héritée

Exemple 2 - Aliasing
Resource Barrier héritée

Tout ceci montre les gains que les développeurs peuvent obtenir simplement grâce aux Enhanced Barriers, sans modifier leur code. Pour connaître les gains potentiels lors de la migration vers la nouvelle API Barrier, consultez la section Exemples d’Enhanced Barriers.
Prise en charge et validation dans PIX
PIX prend en charge à la fois les Enhanced Barriers et les Resource Barriers héritées traduites. Pour les Enhanced Barriers, un événement PIX nommé Barrier est créé ; il contient les paquets CP générés.





Validation
La validation est disponible à la fois pour les Enhanced Barriers et pour les Resource Barriers héritées passant par la couche de traduction. La validation dont vous bénéficiez dépend de l’API que vous utilisez :- Pour les Enhanced Barriers, la parité de validation s’entend en termes de scénarios couverts. Les Enhanced Barriers sont différentes des Resource Barriers ; la validation ne reproduira donc pas à l’identique ce qu’elle faisait pour l’API héritée, mais garantira que toutes les propriétés avant et après sont correctes pour la transition. Par exemple :
- S’assurer que les ressources sont dans la bonne disposition au début d’une transition.
- Valider l’exactitude de l’utilisation de l’API (voir plus bas).
- Pour les Resource Barriers héritées (traduites), la validation offre la parité avec la validation actuelle des Resource Barriers.
- L’appareil sous-jacent prend en charge les Enhanced Barriers
- La liste de commandes n’est pas un bundle
- Avertissement si un nombre (de groupes ou de barrières par groupe) est égal à zéro
- Vérification que les sous-ressources de la barrière de texture correspondent aux limites des sous-ressources de la texture
- Le type de barrière correspond au type de ressource
- LayoutBefore et AccessBefore correspondent à la disposition et à l’accès connus ou supposés de la ressource.
- SyncBefore, AccessBefore et LayoutBefore sont compatibles.
- SyncAfter, AccessAfter et LayoutAfter sont compatibles.
- Les barrières End-split correspondent aux barrières Begin-split précédentes.
- Les barrières fractionnées de tampon et D3D12_BARRIER_LAYOUT_COMMON ne franchissent pas les limites d’ExecuteCommandLists.
- Probablement un avertissement plutôt qu’une erreur, en raison des tolérances héritées
