Como usar recursos dinâmicos

APIs importantes

Você cria e usa recursos dinâmicos quando seu aplicativo precisa alterar os dados nesses recursos. Você pode criar texturas e buffers para uso dinâmico.

O que você precisa saber

Tecnologias

Pré-requisitos

Partimos do princípio de que você conhece C++. Você também precisa ter experiência básica com conceitos de programação de elementos gráficos.

Instruções

Etapa 1: Especificar o uso dinâmico

Se você quiser que seu aplicativo possa fazer alterações nos recursos, especifique esses recursos como dinâmicos e graváveis ao criá-los.

Para especificar o uso dinâmico

  1. Especifique o uso do recurso como dinâmico. Por exemplo, especifique o valor D3D11_USAGE_DYNAMIC no membro Usage de D3D11_BUFFER_DESC para um buffer de vértice ou constante e especifique o valor D3D11_USAGE_DYNAMIC no membro Usage de D3D11_TEXTURE2D_DESC para uma textura 2D.
  2. Especifique o recurso como gravável. Por exemplo, especifique o valor D3D11_CPU_ACCESS_WRITE no membro CPUAccessFlags de D3D11_BUFFER_DESC ou D3D11_TEXTURE2D_DESC.
  3. Passe a descrição do recurso para a função de criação. Por exemplo, passe o endereço de D3D11_BUFFER_DESC para ID3D11Device::CreateBuffer e passe o endereço de D3D11_TEXTURE2D_DESC para ID3D11Device::CreateTexture2D.

Aqui está um exemplo de criação de um buffer de vértice dinâmico:

    // 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
        )
        );

Etapa 2: Alterar um recurso dinâmico

Se você especificou um recurso como dinâmico e gravável ao criá-lo, poderá fazer alterações posteriormente nesse recurso.

Para alterar dados em um recurso dinâmico

  1. Configure os novos dados para o recurso. Declare uma variável de tipo D3D11_MAPPED_SUBRESOURCE e inicialize-a como zero. Você usará essa variável para alterar o recurso.
  2. Desabilite o acesso da GPU (unidade de processamento gráfico) aos dados que você deseja alterar e obtenha um ponteiro para a memória que contém os dados. Para obter esse ponteiro, passe D3D11_MAP_WRITE_DISCARD para o parâmetro MapType ao chamar ID3D11DeviceContext::Map. Defina esse ponteiro para o endereço da variável D3D11_MAPPED_SUBRESOURCE da etapa anterior.
  3. Escreva os novos dados na memória.
  4. Chame ID3D11DeviceContext::Unmap para reabilitar o acesso à GPU aos dados quando terminar de gravar os novos dados.

Aqui está um exemplo de alteração de um buffer de vértice dinâmico:

// 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);
}

Comentários

Usando texturas dinâmicas

Recomendamos que você crie apenas uma textura dinâmica por formato e possivelmente por tamanho. Não recomendamos criar mipmaps dinâmicos, cubos e volumes devido à sobrecarga adicional no mapeamento de cada nível. Para mipmaps, você pode especificar D3D11_MAP_WRITE_DISCARD somente no nível superior. Todos os níveis são descartados mapeando apenas o nível superior. Esse comportamento é o mesmo para volumes e cubos. Para cubos, o nível superior e a face 0 são mapeados.

Usando buffers dinâmicos

Quando você chama Mapa em um buffer de vértice estático enquanto a GPU está usando o buffer, você recebe uma penalidade significativa de desempenho. Nessa situação, o Mapa deve aguardar até que a GPU termine de ler dados de vértice ou índice do buffer antes que o Mapa possa retornar ao aplicativo de chamada, o que causa um atraso significativo. Chamar Mapa e renderizar de um buffer estático várias vezes por quadro também impede que a GPU faça buffer de comandos de renderização, pois ela deve concluir comandos antes de retornar o ponteiro do mapa. Sem comandos em buffer, a GPU permanece ociosa até que o aplicativo termine de preencher o buffer de vértice ou o buffer de índice e emita um comando de renderização.

