Pular para o conteúdo principal
A memória XG (XG Memory) é uma biblioteca de runtime auxiliar para criar mapeamentos de memória especializados para títulos do Microsoft Game Development Kit (GDK). Ela foi projetada como sucessora de vários métodos diferentes de mapeamento de ESRAM introduzidos com a API Direct3D 11.X do sistema operacional de títulos herdado. Para o Direct3D 12.X no XBOX One, recomendamos a memória XG para acessar a memória ESRAM de alta velocidade. Esta visão geral descreve os componentes da memória XG e como eles funcionam em conjunto com as APIs de mapeamento de memória do sistema. A memória XG é fornecida em forma de código-fonte (totalmente embutido) em xgmemory.h e xgmemory.inl no Gaming eXtensions Development Kit (GXDK), para que possa ser personalizada, se necessário.

Filosofia de design

Fundamentalmente, a memória XG é uma solução para mapear páginas de memória física (tanto páginas do sistema quanto da ESRAM) para um intervalo de endereços virtuais. O sistema operacional de títulos do Microsoft Game Development Kit (GDK) e a pilha de drivers gráficos fornecem APIs de baixo nível, como XMemVirtualAlloc e XMemAllocatePhysicalPages, para executar essas operações, mas elas podem ser difíceis de usar com eficácia. A memória XG é construída sobre as APIs de baixo nível e foi projetada com os seguintes princípios em mente.
  1. Fornecer uma API de alto nível e fácil de usar que todos os títulos possam adotar para o desenvolvimento com D3D12.X e até mesmo D3D11.X. Ao mesmo tempo, distribuir o código-fonte para que a complexidade subjacente possa ser inspecionada e aprimorada, se necessário.
  2. Projetar com suporte a layouts de memória dinâmicos em mente. Os títulos modernos estão adotando cada vez mais a resolução dinâmica e outras técnicas que exigem o remapeamento frequente de layouts de memória em runtime.
  3. Projetar com suporte a remapeamento fácil de memória dentro do mesmo quadro. A utilização eficiente da ESRAM requer o uso de estratégias em que diferentes recursos possam compartilhar as mesmas páginas de memória se não estiverem sendo usados ao mesmo tempo.
  4. Garantir que o aliasing de memória e o aliasing de dados entre recursos sejam mais bem delineados para facilitar o uso de ferramentas. O aliasing de memória ocorre quando dois recursos compartilham a mesma memória, mas os dados dentro da memória nunca são vistos pelos dois recursos simultaneamente para reinterpretação. O aliasing de dados ocorre quando dois recursos compartilham a mesma memória, mas há a intenção de reinterpretar os dados nela de duas maneiras diferentes.
As APIs de ESRAM herdadas do Direct3D 11.X sempre tratavam a ESRAM como um bloco contíguo de memória, o que limitava muito a flexibilidade para compartilhar a ESRAM entre dois ou mais recursos. As APIs herdadas também aumentavam a complexidade quando era necessário fazer ajustes no consumo de ESRAM de qualquer recurso. Outros recursos precisavam ser corrigidos para corresponder às alterações. A memória XG foi projetada especificamente para resolver esses problemas, pois opera em páginas, não em intervalos. Além disso, ela faz os cálculos por você para alocar corretamente as páginas onde são necessárias.

Objetos da API de memória XG

A API de memória XG tem três objetos de API: o mecanismo, o layout e o mapeamento.
  1. O mecanismo (classe XGMemoryLayoutEngine) é um objeto que representa o orçamento de memória que você está atribuindo a um ou mais layouts de memória. Ele armazena uma lista de páginas físicas de ESRAM e/ou de memória do sistema que serão mapeadas para layouts. É também onde os objetos de layout são criados.
  2. O layout (classe XGMemoryLayout) é um objeto que representa um único layout de memória, composto por um intervalo de endereços virtuais reservado e vários mapeamentos dentro dele. Embora cada mecanismo possa criar quantos layouts você quiser, apenas um layout de cada mecanismo deve estar em uso a qualquer momento. Os layouts do mesmo mecanismo compartilham as mesmas páginas de memória física.
  3. O mapeamento (struct XGMemoryLayoutMapping) representa um único mapeamento de um conjunto de páginas físicas para um intervalo de endereços virtuais dentro de um layout. Depois que um mapeamento é construído, um ou mais recursos gráficos podem ser criados com posicionamento no endereço virtual do mapeamento.

