Compartir a través de


Carga de diferentes tipos de recursos

Muestra cómo usar un búfer para cargar datos de búfer de constantes y datos de búfer de vértices en la GPU y cómo subasignar y colocar correctamente los datos dentro de los búferes. El uso de un único búfer aumenta la flexibilidad de uso de memoria y proporciona a las aplicaciones un control más estricto sobre el uso de memoria. También muestra las diferencias entre los modelos de Direct3D 11 y Direct3D 12 para cargar diferentes tipos de recursos.

Carga de diferentes tipos de recursos

En Direct3D 12, creas un búfer para dar cabida a diferentes tipos de datos de recursos para cargarlos y copias los datos de recursos en el mismo búfer de forma similar para los distintos datos de recursos. A continuación, se crean vistas individuales para enlazar esos datos de recursos a la canalización de gráficos en el modelo de enlace de recursos de Direct3D 12.

En Direct3D 11, crea búferes independientes para distintos tipos de datos de recursos (tenga en cuenta los diferentes BindFlags usados en el código de ejemplo de Direct3D 11 siguiente), enlazar explícitamente cada búfer de recursos a la canalización de gráficos y actualizar los datos de recursos con distintos métodos basados en distintos tipos de recursos.

En Direct3D 12 y Direct3D 11, debes usar cargar recursos solo donde la CPU escribirá los datos una vez y la GPU la leerá una vez.

En algunos casos,

  • la GPU leerá los datos varias veces o
  • la GPU no leerá los datos de forma lineal o
  • la representación ya es significativamente limitada por GPU.

En esos casos, la mejor opción podría ser usar ID3D12GraphicsCommandList::CopyTextureRegion o ID3D12GraphicsCommandList::CopyBufferRegion para copiar los datos del búfer de carga en un recurso predeterminado.

Un recurso predeterminado puede residir en memoria de vídeo físico en GPU discretas.

Ejemplo de código: 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
    );

    // ...
}

Ejemplo de código: 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) );
}

Tenga en cuenta el uso de las estructuras auxiliares CD3DX12_HEAP_PROPERTIES y CD3DX12_RESOURCE_DESC.

Constantes

Para establecer constantes, vértices e índices dentro de un montón de carga o lectura diferida, use las siguientes API.

Recursos

Los recursos son el concepto de Direct3D que abstrae el uso de la memoria física de GPU. Los recursos requieren espacio de direcciones virtuales de GPU para acceder a la memoria física. La creación de recursos está libre.

Hay tres tipos de recursos con respecto a la creación y flexibilidad de direcciones virtuales en Direct3D 12.

Recursos confirmados

Los recursos confirmados son la idea más común de los recursos de Direct3D a lo largo de las generaciones. Al crear este recurso, se asigna el intervalo de direcciones virtuales, un montón implícito lo suficientemente grande como para ajustarse a todo el recurso y se confirma el intervalo de direcciones virtuales en la memoria física encapsulada por el montón. Las propiedades implícitas del montón deben pasarse para que coincidan con la paridad funcional con versiones anteriores de Direct3D. Consulte ID3D12Device::CreateCommittedResource.

Recursos reservados

Los recursos reservados son equivalentes a los recursos en mosaico de Direct3D 11. En su creación, solo se asigna un intervalo de direcciones virtuales y no se asigna a ningún montón. La aplicación asignará estos recursos a montones más adelante. Las funcionalidades de estos recursos no cambian actualmente desde Direct3D 11, ya que se pueden asignar a un montón en una granularidad de mosaico de 64 KB con UpdateTileMappings. Consulte ID3D12Device::CreateReservedResource.

Recursos colocados

Novedad de Direct3D 12, puedes crear montones independientes de los recursos. Después, puede localizar varios recursos dentro de un único montón. Puede hacerlo sin crear recursos en mosaico o reservados, lo que permite que la aplicación pueda crear directamente las funcionalidades de todos los tipos de recursos. Es posible que varios recursos se superpongan y debe usar ID3D12GraphicsCommandList::ResourceBarrier para volver a usar la memoria física correctamente. Consulte ID3D12Device::CreatePlacedResource.

Reflexión de tamaño de recurso

Debe usar la reflexión de tamaño de recurso para comprender la cantidad de texturas de espacio que requieren los diseños de textura desconocidos en montones. También se admiten búferes, pero principalmente como comodidad.

Debe tener en cuenta las discrepancias de alineación principales para ayudar a empaquetar los recursos de forma más densa.

Por ejemplo, una matriz de un solo elemento con un búfer de un solo byte devuelve un tamaño de 64 KB y una alineación de 64 KB, porque los búferes solo pueden estar alineados con 64 KB.

Además, una matriz de tres elementos con dos texturas alineadas de un solo elemento de 64 KB y un solo texel de 4 MB informes de textura alineadas difieren los tamaños en función del orden de la matriz. Si las texturas alineadas de 4 MB están en el medio, el tamaño resultante es de 12 MB. De lo contrario, el tamaño resultante es de 8 MB. La alineación devuelta siempre sería de 4 MB, el superconjunto de todas las alineaciones de la matriz de recursos.

Haga referencia a las SIGUIENTES API.

Alineación del búfer

Las restricciones de alineación del búfer no han cambiado de Direct3D 11, en particular:

  • 4 MB para texturas de varias muestras.
  • 64 KB para texturas y búferes de muestra única.