Partager via


Téléchargement de différents types de ressources

Montre comment utiliser un tampon pour télécharger à la fois les données de tampon constantes et les données de tampon de sommet vers le GPU, et comment sous-allouer correctement et placer les données dans les tampons. L’utilisation d’un seul tampon augmente la flexibilité d’utilisation de la mémoire et offre aux applications un contrôle plus strict sur l’utilisation de la mémoire. Montre également les différences entre les modèles Direct3D 11 et Direct3D 12 pour le téléchargement de différents types de ressources.

Télécharger différents types de ressources

Dans Direct3D 12, vous créez un tampon pour accueillir différents types de données de ressources à télécharger, et vous copiez les données de ressources vers le même tampon de manière similaire pour différents types de ressources. Des vues individuelles sont ensuite créées pour lier ces données de ressources au pipeline graphique dans le modèle de liaison de ressources de Direct3D 12.

Dans Direct3D 11, vous créez des tampons distincts pour différents types de données de ressources (notez les différents BindFlags utilisés dans le code d’exemple de Direct3D 11 ci-dessous), en liant explicitement chaque tampon de ressource au pipeline graphique et en mettant à jour les données de ressources avec différentes méthodes basées sur différents types de ressources.

Dans Direct3D 12 et Direct3D 11, vous devez utiliser des ressources de téléchargement uniquement là où le CPU écrira les données une seule fois et le GPU les lira une seule fois.

Dans certains cas,

  • le GPU lira les données plusieurs fois, ou
  • le GPU ne lira pas les données de manière linéaire, ou
  • le rendu est déjà significativement limité par le GPU.

Dans ces cas, la meilleure option pourrait être d’utiliser ID3D12GraphicsCommandList::CopyTextureRegion ou ID3D12GraphicsCommandList::CopyBufferRegion pour copier les données du tampon de téléchargement vers une ressource par défaut.

Une ressource par défaut peut résider dans la mémoire vidéo physique sur les GPU discrets.

Exemple de code : Direct3D 11

// Direct3D 11: Separate buffers for each resource type.

void main()
{
    // ...

    // Create a constant buffer.
    float constantBufferData[] = ...;

    D3D11_BUFFER_DESC constantBufferDesc = {0};  
    constantBufferDesc.ByteWidth = sizeof(constantBufferData);  
    constantBufferDesc.Usage = D3D11_USAGE_DYNAMIC;  
    constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;  
    constantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;  

    ComPtr<ID3D11Buffer> constantBuffer;
    d3dDevice->CreateBuffer(  
        &constantBufferDesc,  
        NULL,
        &constantBuffer  
        );

    // Create a vertex buffer.
    float vertexBufferData[] = ...;

    D3D11_BUFFER_DESC vertexBufferDesc = { 0 };
    vertexBufferDesc.ByteWidth = sizeof(vertexBufferData);
    vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;

    ComPtr<ID3D11Buffer> vertexBuffer;
    d3dDevice->CreateBuffer(
        &vertexBufferDesc,
        NULL,
        &vertexBuffer
        );

    // ...
}

void DrawFrame()
{
    // ...

    // Bind buffers to the graphics pipeline.
    d3dDeviceContext->VSSetConstantBuffers(0, 1, constantBuffer.Get());
    d3dDeviceContext->IASetVertexBuffers(0, 1, vertexBuffer.Get(), ...);

    // Update the constant buffer.
    D3D11_MAPPED_SUBRESOURCE mappedResource;  
    d3dDeviceContext->Map(
        constantBuffer.Get(),
        0, 
        D3D11_MAP_WRITE_DISCARD,
        0,
        &mappedResource
        );
    memcpy(mappedResource.pData, constantBufferData,
        sizeof(contatnBufferData));
    d3dDeviceContext->Unmap(constantBuffer.Get(), 0);  

    // Update the vertex buffer.
    d3dDeviceContext->UpdateSubresource(
        vertexBuffer.Get(),
        0,
        NULL,
        vertexBufferData,
        sizeof(vertexBufferData),
        0
    );

    // ...
}

