> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Implémentation d'un gestionnaire de mémoire ESRAM

> Implémentation d'un gestionnaire de mémoire ESRAM

<Note>
  Cette rubrique est propre à la XBOX One.
</Note>

L'ESRAM (Enhanced Static Random Access Memory) est une mémoire RAM dédiée de 32 Mo à laquelle seul le GPU peut accéder directement. Cette mémoire offre une bande passante extrêmement élevée, ce qui permet au GPU de lire et d'écrire des données rapidement. L'ESRAM peut être utilisée pour presque n'importe quelle ressource Direct3D, comme les cartes d'ombres, les mémoires tampons et les textures. Pour tirer le meilleur gain de performances de l'ESRAM, utilisez-la pour stocker les données auxquelles vous accédez le plus fréquemment.

Les sections suivantes expliquent comment gérer efficacement l'ESRAM sur un kit de développement XBOX One. La section du didacticiel décrit des types de données et des fonctionnalités utiles pour un gestionnaire de mémoire ESRAM.

* [Types de données pouvant résider dans l'ESRAM](#ID4E5)
* [Gestionnaire ESRAM](#ID4ESB)

<a id="ID4E5" />

## Types de données pouvant résider dans l'ESRAM

* L'ESRAM de la XBOX One est une mémoire dédiée à haute bande passante (duplex) de 32 Mo.
* L'ESRAM est destinée à stocker les cibles de rendu et de profondeur, les cartes d'ombres, les lightmaps et d'autres ressources à haute bande passante.
* Des paramètres de création de ressources permettent de spécifier la résidence en ESRAM.
* L'ESRAM permet de stocker des mémoires tampons et des textures 1D, 2D et 3D.
* L'ESRAM prend en charge uniquement les usages par défaut et immuables. Le CPU n'a pas accès à l'ESRAM.
* Les chaînes de permutation ne peuvent pas être stockées dans l'ESRAM.

<a id="ID4ESB" />

## Gestionnaire ESRAM

Le choix des données à stocker dans l'ESRAM est important. Vous devez stocker dans l'ESRAM les données auxquelles vous accédez le plus fréquemment, et ces données changeront probablement au fil de la progression de votre jeu, par exemple lors des transitions entre niveaux. Il est donc conseillé d'implémenter un gestionnaire de mémoire ESRAM. Il n'existe pas de gestionnaire ESRAM prédéfini ; vous devrez créer votre propre gestionnaire ESRAM pour votre projet et l'adapter à vos besoins spécifiques.

Un gestionnaire ESRAM fonctionne de manière similaire à un gestionnaire de mémoire traditionnel. Le gestionnaire ESRAM doit au minimum assurer les fonctions suivantes : allouer de la mémoire, charger et décharger des données depuis la DRAM, et libérer la mémoire qui n'est plus nécessaire. Vous trouverez ci-dessous des exemples de types de données et de fonctions courants que votre gestionnaire ESRAM est susceptible d'utiliser.

* [Classe ESRAMAllocation : une allocation dans l'ESRAM](#ID4E3B)
* [Classe ESRAMResource : une ressource allouée dans l'ESRAM](#ID4ELC)
* [Classe ESRAMTexture : une texture allouée dans l'ESRAM](#ID4E1C)
* [Classe ESRAMBuffer : une mémoire tampon allouée dans l'ESRAM](#ID4EJD)
* [Classe FreeSpace : une zone d'espace libre dans l'ESRAM](#ID4EYD)
* [ESRAMManager::Allocate : alloue de la mémoire dans l'ESRAM en recherchant le premier espace adapté](#ID4EHE)
* [ESRAMManager::Free : libère de la mémoire dans l'ESRAM](#ID4EYE)
* [ESRAMManager::InsertGPUWait : indique au GPU d'attendre une opération DMA impliquant la ressource](#ID4ELF)
* [ESRAMManager::Create : crée une mémoire tampon dans l'ESRAM sans contenu initial](#ID4E1F)
* [ESRAMManager::Prefetch : prélit des données à partir d'une mémoire tampon DRAM](#ID4ENG)
* [ESRAMManager::Writeback : copie une mémoire tampon ESRAM dans la DRAM](#ID4E3G)
* [ESRAMManager::Discard : marque une ressource ESRAM comme n'étant plus utilisée, afin que sa mémoire puisse être récupérée](#ID4ENH)
* [ESRAMManager::GarbageCollect : libère la mémoire appartenant aux ressources abandonnées](#ID4ECAAC)

<a id="ID4E3B" />

### Classe ESRAMAllocation : une allocation dans l'ESRAM

**Allocation ESRAM :** cette classe définit un bloc de mémoire contigu qui a été alloué dans l'ESRAM, ainsi qu'un pointeur vers cette mémoire.

**C++**

```cpp theme={null}
// An allocation in ESRAM
class ESRAMAllocation
{
public:
    ESRAMAllocation() : m_esramPtr(INVALID_PTR), m_beginBlock(INVALID_BLOCK), m_endBlock(INVALID_BLOCK) {}

    BOOL        IsValid() const { return m_beginBlock != INVALID_BLOCK; }
    void        Invalidate() { m_esramPtr = INVALID_PTR; m_beginBlock = m_endBlock = INVALID_BLOCK; }

    ESRAMPtr    m_esramPtr;     // The start location of this allocation in ESRAM
    USHORT      m_beginBlock;   // The first 4K block of this allocation
    USHORT      m_endBlock;     // One past the end, like STL
};  
```

<a id="ID4ELC" />

### Classe ESRAMResource : une ressource allouée dans l'ESRAM

**Ressource ESRAM :** cette classe est essentiellement un wrapper de la classe ESRAMAllocation, avec une valeur de barrière (fence) supplémentaire. Les valeurs de barrière sont des identifiants uniques attribués à des opérations GPU ou DMA spécifiques. Utilisez les valeurs de barrière pour vous assurer que les ressources ne sont utilisées qu'au moment approprié.

**C++**

```cpp theme={null}
// A resource allocated in ESRAM
class ESRAMResource
{
public:
    ESRAMResource() : m_fence(INVALID_FENCE) { } 

    // The allocation in ESRAM
    ESRAMAllocation                     m_allocation;

    // A fence value for the last DMA or GPU operation that touched this resource. The 
    // resource should not be touched again until this fence has been passed.
    UINT64                              m_fence;
};  
```

<a id="ID4E1C" />

### Classe ESRAMTexture : une texture allouée dans l'ESRAM

**Texture ESRAM :** une texture allouée dans l'ESRAM. Elle contient la ressource ESRAM, un pointeur de texture et divers pointeurs de vue associés à cette texture.

**C++**

```cpp theme={null}
// A texture allocated in ESRAM
class ESRAMTexture
{
public:
    // The texture resource in ESRAM
    XSF::D3DTexture2DPtr                m_spTexture;

    // Views: null if the texture does not support the given View
    XSF::D3DShaderResourceViewPtr       m_spSRV;
    XSF::D3DUnorderedAccessViewPtr      m_spUAV;
    XSF::D3DRenderTargetViewPtr         m_spRTV;
    XSF::D3DDepthStencilViewPtr         m_spDSV;

    ESRAMResource                       m_esramResource;
};  
```

<a id="ID4EJD" />

### Classe ESRAMBuffer : une mémoire tampon allouée dans l'ESRAM

**Mémoire tampon ESRAM :** un exemple de mémoire tampon allouée dans l'ESRAM. Elle contient la ressource ESRAM et un pointeur vers la mémoire tampon.

**C++**

```cpp theme={null}
// A buffer allocated in ESRAM
class ESRAMBuffer
{
public:
    // The buffer resource in ESRAM
    XSF::D3DBufferPtr                   m_spBuffer;

    ESRAMResource                       m_esramResource;
};  
```

<a id="ID4EYD" />

### Classe FreeSpace : une zone d'espace libre dans l'ESRAM

**Espace libre :** la plupart des gestionnaires de mémoire allouent de grands blocs de mémoire en une seule fois, puis subdivisent ces blocs en morceaux plus petits à attribuer. Cette approche est nettement plus rapide, mais a tendance à produire de petits blocs de mémoire inutilisés. Utilisez la classe FreeSpace pour suivre les blocs de mémoire inutilisés. Il est conseillé, sans être obligatoire, d'allouer toute l'ESRAM sous la forme d'un seul FreeSpace lors de l'initialisation de votre gestionnaire ESRAM.

**C++**

```cpp theme={null}
// An area of free space in ESRAM
class FreeSpace
{
public:
    FreeSpace(USHORT beginBlock, USHORT endBlock) : m_beginBlock(beginBlock), m_endBlock(endBlock) {}

    USHORT m_beginBlock;
    USHORT m_endBlock;   // One past the end, like STL
};  
```

<a id="ID4EHE" />

### ESRAMManager::Allocate : alloue de la mémoire dans l'ESRAM en recherchant le premier espace adapté

La fonction **Allocate** parcourt tous les FreeSpaces jusqu'à trouver un espace suffisamment grand pour contenir les données à allouer. Une fois un espace approprié trouvé, **Allocate** renvoie l'emplacement mémoire et met à jour la liste FreeSpace en conséquence.

**C++**

```cpp theme={null}
//--------------------------------------------------------------------------------------
// Name: Allocate
// Desc: Allocate memory in ESRAM using a simple first-fit freelist allocator
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void ESRAMManager::Allocate(UINT numBytes, UINT alignment, ESRAMAllocation& alloc)
{
    XSFScopedNamedEvent( m_spImmediateContext.Get(), XSF_COLOR_RENDER, L"ESRAMManager Allocate" );

    // Make sure the allocation comes back invalid if we can't fit
    alloc.Invalidate();

    // Try to find contiguous free space big enough to fit the aligned allocation
    for( auto iter = m_freeSpaces.rbegin(); iter != m_freeSpaces.rend(); ++iter )
    {
        FreeSpace& freeSpace = *iter;

        // See if we can fit the aligned allocation inside this free space
        ESRAMPtr beginPtr = freeSpace.m_beginBlock * BLOCK_SIZE;
        ESRAMPtr endPtr = freeSpace.m_endBlock * BLOCK_SIZE;
        ESRAMPtr alignedPtr = Align(beginPtr, alignment);

        if(alignedPtr + numBytes <= endPtr)
        {
            // Our allocation will fit in this free space
            alloc.m_beginBlock = freeSpace.m_beginBlock;
            alloc.m_esramPtr = alignedPtr;

            // Shrink the free space to only the bit left after the allocation
            freeSpace.m_beginBlock = (USHORT) ( Align(alignedPtr + numBytes, BLOCK_SIZE) / BLOCK_SIZE );
            alloc.m_endBlock = freeSpace.m_beginBlock;
            if(freeSpace.m_beginBlock == freeSpace.m_endBlock)
            {
                // We consumed this entire piece of free space
                m_freeSpaces.erase( (++iter).base() ); // Need to increment to get the correct iterator for erase
            }

            break;
        }
    }
}  
```

<a id="ID4EYE" />

### ESRAMManager::Free : libère de la mémoire dans l'ESRAM

La fonction **Free** fait l'inverse de la fonction Allocate. Appelez **Free** sur les données que vous ne souhaitez plus stocker dans l'ESRAM. **Free** efface les données et réintègre cette mémoire dans la liste FreeSpace.

**C++**

```cpp theme={null}
//--------------------------------------------------------------------------------------
// Name: Free
// Desc: Free memory in ESRAM
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void ESRAMManager::Free(ESRAMAllocation& alloc)
{
    XSFScopedNamedEvent( m_spImmediateContext.Get(), XSF_COLOR_RENDER, L"ESRAMManager Free" );

    // See if we can coalesce the freed memory with an existing free space
    FreeSpace* pCoalesced = nullptr;
    for(auto iter = m_freeSpaces.rbegin(); iter != m_freeSpaces.rend(); ++iter)
    {
        FreeSpace& freeSpace = *iter;
        if(freeSpace.m_endBlock == alloc.m_beginBlock)
        {
            // This free space comes immediately before the allocation
            if(pCoalesced == nullptr)
            {
                // Grow the free space to encompass the allocation
                freeSpace.m_endBlock = alloc.m_endBlock;
                pCoalesced = &freeSpace;
            }
            else
            {
                // We already found an area of free space that comes immediately after the allocation,
                //  and now we found an area of free space that comes immediately before the allocation.
                //  Coalesce the two free spaces.
                pCoalesced->m_beginBlock = freeSpace.m_beginBlock;
                m_freeSpaces.erase( (++iter).base() ); // Need to increment to get the correct iterator for erase
                break;
            }
        }
        else if(freeSpace.m_beginBlock == alloc.m_endBlock)
        {
            // This free space comes immediately after the allocation
            if(pCoalesced == nullptr)
            {
                // Grow the free space to encompass the allocation
                freeSpace.m_beginBlock = alloc.m_beginBlock;
                pCoalesced = &freeSpace;
            }
            else
            {
                // We already found an area of free space that comes immediately before the allocation,
                //  and now we found an area of free space that comes immediately after the allocation.
                //  Coalesce the two free spaces.
                pCoalesced->m_endBlock = freeSpace.m_endBlock;
                m_freeSpaces.erase( (++iter).base() ); // Need to increment to get the correct iterator for erase
                break;
            }
        }
    }

    if( pCoalesced == nullptr )
    {
        // We weren't able to coalesce this allocation with any existing free space, so add it to the list
        m_freeSpaces.emplace_back(alloc.m_beginBlock, alloc.m_endBlock);
    }

    alloc.Invalidate();
}  
```

<a id="ID4ELF" />

### ESRAMManager::InsertGPUWait : indique au GPU d'attendre une opération DMA impliquant la ressource

La fonction **InsertGPUWait** empêche le GPU d'accéder aux données ESRAM pendant que ces données sont manipulées. Les barrières indiquent au GPU d'attendre. Cette opération doit être effectuée après un Prefetch ou un Writeback.

**C++**

```cpp theme={null}
//--------------------------------------------------------------------------------------
// Name: InsertGPUWait
// Desc: Tell the GPU to wait on a DMA operation involving the given resource. You must 
//  do this after performing a Prefetch or a Writeback, before accessing the resource on the GPU.
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void ESRAMManager::InsertGPUWait( ESRAMResource const & esramResource )
{
    m_spImmediateContext->InsertWaitOnFence( D3D11_INSERT_FENCE_NO_KICKOFF, esramResource.m_fence );
}  
```

<a id="ID4E1F" />

### ESRAMManager::Create : crée une mémoire tampon dans l'ESRAM sans contenu initial

La fonction **Create** prend des données en entrée, les convertit dans des formats gérables par l'ESRAM, puis appelle la fonction **Allocate** pour obtenir un bloc de mémoire. Si un bloc de mémoire suffisamment grand est disponible, créez les ressources à l'aide de **ID3D11Device**.

**C++**

```cpp theme={null}
//--------------------------------------------------------------------------------------
// Name: Create
// Desc: Create a buffer in ESRAM without any initial contents
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void ESRAMManager::Create( ID3D11Device* const pDevice, D3D11_BUFFER_DESC desc, ESRAMBuffer& esramDest )
{

    desc.MiscFlags |= D3D11X_RESOURCE_MISC_ESRAM_RESIDENT;

    // Calculate the size and alignment required to create the buffer in ESRAM
    XG_BUFFER_DESC xgDesc;
    ZeroMemory( &xgDesc, sizeof(xgDesc) );
    xgDesc.ByteWidth = desc.ByteWidth;
    xgDesc.Usage = (XG_USAGE)desc.Usage;
    xgDesc.BindFlags = desc.BindFlags;
    xgDesc.CPUAccessFlags = desc.CPUAccessFlags;
    xgDesc.MiscFlags = desc.MiscFlags;
    xgDesc.StructureByteStride = desc.StructureByteStride;
    xgDesc.ESRAMOffsetBytes = 0;
    xgDesc.ESRAMUsageBytes = 0;

    XG_RESOURCE_LAYOUT layout;
    XSF_ERROR_IF_FAILED( XGComputeBufferLayout( &xgDesc, &layout ) );

    // Try to allocate the proper amount of space in ESRAM
    ESRAMResource& esramResource = esramDest.m_esramResource;
    Allocate( (UINT)layout.SizeBytes, (UINT)layout.BaseAlignmentBytes, esramResource.m_allocation );
    if( !esramResource.m_allocation.IsValid() )
    {
        // The allocation failed. Try freeing any discarded resources, and try again
        GarbageCollect();
        Allocate( (UINT)layout.SizeBytes, (UINT)layout.BaseAlignmentBytes, esramResource.m_allocation );
    }
    if( !esramResource.m_allocation.IsValid() )
    {
        // The allocation failed. Crash.
        XSF_ERROR_IF_FAILED( E_FAIL );
        return;
    }

    // Create the buffer resource in ESRAM
    desc.ESRAMOffsetBytes = esramResource.m_allocation.m_esramPtr;
    desc.ESRAMUsageBytes = 0;
    XSF_ERROR_IF_FAILED( pDevice->CreateBuffer( &desc, nullptr, esramDest.m_spBuffer.ReleaseAndGetAddressOf() ) );
}  
```

<a id="ID4ENG" />

### ESRAMManager::Prefetch : prélit des données à partir d'une mémoire tampon DRAM

La fonction **Prefetch** copie des données de la DRAM vers l'ESRAM.

**C++**

```cpp theme={null}
//--------------------------------------------------------------------------------------
// Name: Prefetch
// Desc: Load a DRAM buffer into ESRAM
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void ESRAMManager::Prefetch( ID3D11Device* const pDevice, ID3D11Buffer* const pDRAMSource, ESRAMBuffer& esramDest )
{
    D3D11_BUFFER_DESC desc;
    pDRAMSource->GetDesc( &desc );

    Create(pDevice, desc, esramDest);

    // Prefetch should be used for resources not written to by the GPU, so we don't need to sync 
    //  with the GPU like we do in Writeback
    m_spDmaContext->CopyResource( esramDest.m_spBuffer.Get(), pDRAMSource, 0 );

    // Insert a fence after the copy and kickoff the DMA engine
    esramDest.m_esramResource.m_fence = m_spDmaContext->InsertFence( 0 );
}  
```

<a id="ID4E3G" />

### ESRAMManager::Writeback : copie une mémoire tampon ESRAM dans la DRAM

La fonction **Writeback** fait l'inverse de la fonction **Prefetch** : elle copie des données de l'ESRAM vers la DRAM.

**C++**

```cpp theme={null}
//--------------------------------------------------------------------------------------
// Name: Writeback
// Desc: Copy an ESRAM buffer into DRAM
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void ESRAMManager::Writeback( ESRAMBuffer& esramSource, ID3D11Buffer* pDRAMDest )
{
    // Writeback is typically used for resources that are written by the GPU. Thus we need to ensure that 
    //  the GPU is done using this resource before we start the DMA operation. We insert a GPU fence and wait
    //  on it with the DMA engine. Once we get an API to access the internal write fence for a given resource,  
    //  we can wait on that instead.
    m_spImmediateContext->FlushGpuCaches( esramSource.m_spBuffer.Get() );
    UINT64 fence = m_spImmediateContext->InsertFence( 0 );
    m_spDmaContext->InsertWaitOnFence( 0, fence );

    m_spDmaContext->CopyResource( pDRAMDest, esramSource.m_spBuffer.Get(), 0 );

    // Insert a fence after the copy and kickoff the DMA engine
    esramSource.m_esramResource.m_fence = m_spDmaContext->InsertFence( 0 );
}  
```

<a id="ID4ENH" />

### ESRAMManager::Discard : marque une ressource ESRAM comme n'étant plus utilisée, afin que sa mémoire puisse être récupérée

La fonction **Discard** abandonne les données stockées dans l'ESRAM lorsqu'elles ne sont plus nécessaires. Lors de l'abandon de données, insérez une barrière pour vous assurer que les données ne sont pas supprimées avant que le GPU n'ait fini de les utiliser. Ensuite, placez la ressource dans une liste d'objets abandonnés que le garbage collector libérera ultérieurement.

**C++**

```cpp theme={null}
//--------------------------------------------------------------------------------------
// Name: Discard
// Desc: Mark an ESRAM resource as no longer in use, so its memory can be reclaimed
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void ESRAMManager::Discard( ESRAMBuffer& esramBuffer )
{
    // Flush the GPU caches to ensure the result of any GPU writes takes effect before we reclaim
    //  the memory for another purpose
    m_spImmediateContext->FlushGpuCaches( esramBuffer.m_spBuffer.Get() );

    DiscardInternal( esramBuffer.m_esramResource );
}  
```

**C++**

```cpp theme={null}
void ESRAMManager::DiscardInternal( _In_ ESRAMResource& esramResource )
{
    // Insert a fence on the GPU to mark the last time the GPU touched this resource. This fence
    //  will be used later to ensure the DMA engine doesn't trample over memory that is still in use.
    esramResource.m_fence = m_spImmediateContext->InsertFence( 0 );

    // If we waited on the fence and freed the resource memory immediately, every discard operation
    //  would block the DMA engine until the GPU caught up. That would be bad. So instead we just
    //  add the discarded resource to a list, and "garbage collect" later when we actually need 
    //  to use the free space.
    m_discardedResources.push_back( esramResource );

    esramResource.m_allocation.Invalidate();
    esramResource.m_fence = INVALID_FENCE;
}  
```

<a id="ID4ECAAC" />

### ESRAMManager::GarbageCollect : libère la mémoire appartenant aux ressources abandonnées

La fonction **GarbageCollect** parcourt la liste des ressources abandonnées, attend qu'elles ne soient plus utilisées par le GPU, puis appelle la fonction **Free** sur celles-ci. La fonction **Free** efface les données et réinsère l'espace désormais vide dans la liste FreeSpace.

**C++**

```cpp theme={null}
//--------------------------------------------------------------------------------------
// Name: GarbageCollect
// Desc: Free memory belonging to discarded resources
//--------------------------------------------------------------------------------------
void ESRAMManager::GarbageCollect()
{
    for(int i = 0; i < m_discardedResources.size(); ++i)
    {
        // At this point the resource fence indicates the last time the GPU used the resource.
        //  Have the DMA engine wait on this fence to ensure that we can't copy a new resource onto
        //  the reclaimed memory until the GPU is done with it.
        m_spDmaContext->InsertWaitOnFence( D3D11_INSERT_FENCE_NO_KICKOFF, m_discardedResources[i].m_fence );
        Free( m_discardedResources[i].m_allocation );
    }
    m_discardedResources.clear();
}  
```


## Related topics

- [Répertoire des rubriques NDA : liens actifs vers Microsoft Learn](/fr/nda/nda-topic-directory.md)
- [XR-013 Liaison des comptes Microsoft avec les comptes d'éditeur](/fr/publishing/certification/xr/xr-013.md)
- [XR-013 Liaison des comptes Microsoft aux comptes d'éditeur](/fr-CA/publishing/certification/xr/xr-013.md)
- [Guides de portage vers le GDK XBOX](/fr/home/build-first-title/porting-guides.md)
- [Notes de version de PlayFab Party](/fr/services/playfab/multiplayer/networking/release-notes.md)