O ideal é que os dados de vértice ou índice nunca sejam alterados, mas isso nem sempre é possível. Há muitas situações em que o aplicativo precisa alterar dados de vértice ou índice a cada quadro, talvez até várias vezes por quadro. Para essas situações, recomendamos que você crie o buffer de vértice ou índice com D3D11_USAGE_DYNAMIC. Esse sinalizador de uso faz com que o runtime otimize para operações de mapa frequentes. D3D11_USAGE_DYNAMIC só é útil quando o buffer é mapeado com frequência; se os dados permanecerem constantes, coloque esses dados em um vértice estático ou buffer de índice.

Para receber uma melhoria de desempenho ao usar buffers de vértice dinâmicos, seu aplicativo deve chamar Map com os valores de D3D11_MAP apropriados. D3D11_MAP_WRITE_DISCARD indica que o aplicativo não precisa manter os dados de vértice ou índice antigos no buffer. Se a GPU ainda estiver usando o buffer quando você chamar Map com D3D11_MAP_WRITE_DISCARD, o runtime retornará um ponteiro para uma nova região de memória em vez dos dados de buffer antigos. Isso permite que a GPU continue usando os dados antigos enquanto o aplicativo coloca dados no novo buffer. Nenhum gerenciamento de memória adicional é necessário no aplicativo; o buffer antigo é reutilizado ou destruído automaticamente quando a GPU é concluída com ele.

Observação

Quando você mapeia um buffer com D3D11_MAP_WRITE_DISCARD, o runtime sempre descarta todo o buffer. Você não pode preservar informações em áreas não mapeadas do buffer especificando um campo de deslocamento diferente de zero ou tamanho limitado.

 

Há casos em que a quantidade de dados que o aplicativo precisa armazenar por mapa é pequena, como adicionar quatro vértices para renderizar um sprite. D3D11_MAP_WRITE_NO_OVERWRITE indica que o aplicativo não substituirá os dados já em uso no buffer dinâmico. A chamada mapa retornará um ponteiro para os dados antigos, o que permitirá que o aplicativo adicione novos dados em regiões não utilizados do vértice ou buffer de índice. O aplicativo não deve modificar vértices ou índices usados em uma operação de desenho, pois eles ainda podem estar em uso pela GPU. Recomendamos que o aplicativo use D3D11_MAP_WRITE_DISCARD depois que o buffer dinâmico estiver cheio para receber uma nova região de memória, que descarta os dados de vértice ou índice antigos após a conclusão da GPU.

O mecanismo de consulta assíncrona é útil para determinar se os vértices ainda estão em uso pela GPU. Emita uma consulta do tipo D3D11_QUERY_EVENT após a última chamada de desenho que usa os vértices. Os vértices não estão mais em uso quando ID3D11DeviceContext::GetData retorna S_OK. Ao mapear um buffer com D3D11_MAP_WRITE_DISCARD ou nenhum valor de mapa, você sempre tem a garantia de que os vértices são sincronizados corretamente com a GPU. Mas, quando você mapear sem valores de mapa, incorrerá na penalidade de desempenho descrita anteriormente.

Observação

O runtime do Direct3D 11.1, que está disponível a partir de Windows 8, permite mapear buffers constantes dinâmicos e SRVs (exibições de recursos de sombreador) de buffers dinâmicos com D3D11_MAP_WRITE_NO_OVERWRITE. O Direct3D 11 e os runtimes anteriores limitaram o mapeamento de atualização parcial sem substituição para buffers de vértice ou índice. Para determinar se um dispositivo Direct3D dá suporte a esses recursos, chame ID3D11Device::CheckFeatureSupport com D3D11_FEATURE_D3D11_OPTIONS. CheckFeatureSupport preenche membros de uma estrutura de D3D11_FEATURE_DATA_D3D11_OPTIONS com os recursos do dispositivo. Os membros relevantes aqui são MapNoOverwriteOnDynamicConstantBuffer e MapNoOverwriteOnDynamicBufferSRV.

 

Como usar o Direct3D 11