Passer au contenu principal
XG Memory est une bibliothèque d’exécution auxiliaire permettant de créer des mappages de mémoire spécialisés pour les titres du Microsoft Game Development Kit (GDK). Elle a été conçue pour succéder à plusieurs méthodes de mappage de l’ESRAM introduites avec l’API Direct3D 11.X de l’ancien système d’exploitation de titre. Pour Direct3D 12.X sur XBOX One, nous recommandons XG Memory pour accéder à la mémoire ESRAM haute vitesse. Cette vue d’ensemble décrit les composants de XG Memory et la façon dont ils fonctionnent avec les API système de mappage de mémoire. XG Memory est fourni sous forme de code source (entièrement inline) dans xgmemory.h et xgmemory.inl du Gaming eXtensions Development Kit (GXDK), ce qui permet de le personnaliser au besoin.

Philosophie de conception

Fondamentalement, XG Memory est une solution pour mapper des pages de mémoire physique (pages système et pages ESRAM) à une plage d’adresses virtuelles. Le système d’exploitation de titre et la pile de pilotes graphiques du Microsoft Game Development Kit (GDK) fournissent des API de bas niveau comme XMemVirtualAlloc et XMemAllocatePhysicalPages pour effectuer ces opérations, mais elles peuvent être difficiles à utiliser efficacement. XG Memory est construit au-dessus des API de bas niveau et a été conçu selon les principes suivants.
  1. Fournir une API de haut niveau et facile à utiliser que tous les titres peuvent adopter pour le développement D3D12.X, et même D3D11.X. En même temps, livrer le code source afin que la complexité sous-jacente puisse être examinée et améliorée au besoin.
  2. Concevoir en tenant compte de la prise en charge des dispositions de mémoire dynamiques. Les titres modernes adoptent de plus en plus la résolution dynamique et d’autres techniques qui nécessitent de fréquents remappages des dispositions de mémoire à l’exécution.
  3. Concevoir en tenant compte de la prise en charge d’un remappage facile de la mémoire au sein d’une même image. Une utilisation efficace de l’ESRAM exige des stratégies où différentes ressources peuvent partager les mêmes pages de mémoire si elles ne sont pas utilisées en même temps.
  4. Veiller à ce que le crénelage de mémoire (memory aliasing) et le crénelage de données (data aliasing) entre les ressources soient mieux délimités pour faciliter l’outillage. Le crénelage de mémoire se produit lorsque deux ressources partagent la même mémoire, mais que les données de cette mémoire ne sont jamais consultées simultanément par les deux ressources à des fins de réinterprétation. Le crénelage de données se produit lorsque deux ressources partagent la même mémoire, mais qu’il y a une intention de réinterpréter les données qu’elle contient de deux façons différentes.
Les anciennes API ESRAM de Direct3D 11.X traitaient toujours l’ESRAM comme un bloc de mémoire contigu, ce qui limitait grandement la flexibilité du partage de l’ESRAM entre deux ressources ou plus. Les anciennes API augmentaient également la complexité lorsque des ajustements devaient être apportés à la consommation d’ESRAM d’une ressource. Les autres ressources devaient être corrigées pour correspondre aux changements. XG Memory est conçu précisément pour résoudre ces problèmes, car il fonctionne sur des pages, et non sur des plages. De plus, il effectue les calculs à votre place pour allouer correctement les pages là où elles sont nécessaires.

Objets de l’API XG Memory

L’API XG Memory comporte trois objets d’API : le moteur, la disposition et le mappage.
  1. Le moteur (classe XGMemoryLayoutEngine) est un objet qui représente le budget de mémoire que vous attribuez à une ou plusieurs dispositions de mémoire. Il stocke une liste de pages physiques d’ESRAM et/ou de mémoire système qui seront mappées aux dispositions. C’est également là que les objets de disposition sont créés.
  2. La disposition (classe XGMemoryLayout) est un objet qui représente une seule disposition de mémoire, composée d’une plage d’adresses virtuelles réservée et de plusieurs mappages à l’intérieur de celle-ci. Bien que chaque moteur puisse créer autant de dispositions que vous le souhaitez, une seule disposition de chaque moteur doit être utilisée à un moment donné. Les dispositions d’un même moteur partagent les mêmes pages de mémoire physique.
  3. Le mappage (structure XGMemoryLayoutMapping) représente un seul mappage d’un ensemble de pages physiques à une plage d’adresses virtuelles au sein d’une disposition. Une fois un mappage construit, une ou plusieurs ressources graphiques peuvent être créées avec un placement à l’adresse virtuelle du mappage.

Procédure pas à pas et exemples d’utilisation de XG Memory

Préparation à l’utilisation de XG Memory

