Поделиться через


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

Важные интерфейсы программирования приложений

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

Что нужно знать

Технологии

Необходимые условия

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

Инструкции

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

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

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

  1. Укажите использование ресурсов как динамическое. Например, укажите значение D3D11_USAGE_DYNAMIC в элементе Usage структуры D3D11_BUFFER_DESC для вершинного или константного буфера и укажите значение D3D11_USAGE_DYNAMIC в элементе Usage структуры D3D11_TEXTURE2D_DESC для 2D текстуры.
  2. Укажите ресурс как доступный для записи. Например, укажите значение D3D11_CPU_ACCESS_WRITE в CPUAccessFlags члена D3D11_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.

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

При вызове отображения в статическом буфере вершин, когда GPU использует буфер, вы получите значительное ухудшение производительности. В этой ситуации карта должна ждать, пока GPU не завершит считывание вершинных или индексных данных из буфера, прежде чем карта сможет вернуться в вызывающее приложение, что приводит к значительной задержке. Вызов и сопоставление, а затем многократная отрисовка из статического буфера в течение одного кадра, также предотвращают возможность буферизации команд отрисовки на 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 после последнего вызова рисования, использующего вершины. Вершины больше не используются, когда ID3D11DeviceContext::GetData возвращает S_OK. При сопоставлении буфера с D3D11_MAP_WRITE_DISCARD или без значений карты всегда гарантируется, что вершины синхронизированы правильно с GPU. Однако, если вы выполняете сопоставление без значений отображения, вы столкнетесь с потерей производительности, описанной ранее.

Заметка

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

 

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