Exemple de code : Direct3D 12

// Direct3D 12: One buffer to accommodate different types of resources

ComPtr<ID3D12Resource> m_spUploadBuffer;
UINT8* m_pDataBegin = nullptr;    // starting position of upload buffer
UINT8* m_pDataCur = nullptr;      // current position of upload buffer
UINT8* m_pDataEnd = nullptr;      // ending position of upload buffer

void main()
{
    //
    // Initialize an upload buffer
    //

    InitializeUploadBuffer(64 * 1024);

    // ...
}

void DrawFrame()
{
    // ...

    // Set vertices data to the upload buffer.

    float vertices[] = ...;
    UINT verticesOffset = 0;
    ThrowIfFailed(
        SetDataToUploadBuffer(
            vertices, sizeof(float), sizeof(vertices) / sizeof(float),
            sizeof(float), 
            verticesOffset
            ));

    // Set constant data to the upload buffer.

    float constants[] = ...;
    UINT constantsOffset = 0;
    ThrowIfFailed(
        SetDataToUploadBuffer(
            constants, sizeof(float), sizeof(constants) / sizeof(float), 
            D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, 
            constantsOffset
            ));

    // Create vertex buffer views for the new binding model.

    D3D12_VERTEX_BUFFER_VIEW vertexBufferViewDesc = {
        m_spUploadBuffer->GetGPUVirtualAddress() + verticesOffset,
        sizeof(vertices), // size
        sizeof(float) * 4,  // stride
    };

    commandList->IASetVertexBuffers( 
        0,
        1,
        &vertexBufferViewDesc,
        ));

    // Create constant buffer views for the new binding model.

    D3D12_CONSTANT_BUFFER_VIEW_DESC constantBufferViewDesc = {
        m_spUploadBuffer->GetGPUVirtualAddress() + constantsOffset,
        sizeof(constants) // size
         };

    d3dDevice->CreateConstantBufferView(
        &constantBufferViewDesc,
        ...
        ));

    // Continue command list building and execution ...
}

//
// Create an upload buffer and keep it always mapped.
//

HRESULT InitializeUploadBuffer(SIZE_T uSize)
{
    HRESULT hr = d3dDevice->CreateCommittedResource(
         &CD3DX12_HEAP_PROPERTIES( D3D12_HEAP_TYPE_UPLOAD ),    
               D3D12_HEAP_FLAG_NONE, 
               &CD3DX12_RESOURCE_DESC::Buffer( uSize ), 
               D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,  
               IID_PPV_ARGS( &m_spUploadBuffer ) );

    if (SUCCEEDED(hr))
    {
        void* pData;
        //
        // No CPU reads will be done from the resource.
        //
        CD3DX12_RANGE readRange(0, 0);
        m_spUploadBuffer->Map( 0, &readRange, &pData ); 
        m_pDataCur = m_pDataBegin = reinterpret_cast< UINT8* >( pData );
        m_pDataEnd = m_pDataBegin + uSize;
    }
    return hr;
}

//
// Sub-allocate from the buffer, with offset aligned.
//

HRESULT SuballocateFromBuffer(SIZE_T uSize, UINT uAlign)
{
    m_pDataCur = reinterpret_cast< UINT8* >(
        Align(reinterpret_cast< SIZE_T >(m_pDataCur), uAlign)
        );

    return (m_pDataCur + uSize > m_pDataEnd) ? E_INVALIDARG : S_OK;
}

//
// Place and copy data to the upload buffer.
//

HRESULT SetDataToUploadBuffer(
    const void* pData, 
    UINT bytesPerData, 
    UINT dataCount, 
    UINT alignment, 
    UINT& byteOffset
    )
{
    SIZE_T byteSize = bytesPerData * dataCount;
    HRESULT hr = SuballocateFromBuffer(byteSize, alignment);
    if (SUCCEEDED(hr))
    {
        byteOffset = UINT(m_pDataCur - m_pDataBegin);
        memcpy(m_pDataCur, pData, byteSize); 
        m_pDataCur += byteSize;
    }
    return hr;
}