Avant de commencer à utiliser XG Memory, réfléchissez à votre titre et à la place que XG Memory y occuperait. N’avez-vous qu’une ou deux ressources qui doivent utiliser l’ESRAM? Avez-vous un sous-système entier qui doit réduire son empreinte mémoire grâce au partage? Pouvez-vous configurer toutes les cibles de rendu de votre moteur à un seul endroit? XG Memory devient plus utile à mesure que davantage de ressources sont placées dans la même disposition et que davantage d’occasions de partage de mémoire deviennent évidentes. La conception des dispositions de XG Memory convient également le mieux aux moteurs dont les ressources de cibles de rendu sont configurées en série (sous forme d’une suite séquentielle d’étapes sur le même thread).

Étape 1 : créer un moteur de disposition XG Memory

Pour commencer, créez un moteur qui contient la quantité maximale de mémoire physique que vous souhaitez utiliser pour toutes les ressources qui seront créées à l’aide de XG Memory. Par exemple, vous pourriez vouloir créer un moteur avec 32 Mo (512 pages) d’ESRAM et 64 Mo (1 024 pages) supplémentaires de mémoire système. Lors de la création du moteur, vous pouvez soit transmettre une liste préallouée de numéros de pages physiques provenant de XMemAllocatePhysicalPages, soit demander à XG Memory d’appeler XMemAllocatePhysicalPages en votre nom. Les numéros de pages ESRAM peuvent être générés très simplement à l’aide des indices 0 à 511 pour désigner les 512 pages de 64 Ko qui composent l’ensemble de l’ESRAM.

Étape 2 : Créer une disposition XG Memory

Ensuite, considérez la taille virtuelle totale de toutes les ressources que vous prévoyez configurer, y compris le partage (c’est-à-dire en comptant deux fois les chevauchements). Par exemple, nous pouvons créer une disposition avec 128 Mo d’espace d’adressage virtuel pour couvrir environ 10 cibles 1080p de 32 bpp, plus un peu de marge. Ce nombre peut être surestimé avec très peu de pénalité, car la création de la disposition ne fait que réserver de l’espace d’adressage virtuel. La réservation d’espace d’adressage virtuel est très peu coûteuse, et vous disposez de centaines de Go d’espace d’adressage virtuel. La réservation d’adresses virtuelles est effectuée en appelant XMemVirtualAlloc avec MEM_RESERVE. En plus de réserver de l’espace d’adressage virtuel, la disposition contient des listes libres de pages de mémoire physique obtenues du moteur. À mesure que les mappages sont créés, les pages sont prises dans la liste libre selon le principe du dernier entré, premier sorti (LIFO).

Étape 3 : Créer des mappages et des ressources placées

Pour chaque ressource que vous souhaitez configurer, déterminez la taille de la ressource dans l’espace d’adressage virtuel à l’aide de la bibliothèque XG, puis arrondissez vers le haut et divisez pour obtenir un nombre de pages de 64 Ko. La classe de disposition XG Memory comporte plusieurs méthodes pour créer des mappages, mais elles partagent toutes un concept commun. Pour chaque mappage, vous spécifiez un nombre de pages ESRAM et un nombre de pages de mémoire système à utiliser lors de la validation des pages physiques dans le mappage. Les diverses API de création de mappages ne diffèrent que par la complexité du modèle de mappage à générer (l’ordre précis des pages ESRAM et système dans le mappage). En interne, XG Memory utilise une combinaison de XMemMapPhysicalPages et de D3DMapEsramPages pour valider respectivement les pages système et ESRAM dans l’espace d’adressage virtuel réservé de la disposition. Une fois un mappage créé, vous pouvez utiliser le membre MappingBaseAddress de la structure XGMemoryLayoutMapping comme entrée d’une API de création de ressource placée, comme ID3D12Device::CreatePlacedResourceX. N’oubliez pas que les adresses virtuelles graphiques sur XBOX One sont uniformes entre le processeur et le GPU; vous pouvez donc convertir une valeur d’adresse UINT64 en void* ou inversement.

Étape 4 : (facultatif) : libérer des mappages pour réutiliser des pages physiques

Si vous souhaitez partager des pages entre deux ressources ou plus, vous pouvez libérer (relinquish) un mappage afin de remettre ses pages physiques dans la liste libre de la disposition. Notez que l’opération de libération n’annule pas la validation de la mémoire du mappage. Elle permet de réattribuer les pages validées dans ce mappage à de nouveaux mappages. Par conséquent, après avoir libéré un mappage, vous ne pouvez plus utiliser la mémoire de ce mappage une fois que la mémoire associée aux mappages subséquents est en cours d’utilisation. La libération peut être considérée comme une barrière entre une utilisation de la mémoire et une autre utilisation de la même mémoire. La libération facilite également l’outillage et la déduction de l’intention, car la mémoire n’est réutilisée qu’au niveau physique, et non au niveau virtuel. Les deux mappages différents ont des adresses virtuelles uniques. Si vous souhaitez créer intentionnellement un crénelage de mémoire (par exemple, superposer une cible de couleur à une mémoire tampon de gabarit pour remplir rapidement le gabarit), vous devez le faire en créant deux ressources placées à l’aide de la même adresse virtuelle du même mappage.

Pseudocode d’un scénario

