Guide pratique pour utiliser des ressources dynamiques

API importantes

Vous créez et utilisez des ressources dynamiques lorsque votre application doit modifier des données dans ces ressources. Vous pouvez créer des textures et des tampons pour une utilisation dynamique.

Bon à savoir

Technologies

Prérequis

Nous partons du principe que vous êtes familiarisé avec C++. Vous avez également besoin d’une expérience de base dans les concepts de programmation graphique.

Instructions

Étape 1 : Spécifier une utilisation dynamique

Si vous souhaitez que votre application puisse apporter des modifications aux ressources, vous devez spécifier ces ressources comme dynamiques et accessibles en écriture lorsque vous les créez.

Pour spécifier une utilisation dynamique

  1. Spécifiez l’utilisation des ressources comme dynamique. Par exemple, spécifiez la valeur D3D11_USAGE_DYNAMIC dans le membre Usage de D3D11_BUFFER_DESC pour un vertex ou une mémoire tampon constante et spécifiez la valeur D3D11_USAGE_DYNAMIC dans le membre Usage de D3D11_TEXTURE2D_DESC pour une texture 2D.
  2. Spécifiez la ressource comme accessible en écriture. Par exemple, spécifiez la valeur D3D11_CPU_ACCESS_WRITE dans le membre CPUAccessFlags de D3D11_BUFFER_DESC ou D3D11_TEXTURE2D_DESC.
  3. Transmettez la description de la ressource à la fonction de création. Par exemple, transmettez l’adresse de D3D11_BUFFER_DESC à ID3D11Device::CreateBuffer et transmettez l’adresse de D3D11_TEXTURE2D_DESC à ID3D11Device::CreateTexture2D.

Voici un exemple de création d’une mémoire tampon de vertex dynamique :

    // Create a vertex buffer for a triangle.

    float2 triangleVertices[] =
    {
        float2(-0.5f, -0.5f),
        float2(0.0f, 0.5f),
        float2(0.5f, -0.5f),
    };

    D3D11_BUFFER_DESC vertexBufferDesc = { 0 };
    vertexBufferDesc.ByteWidth = sizeof(float2)* ARRAYSIZE(triangleVertices);
    vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;

    D3D11_SUBRESOURCE_DATA vertexBufferData;
    vertexBufferData.pSysMem = triangleVertices;
    vertexBufferData.SysMemPitch = 0;
    vertexBufferData.SysMemSlicePitch = 0;

    DX::ThrowIfFailed(
        m_d3dDevice->CreateBuffer(
        &vertexBufferDesc,
        &vertexBufferData,
        &vertexBuffer2
        )
        );

Étape 2 : Modifier une ressource dynamique

Si vous avez spécifié une ressource comme dynamique et accessible en écriture lors de sa création, vous pouvez apporter des modifications ultérieures à cette ressource.

Pour modifier des données dans une ressource dynamique

  1. Configurez les nouvelles données pour la ressource. Déclarez une variable de type D3D11_MAPPED_SUBRESOURCE et initialisez-la sur zéro. Vous allez utiliser cette variable pour modifier la ressource.
  2. Désactivez l’accès de l’unité de traitement graphique (GPU) aux données que vous souhaitez modifier et obtenez un pointeur vers la mémoire qui contient les données. Pour obtenir ce pointeur, passez D3D11_MAP_WRITE_DISCARD au paramètre MapType lorsque vous appelez ID3D11DeviceContext::Map. Définissez ce pointeur vers l’adresse de la variable D3D11_MAPPED_SUBRESOURCE de l’étape précédente.
  3. Écrivez les nouvelles données dans la mémoire.
  4. Appelez ID3D11DeviceContext::Unmap pour réactiver l’accès GPU aux données lorsque vous avez terminé d’écrire les nouvelles données.

Voici un exemple de modification d’une mémoire tampon de vertex dynamique :

// This might get called as the result of a click event to double the size of the triangle.
void TriangleRenderer::MapDoubleVertices()
{
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));
    float2 vertices[] =
    {
        float2(-1.0f, -1.0f),
        float2(0.0f, 1.0f),
        float2(1.0f, -1.0f),
    };
    //  Disable GPU access to the vertex buffer data.
    auto m_d3dContext = m_deviceResources->GetD3DDeviceContext();
    m_d3dContext->Map(vertexBuffer2.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    //  Update the vertex buffer here.
    memcpy(mappedResource.pData, vertices, sizeof(vertices));
    //  Reenable GPU access to the vertex buffer data.
    m_d3dContext->Unmap(vertexBuffer2.Get(), 0);
}

Notes

Utilisation de textures dynamiques

Nous vous recommandons de créer une seule texture dynamique par format et éventuellement par taille. Nous vous déconseillons de créer des mipmaps, des cubes et des volumes dynamiques en raison de la surcharge supplémentaire dans le mappage à chaque niveau. Pour les mipmaps, vous pouvez spécifier D3D11_MAP_WRITE_DISCARD uniquement au niveau supérieur. Tous les niveaux sont ignorés en mappant uniquement le niveau supérieur. Ce comportement est le même pour les volumes et les cubes. Pour les cubes, le niveau supérieur et la face 0 sont mappés.

Utilisation de tampons dynamiques