//
// Align uLocation to the next multiple of uAlign.
//

UINT Align(UINT uLocation, UINT uAlign)
{
    if ( (0 == uAlign) || (uAlign & (uAlign-1)) )
    {
        ThrowException("non-pow2 alignment");
    }

    return ( (uLocation + (uAlign-1)) & ~(uAlign-1) );
}

Notez l’utilisation des structures d’assistance CD3DX12_HEAP_PROPERTIES et CD3DX12_RESOURCE_DESC.

Constantes

Pour définir des constantes, des sommets et des indices dans un tas de téléchargement ou de lecture, utilisez les API suivantes.

Ressources

Les ressources sont le concept de Direct3D qui abstrait l’utilisation de la mémoire physique du GPU. Les ressources nécessitent un espace d’adresse virtuel GPU pour accéder à la mémoire physique. La création de ressources est multi-thread.

Il existe trois types de ressources en ce qui concerne la création d’adresses virtuelles et la flexibilité dans Direct3D 12.

Ressources engagées

Les ressources engagées sont l’idée la plus courante des ressources Direct3D au fil des générations. La création d’une telle ressource alloue une plage d’adresses virtuelles, un tas implicite suffisamment grand pour contenir toute la ressource et engage la plage d’adresses virtuelles à la mémoire physique encapsulée par le tas. Les propriétés du tas implicite doivent être passées pour correspondre à la parité fonctionnelle avec les versions précédentes de Direct3D. Veuillez vous reporter à ID3D12Device::CreateCommittedResource.

Ressources réservées

Les ressources réservées sont équivalentes aux ressources en mosaïque Direct3D 11. Lors de leur création, seule une plage d’adresses virtuelles est allouée et n’est pas mappée à un tas. L’application mappera ces ressources à des tas plus tard. Les capacités de ces ressources sont actuellement inchangées par rapport à Direct3D 11, car elles peuvent être mappées à un tas à une granularité de mosaïque de 64 Ko avec UpdateTileMappings. Veuillez vous reporter à ID3D12Device::CreateReservedResource.

Ressources placées

Nouveau pour Direct3D 12, vous pouvez créer des tas séparés des ressources. Ensuite, vous pouvez placer plusieurs ressources dans un seul tas. Vous pouvez le faire sans créer de ressources en mosaïque ou réservées, en activant les capacités pour tous les types de ressources pouvant être créées directement par votre application. Plusieurs ressources peuvent se chevaucher, et vous devez utiliser le ID3D12GraphicsCommandList::ResourceBarrier pour réutiliser correctement la mémoire physique. Veuillez vous reporter à ID3D12Device::CreatePlacedResource.

Réflexion sur la taille des ressources

Vous devez utiliser la réflexion sur la taille des ressources pour comprendre combien d’espace les textures avec des dispositions de texture inconnues nécessitent dans les tas. Les tampons sont également pris en charge, mais principalement par commodité.

Vous devez être conscient des principales divergences d’alignement pour aider à emballer les ressources plus densément.

Par exemple, un tableau à élément unique avec un tampon d’un octet renvoie une Taille de 64 Ko et un Alignement de 64 Ko, car les tampons peuvent être alignés uniquement sur 64 Ko.

De plus, un tableau de trois éléments avec deux textures alignées sur un seul texel de 64 Ko et une texture alignée sur un seul texel de 4 Mo indique des tailles différentes en fonction de l’ordre du tableau. Si les textures alignées de 4 Mo sont au milieu, alors la Taille résultante est de 12 Mo. Sinon, la taille résultante est de 8 Mo. L’alignement renvoyé serait toujours de 4 Mo, le sur-ensemble de tous les alignements dans le tableau de ressources.

Référez-vous aux API suivantes.

Alignement des tampons

Les restrictions d’alignement des tampons n’ont pas changé par rapport à Direct3D 11, notamment :

  • 4 Mo pour les textures multi-échantillons.
  • 64 Ko pour les textures et tampons à échantillon unique.