Практическое руководство. Использование динамических ресурсов

Важные API

Вы создаете и используете динамические ресурсы, когда приложению необходимо изменить данные в этих ресурсах. Вы можете создавать текстуры и буферы для динамического использования.

Это важно знать

Технологии

Предварительные требования

Предполагается, что вы знакомы с C++. Также вы должны быть знакомы с основными принципами программирования графики.

Инструкции

Шаг 1. Указание динамического использования

Если вы хотите, чтобы приложение могло вносить изменения в ресурсы, необходимо указать эти ресурсы как динамические и доступные для записи при их создании.

Указание динамического использования

  1. Укажите динамическое использование ресурсов. Например, укажите значение D3D11_USAGE_DYNAMIC в элементе Usageэлемента D3D11_BUFFER_DESC для буфера вершин или констант и укажите значение D3D11_USAGE_DYNAMIC в элементе Usageэлемента D3D11_TEXTURE2D_DESC для двухd-текстуры.
  2. Укажите ресурс как доступный для записи. Например, укажите значение D3D11_CPU_ACCESS_WRITE в элементе CPUAccessFlagsD3D11_BUFFER_DESC или D3D11_TEXTURE2D_DESC.
  3. Передайте описание ресурса в функцию создания. Например, передайте адрес D3D11_BUFFER_DESCв ID3D11Device::CreateBuffer , а адрес D3D11_TEXTURE2D_DESCв ID3D11Device::CreateTexture2D.

Ниже приведен пример создания динамического буфера вершин:

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

Шаг 2. Изменение динамического ресурса

Если вы указали ресурс как динамический и доступный для записи при его создании, вы можете позже внести изменения в этот ресурс.

Изменение данных в динамическом ресурсе

  1. Настройте новые данные для ресурса. Объявите переменную типа D3D11_MAPPED_SUBRESOURCE и инициализируйте ее нулевым значением. Эта переменная используется для изменения ресурса.
  2. Отключите доступ графического процессора (GPU) к данным, которые вы хотите изменить, и получите указатель на память, содержащую данные. Чтобы получить этот указатель, передайте D3D11_MAP_WRITE_DISCARD в параметр MapType при вызове ID3D11DeviceContext::Map. Установите этот указатель на адрес переменной D3D11_MAPPED_SUBRESOURCE из предыдущего шага.
  3. Запишите новые данные в память.
  4. Вызовите ID3D11DeviceContext::Unmap , чтобы повторно включить доступ GPU к данным после завершения записи новых данных.

Ниже приведен пример изменения динамического буфера вершин:

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

Комментарии

Использование динамических текстур

Рекомендуется создавать только одну динамическую текстуру для каждого формата и, возможно, для каждого размера. Мы не рекомендуем создавать динамические MIP-карты, кубы и тома из-за дополнительных издержек при сопоставлении на каждом уровне. Для MIP-карт можно указать D3D11_MAP_WRITE_DISCARD только на верхнем уровне. Все уровни удаляются путем сопоставления только верхнего уровня. Это поведение одинаково для томов и кубов. Для кубов сопоставляются верхний уровень и грань 0.

Использование динамических буферов

При вызове map для статического буфера вершин, когда GPU использует буфер, вы получаете значительное снижение производительности. В этом случае map должно подождать, пока GPU завершит чтение данных вершин или индексов из буфера, прежде чем Map сможет вернуться в вызывающее приложение, что приводит к значительной задержке. Вызов map , а затем отрисовка из статического буфера несколько раз на кадр также предотвращает буферизацию команд отрисовки GPU, так как он должен завершить команды перед возвратом указателя карты. Без буферизованных команд GPU остается в бездействии до тех пор, пока приложение не завершит заполнение буфера вершин или буфера индексов и не выполнит команду отрисовки.

В идеале данные вершины или индекса никогда не изменятся, но это не всегда возможно. Существует множество ситуаций, когда приложению необходимо изменять данные вершин или индексов для каждого кадра, возможно, даже несколько раз для каждого кадра. В таких ситуациях рекомендуется создать буфер вершин или индексов с D3D11_USAGE_DYNAMIC. Этот флаг использования приводит к оптимизации среды выполнения для частых операций сопоставления. D3D11_USAGE_DYNAMIC полезно только при частом сопоставлении буфера; Если данные должны оставаться постоянными, поместите эти данные в статическую вершину или буфер индекса.

Чтобы повысить производительность при использовании динамических буферов вершин, приложение должно вызвать Map с соответствующими значениями D3D11_MAP . D3D11_MAP_WRITE_DISCARD указывает, что приложению не нужно хранить старые данные вершин или индексов в буфере. Если GPU по-прежнему использует буфер при вызове Map с D3D11_MAP_WRITE_DISCARD, среда выполнения возвращает указатель на новую область памяти вместо старых данных буфера. Это позволяет GPU продолжать использовать старые данные, пока приложение помещает данные в новый буфер. В приложении не требуется дополнительное управление памятью; старый буфер повторно используется или уничтожается автоматически после завершения работы с GPU.

Примечание

При сопоставлении буфера с D3D11_MAP_WRITE_DISCARD среда выполнения всегда удаляет весь буфер. Невозможно сохранить сведения в несопоставленных областях буфера, указав ненулевое поле смещения или ограниченного размера.

 

Бывают случаи, когда объем данных, необходимых приложению для хранения на карту, невелик, например добавление четырех вершин для отрисовки спрайта. D3D11_MAP_WRITE_NO_OVERWRITE указывает, что приложение не будет перезаписывать данные, которые уже используются в динамическом буфере. Вызов Map вернет указатель на старые данные, что позволит приложению добавлять новые данные в неиспользуемые области буфера вершины или индекса. Приложение не должно изменять вершины или индексы, используемые в операции рисования, так как они могут по-прежнему использоваться GPU. Мы рекомендуем приложению использовать D3D11_MAP_WRITE_DISCARD после заполнения динамического буфера, чтобы получить новую область памяти, которая удаляет старые данные вершины или индекса после завершения работы GPU.

Механизм асинхронного запроса полезен для определения того, используются ли вершины GPU. Выполните запрос типа D3D11_QUERY_EVENT после последнего вызова draw, использующего вершины. Вершины больше не используются, когда ID3D11DeviceContext::GetData возвращает S_OK. При сопоставлении буфера с D3D11_MAP_WRITE_DISCARD или без значений карты всегда гарантируется правильная синхронизация вершин с GPU. Но при сопоставлении без значений карты вы будете нести снижение производительности, описанное выше.

Примечание

Среда выполнения Direct3D 11.1, доступная начиная с Windows 8, позволяет сопоставлять динамические буферы констант и представления ресурсов шейдера (SRV) динамических буферов с D3D11_MAP_WRITE_NO_OVERWRITE. Среда выполнения Direct3D 11 и более ранних версий ограничили сопоставление частичного обновления без перезаписи с буферами вершин или индексов. Чтобы определить, поддерживает ли устройство Direct3D эти функции, вызовите ID3D11Device::CheckFeatureSupport с D3D11_FEATURE_D3D11_OPTIONS. Функция CheckFeatureSupport заполняет элементы структуры D3D11_FEATURE_DATA_D3D11_OPTIONS функциями устройства. Соответствующими элементами здесь являются MapNoOverwriteOnDynamicConstantBuffer и MapNoOverwriteOnDynamicBufferSRV.

 

Использование Direct3D 11