Lorsque vous appelez Map sur une mémoire tampon de vertex statique pendant que le GPU utilise la mémoire tampon, vous obtenez une pénalité de performances importante. Dans ce cas, Map doit attendre que le GPU soit terminé de lire les données de vertex ou d’index à partir de la mémoire tampon avant que Map puisse revenir à l’application appelante, ce qui entraîne un délai important. L’appel de Map , puis le rendu à partir d’une mémoire tampon statique plusieurs fois par image empêche également le GPU de mettre en mémoire tampon les commandes de rendu, car il doit terminer les commandes avant de renvoyer le pointeur de carte. Sans commandes mises en mémoire tampon, le GPU reste inactif jusqu’à ce que l’application a terminé de remplir la mémoire tampon de vertex ou la mémoire tampon d’index et émet une commande de rendu.

Dans l’idéal, les données de vertex ou d’index ne changent jamais, mais ce n’est pas toujours possible. Il existe de nombreuses situations où l’application doit modifier les données de vertex ou d’indexer chaque image, voire plusieurs fois par image. Pour ces situations, nous vous recommandons de créer la mémoire tampon de vertex ou d’index avec D3D11_USAGE_DYNAMIC. Cet indicateur d’utilisation entraîne l’optimisation du runtime pour les opérations de carte fréquentes. D3D11_USAGE_DYNAMIC n’est utile que lorsque la mémoire tampon est mappée fréquemment ; si les données doivent rester constantes, placez ces données dans un vertex statique ou une mémoire tampon d’index.

Pour bénéficier d’une amélioration des performances lorsque vous utilisez des tampons de vertex dynamiques, votre application doit appeler Map avec les valeurs de D3D11_MAP appropriées. D3D11_MAP_WRITE_DISCARD indique que l’application n’a pas besoin de conserver les anciennes données de vertex ou d’index dans la mémoire tampon. Si le GPU utilise toujours la mémoire tampon lorsque vous appelez Map avec D3D11_MAP_WRITE_DISCARD, le runtime retourne un pointeur vers une nouvelle région de mémoire au lieu des anciennes données de mémoire tampon. Cela permet au GPU de continuer à utiliser les anciennes données pendant que l’application place les données dans la nouvelle mémoire tampon. Aucune gestion supplémentaire de la mémoire n’est requise dans l’application ; l’ancienne mémoire tampon est réutilisée ou détruite automatiquement lorsque le GPU est terminé.

Remarque

Lorsque vous mappez une mémoire tampon avec D3D11_MAP_WRITE_DISCARD, le runtime ignore toujours la mémoire tampon entière. Vous ne pouvez pas conserver les informations dans les zones non maapplées de la mémoire tampon en spécifiant un décalage différent de zéro ou un champ de taille limitée.

 

Il existe des cas où la quantité de données que l’application doit stocker par carte est faible, comme l’ajout de quatre sommets pour afficher un sprite. D3D11_MAP_WRITE_NO_OVERWRITE indique que l’application ne remplacera pas les données déjà utilisées dans la mémoire tampon dynamique. L’appel de carte retourne un pointeur vers les anciennes données, ce qui permet à l’application d’ajouter de nouvelles données dans les régions inutilisées du vertex ou de la mémoire tampon d’index. L’application ne doit pas modifier les sommets ou les index qui sont utilisés dans une opération de dessin, car ils peuvent toujours être utilisés par le GPU. Nous recommandons à l’application d’utiliser D3D11_MAP_WRITE_DISCARD une fois que la mémoire tampon dynamique est pleine pour recevoir une nouvelle région de mémoire, ce qui ignore les anciennes données de vertex ou d’index une fois le GPU terminé.

Le mécanisme de requête asynchrone est utile pour déterminer si les sommets sont toujours utilisés par le GPU. Émettez une requête de type D3D11_QUERY_EVENT après le dernier appel de dessin qui utilise les sommets. Les sommets ne sont plus utilisés quand ID3D11DeviceContext::GetData retourne S_OK. Lorsque vous mappez une mémoire tampon avec D3D11_MAP_WRITE_DISCARD ou aucune valeur de carte, vous êtes toujours garanti que les sommets sont correctement synchronisés avec le GPU. Toutefois, lorsque vous mappez sans valeurs de carte, vous encourez la pénalité de performances décrite précédemment.

Notes

Le runtime Direct3D 11.1, disponible à partir de Windows 8, permet de mapper des mémoires tampons dynamiques constantes et des vues de ressources de nuanceur (SMV) de mémoires tampons dynamiques avec D3D11_MAP_WRITE_NO_OVERWRITE. Les runtimes Direct3D 11 et antérieurs limitaient le mappage sans remplacement de mise à jour partielle aux tampons de vertex ou d’index. Pour déterminer si un appareil Direct3D prend en charge ces fonctionnalités, appelez ID3D11Device::CheckFeatureSupport avec D3D11_FEATURE_D3D11_OPTIONS. CheckFeatureSupport remplit les membres d’une structure D3D11_FEATURE_DATA_D3D11_OPTIONS avec les fonctionnalités de l’appareil. Les membres concernés ici sont MapNoOverwriteOnDynamicConstantBuffer et MapNoOverwriteOnDynamicBufferSRV.

 

Comment utiliser Direct3D 11