Considérez la séquence suivante de pseudo-appels d’API, en supposant qu’un moteur de disposition de mémoire a déjà été créé.
  1. CreateLayout(“Rendertargets”, WorstCaseTotalPageCount, &layout)
  2. CreateMapping(“Shadow Map”, &mapping1)
  3. Map(mapping1, ShadowMapPageCount, ShadowMapEsramPageCount)
  4. CreatePlacedResourceX(mapping1.address, ShadowMapDesc, &pShadowMapResource)
  5. CreateMapping(“Color Target”, &mapping2)
  6. Map(mapping2, ColorTargetPageCount, ColorTargetEsramPageCount)
  7. CreatePlacedResourceX(mapping2.address, ColorTargetDesc, &pColorTargetResource)
  8. RelinquishMapping(mapping1)
  9. CreateMapping(“Post Effects Target”, &mapping3)
  10. Map(mapping3, PostEffectsPageCount, PostEffectsEsramPageCount)
  11. CreatePlacedResourceX(mapping3.address, PostEffectsDesc, &pPostEffectsResource)
Cette séquence crée trois cibles de rendu : une carte d’ombres, une cible de couleur et une cible de post-effets. Grâce à la fonctionnalité de libération, la carte d’ombres et la cible de post-effets partagent la même mémoire. Notez que le titre n’effectue ici aucun calcul dépendant. Toutes les allocations sont calculées par la disposition à mesure que chaque mappage est créé dans l’ordre. Supposons maintenant que vous avez effectué un profilage et décidé que la répartition des pages ESRAM entre les ressources doit être mise à jour. La cible de couleur a besoin de plus d’ESRAM. Il suffit de réduire la valeur ShadowMapEsramPageCount et d’augmenter la valeur ColorTargetEsramPageCount. XG Memory s’occupe du reste : les pages ESRAM sont réallouées de façon flexible de la carte d’ombres et de la cible de post-effets vers la cible de couleur. Notez qu’à tout moment pendant la création de la disposition, il est possible d’obtenir le nombre actuel de pages ESRAM libres et de pages système libres. La même technique peut être utilisée si vous devez insérer une nouvelle cible de rendu dans la séquence. Par exemple, nous pouvons ajouter un quatrième mappage (une cible de profondeur) dans la disposition comme suit.
  1. CreateLayout(“Rendertargets”, WorstCaseTotalPageCount, &layout)
  2. CreateMapping(“Shadow Map”, &mapping1)
  3. Map(mapping1, ShadowMapPageCount, ShadowMapEsramPageCount)
  4. CreatePlacedResourceX(mapping1.address, ShadowMapDesc, &pShadowMapResource)
  5. CreateMapping(“Color Target”, &mapping2)
  6. Map(mapping2, ColorTargetPageCount, ColorTargetEsramPageCount)
  7. CreatePlacedResourceX(mapping2.address, ColorTargetDesc, &pColorTargetResource)
  8. CreateMapping(“Depth Target”, &mapping4)
  9. Map(mapping4, DepthTargetPageCount, DepthTargetEsramPageCount)
  10. CreatePlacedResourceX(mapping4.address, DepthTargetDesc, &pDepthTargetResource)
  11. RelinquishMapping(mapping1)
  12. CreateMapping(“Post Effects Target”, &mapping3)
  13. Map(mapping3, PostEffectsPageCount, PostEffectsEsramPageCount)
  14. CreatePlacedResourceX(mapping3.address, PostEffectsDesc, &pPostEffectsResource)
Notez qu’aucune modification n’a dû être apportée au code environnant. Lors de l’ajout d’un nouveau mappage, vous pourriez devoir rééquilibrer le nombre de pages ESRAM entre les autres mappages, mais vous n’avez jamais à vous soucier des plages contiguës, des chevauchements d’adresses virtuelles ou de tout autre calcul dépendant. Ensuite, supposons que vous devez modifier dynamiquement la taille des cibles de rendu pour un changement de résolution dynamique. Cependant, vous avez déjà bien équilibré l’utilisation de l’ESRAM entre toutes les ressources, et vous ne voulez pas avoir à retravailler cet équilibre. Ce n’est pas un problème avec XG Memory : il suffit d’ajuster les variables de nombre de pages (ShadowMapPageCount, ColorTargetPageCount, etc.). XG Memory s’occupe du reste, en conservant vos allocations ESRAM intactes. Il vous suffit de vous assurer que la valeur WorstCaseTotalPageCount est suffisamment grande pour gérer la configuration la plus volumineuse de toutes vos ressources.

Conclusion

XG Memory peut réduire considérablement la complexité de l’utilisation de l’ESRAM sur XBOX One. De plus, il peut s’avérer utile même pour les dispositions de mémoire où la réutilisation des pages est souhaitable, mais qui sont composées à 100 % de pages système. Vos commentaires sur XG Memory sont les bienvenus par l’intermédiaire du Soutien aux développeurs de jeux (Game Developer Support).

Voir aussi

d3d12_x
Dernière modification le 6 octobre 2026