- Philosophie de conception
- Objets de l’API XG Memory
- Procédure pas à pas et exemples d’utilisation de XG Memory
- Conclusion
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.- 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.
- 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.
- 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.
- 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.
Objets de l’API XG Memory
L’API XG Memory comporte trois objets d’API : le moteur, la disposition et le mappage.- 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.
- 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.
- 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éé.- CreateLayout(“Rendertargets”, WorstCaseTotalPageCount, &layout)
- CreateMapping(“Shadow Map”, &mapping1)
- Map(mapping1, ShadowMapPageCount, ShadowMapEsramPageCount)
- CreatePlacedResourceX(mapping1.address, ShadowMapDesc, &pShadowMapResource)
- CreateMapping(“Color Target”, &mapping2)
- Map(mapping2, ColorTargetPageCount, ColorTargetEsramPageCount)
- CreatePlacedResourceX(mapping2.address, ColorTargetDesc, &pColorTargetResource)
- RelinquishMapping(mapping1)
- CreateMapping(“Post Effects Target”, &mapping3)
- Map(mapping3, PostEffectsPageCount, PostEffectsEsramPageCount)
- CreatePlacedResourceX(mapping3.address, PostEffectsDesc, &pPostEffectsResource)
- CreateLayout(“Rendertargets”, WorstCaseTotalPageCount, &layout)
- CreateMapping(“Shadow Map”, &mapping1)
- Map(mapping1, ShadowMapPageCount, ShadowMapEsramPageCount)
- CreatePlacedResourceX(mapping1.address, ShadowMapDesc, &pShadowMapResource)
- CreateMapping(“Color Target”, &mapping2)
- Map(mapping2, ColorTargetPageCount, ColorTargetEsramPageCount)
- CreatePlacedResourceX(mapping2.address, ColorTargetDesc, &pColorTargetResource)
- CreateMapping(“Depth Target”, &mapping4)
- Map(mapping4, DepthTargetPageCount, DepthTargetEsramPageCount)
- CreatePlacedResourceX(mapping4.address, DepthTargetDesc, &pDepthTargetResource)
- RelinquishMapping(mapping1)
- CreateMapping(“Post Effects Target”, &mapping3)
- Map(mapping3, PostEffectsPageCount, PostEffectsEsramPageCount)
- CreatePlacedResourceX(mapping3.address, PostEffectsDesc, &pPostEffectsResource)
