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


Отправка разных типов ресурсов

Показывает, как использовать один буфер для отправки данных буфера констант и данных буфера вершин в GPU, а также правильное выделение и размещение данных в буферах. Использование одного буфера повышает гибкость использования памяти и обеспечивает приложения с более жестким контролем над использованием памяти. Также показаны различия между моделями Direct3D 11 и Direct3D 12 для отправки различных типов ресурсов.

Отправка различных типов ресурсов

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

В Direct3D 11 вы создаете отдельные буферы для различных типов данных ресурсов (обратите внимание на различные BindFlags используемые в приведенном ниже примере кода Direct3D 11), явно привязывая каждый буфер ресурсов к графическому конвейеру и обновляя данные ресурсов с различными методами на основе различных типов ресурсов.

В Direct3D 12 и Direct3D 11 следует использовать ресурсы отправки только в том случае, когда ЦП будет записывать данные один раз, а GPU будет читать его один раз.

В некоторых случаях

  • GPU будет считывать данные несколько раз или
  • GPU не считывает данные линейно или
  • Отрисовка значительно ограничена GPU.

В таких случаях лучше использовать ID3D12GraphicsCommandList::CopyTextureRegion или ID3D12GraphicsCommandList::CopyBufferRegion, чтобы скопировать данные буфера отправки в ресурс по умолчанию.

Ресурс по умолчанию может находиться в физической памяти видео на дискретных GPU.

Пример кода: 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
    );

    // ...
}

Пример кода: 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) );
}

Обратите внимание на использование вспомогательных структур CD3DX12_HEAP_PROPERTIES и CD3DX12_RESOURCE_DESC.

Константы

Чтобы задать константы, вершины и индексы в куче отправки или чтения, используйте следующие API.

Ресурсы

Ресурсы — это концепция Direct3D, которая абстрагирует использование физической памяти GPU. Ресурсы требуют виртуального адресного пространства GPU для доступа к физической памяти. Создание ресурсов является свободным потоком.

Существует три типа ресурсов в отношении создания виртуальных адресов и гибкости в Direct3D 12.

Выделенные ресурсы

Выделенные ресурсы — это наиболее распространенная идея ресурсов Direct3D в поколениях. Создание такого ресурса выделяет диапазон виртуальных адресов, неявную кучу достаточно большой, чтобы соответствовать всему ресурсу, и фиксирует диапазон виртуальных адресов в физической памяти, инкапсулированной кучей. Свойства неявной кучи должны быть переданы для сопоставления функционального четности с предыдущими версиями Direct3D. См. раздел ID3D12Device::CreateCommittedResource.

Зарезервированные ресурсы

Зарезервированные ресурсы эквивалентны ресурсам Direct3D 11 с плитками. При создании выделяется только диапазон виртуальных адресов и не сопоставляется с кучей. Приложение сопоставляет такие ресурсы с кучами позже. Возможности таких ресурсов в настоящее время не изменяются с Direct3D 11, так как их можно сопоставить с кучей при детализации плитки 64 КБ с Помощью UpdateTileMappings. Обратитесь к ID3D12Device::CreateReservedResource.

Размещение ресурсов

Новые возможности Direct3D 12 позволяют создавать кучи отдельно от ресурсов. После этого можно найти несколько ресурсов в одной куче. Это можно сделать, не создавая плитки или зарезервированные ресурсы, позволяя создавать возможности для всех типов ресурсов, которые могут создаваться непосредственно приложением. Несколько ресурсов могут перекрываться, и для правильного использования физической памяти необходимо использовать ID3D12GraphicsCommandList::ResourceBarrier. См. раздел ID3D12Device::CreatePlacedResource.

Отражение размера ресурса

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

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

Например, массив с одним элементом с однобайтовым буфером возвращает размер 64 КБ и выравнивание 64 КБ, так как буферы могут быть выровнены только 64 КБ.

Кроме того, массив элементов с двумя однотексельными 64 КБ выровненными текстурами и однотексельными 4 МБ выровненными текстурными отчетами по разным размерам в зависимости от порядка массива. Если выровненные текстуры размером 4 МБ находится в середине, результирующий размер составляет 12 МБ. В противном случае результирующий размер составляет 8 МБ. Возвращаемое выравнивание всегда будет 4 МБ, супермножеством всех выравниваний в массиве ресурсов.

Ссылка на следующие API.

Выравнивание буфера

Ограничения выравнивания буфера не изменились с Direct3D 11, в частности:

  • 4 МБ для нескольких примеров текстур.
  • 64 КБ для одно примеров текстур и буферов.