Passo a passo e exemplos de uso da memória XG

Preparação para usar a memória XG

Antes de começar a usar a memória XG, pense no seu título e em onde a memória XG se encaixaria. Você tem apenas um ou dois recursos que precisam usar a ESRAM? Você tem um subsistema inteiro que precisa reduzir seu consumo de memória por meio de compartilhamento? Você consegue configurar todos os destinos de renderização do seu mecanismo em um só lugar? A memória XG se torna mais útil à medida que mais recursos são colocados no mesmo layout e à medida que mais oportunidades de compartilhamento de memória se tornam evidentes. O design de layout da memória XG também é mais adequado para mecanismos em que os recursos de destino de renderização são configurados serialmente (como uma série sequencial de etapas no mesmo thread).

Etapa 1: criar um mecanismo de layout da memória XG

Para começar, crie um mecanismo que contenha a quantidade máxima de memória física que você gostaria de usar para todos os recursos que serão criados usando a memória XG. Por exemplo, talvez você queira criar um mecanismo com 32 MB (512 páginas) de ESRAM e outros 64 MB (1024 páginas) de memória do sistema. Ao criar o mecanismo, você pode passar uma lista pré-alocada de números de páginas físicas de XMemAllocatePhysicalPages ou instruir a memória XG a chamar XMemAllocatePhysicalPages em seu nome. Os números de páginas da ESRAM podem ser gerados trivialmente usando os índices 0-511 para se referir às 512 páginas de 64 KB que compõem toda a ESRAM.

Etapa 2: criar um layout da memória XG

Em seguida, considere o tamanho virtual total de todos os recursos que você planeja configurar, incluindo o compartilhamento (ou seja, contando duas vezes as sobreposições). Por exemplo, podemos criar um layout com 128 MB de espaço de endereços virtuais para abranger aproximadamente 10 destinos de 1080p e 32 bpp, mais um pouco extra. Esse número pode ser superestimado com pouquíssima penalidade, pois a criação do layout simplesmente reserva espaço de endereços virtuais. A reserva de espaço de endereços virtuais é muito barata, e você tem centenas de GB de espaço de endereços virtuais disponíveis. A reserva de endereços virtuais é feita chamando XMemVirtualAlloc com MEM_RESERVE. Além de reservar espaço de endereços virtuais, o layout contém listas livres de páginas de memória física obtidas do mecanismo. À medida que os mapeamentos são criados, as páginas são retiradas da lista livre no modo último a entrar, primeiro a sair (LIFO).

Etapa 3: criar mapeamentos e recursos de posicionamento

Para cada recurso que você deseja configurar, determine o tamanho do recurso no espaço de endereços virtuais usando a biblioteca XG e, em seguida, arredonde para cima e divida para obter uma contagem de páginas de 64 KB. A classe de layout da memória XG tem vários métodos para criar mapeamentos, mas todos compartilham um conceito comum. Em cada mapeamento, você especifica um número de páginas de ESRAM e um número de páginas de memória do sistema a serem usadas ao confirmar páginas físicas para o mapeamento. As várias APIs de criação de mapeamento diferem apenas na complexidade do padrão de mapeamento a ser gerado (a ordenação específica das páginas de ESRAM e do sistema ao longo do mapeamento). Internamente, a memória XG usa uma combinação de XMemMapPhysicalPages e D3DMapEsramPages para confirmar páginas do sistema e da ESRAM, respectivamente, no espaço de endereços virtuais reservado do layout. Depois que um mapeamento é criado, você pode usar o membro MappingBaseAddress da struct XGMemoryLayoutMapping como entrada para uma API de criação de recursos de posicionamento, como ID3D12Device::CreatePlacedResourceX. Lembre-se de que os endereços virtuais gráficos no XBOX One são uniformes entre a CPU e a GPU, portanto, você pode converter um valor de endereço UINT64 em void* ou vice-versa.

Etapa 4 (opcional): liberar mapeamentos para reutilizar páginas físicas

Se você quiser compartilhar páginas entre dois ou mais recursos, poderá liberar (relinquish) um mapeamento para devolver suas páginas físicas à lista livre dentro do layout. Observe que a operação de liberação não cancela a confirmação da memória do mapeamento. Ela permite que as páginas confirmadas para esse mapeamento sejam reatribuídas a novos mapeamentos. Como resultado, depois de liberar um mapeamento, você não poderá usar a memória desse mapeamento depois que a memória associada aos mapeamentos subsequentes estiver em uso. A liberação pode ser vista como uma barreira entre um uso da memória e outro uso da mesma memória. A liberação também auxilia as ferramentas e a dedução da intenção, pois a memória só é reutilizada no nível físico, e não no nível virtual. Os dois mapeamentos diferentes têm endereços virtuais exclusivos. Se você quiser criar intencionalmente um aliasing de memória (por exemplo, sobrepor um destino de cor a um buffer de estêncil para preencher rapidamente o estêncil), deverá fazer isso criando dois recursos de posicionamento usando o mesmo endereço virtual do mesmo mapeamento.

Pseudocódigo do cenário

Considere a seguinte sequência de pseudochamadas de API, supondo que um mecanismo de layout de memória já tenha sido criado.
  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)
Essa sequência cria três destinos de renderização: um mapa de sombras, um destino de cor e um destino de pós-efeitos. Usando o recurso de liberação, o mapa de sombras e o destino de pós-efeitos compartilham a mesma memória. Observe que não há cálculos dependentes feitos pelo título aqui. Todas as alocações são calculadas pelo layout à medida que cada mapeamento é criado em ordem. Agora, digamos que você tenha feito alguma criação de perfil e decidido que o equilíbrio de páginas de ESRAM entre os recursos precisa ser atualizado. O destino de cor precisa de mais ESRAM. Basta reduzir o valor de ShadowMapEsramPageCount e aumentar o valor de ColorTargetEsramPageCount. A memória XG cuida do resto: as páginas de ESRAM são realocadas de forma flexível do mapa de sombras e do destino de pós-efeitos para o destino de cor. Observe que, a qualquer momento durante a criação do layout, as contagens atuais de páginas de ESRAM livres e de páginas do sistema livres podem ser obtidas. A mesma técnica pode ser usada se você precisar inserir um novo destino de renderização na sequência. Por exemplo, podemos adicionar um quarto mapeamento (um destino de profundidade) ao layout da seguinte maneira.
  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)
Observe que nenhuma alteração precisou ser feita no código ao redor. Ao adicionar um novo mapeamento, talvez seja necessário reequilibrar as contagens de páginas de ESRAM entre os outros mapeamentos, mas você nunca precisa se preocupar com intervalos contíguos, sobreposições de endereços virtuais ou quaisquer outros cálculos dependentes. Em seguida, digamos que você precise alterar dinamicamente o tamanho dos destinos de renderização para uma mudança de resolução dinâmica. No entanto, você já equilibrou bem o uso da ESRAM entre todos os recursos e não quer ter de refazer esse equilíbrio. Isso não é um problema com a memória XG: basta ajustar as variáveis de contagem de páginas (ShadowMapPageCount, ColorTargetPageCount e assim por diante). A memória XG cuidará do resto, mantendo suas alocações de ESRAM intactas. Tudo o que você precisa fazer é garantir que o valor de WorstCaseTotalPageCount seja grande o suficiente para lidar com a maior configuração de todos os seus recursos.

Conclusão

A memória XG pode reduzir muito a complexidade de usar a ESRAM no XBOX One. Além disso, ela pode ser útil até mesmo com layouts de memória em que a reutilização de páginas é desejável e que, ainda assim, são 100% compostos de páginas do sistema. Comentários sobre a memória XG são bem-vindos por meio do Suporte ao Desenvolvedor de Jogos.

Confira também

d3d12_x
Última modificação em 6 de outubro de 2026