Udostępnij przez


Przekazywanie różnych typów zasobów

Pokazuje, jak używać jednego bufora do przesyłania danych bufora stałego i danych bufora wierzchołka do GPU oraz jak prawidłowo podzielić i umieścić dane w buforach. Użycie pojedynczego buforu zwiększa elastyczność użycia pamięci i zapewnia aplikacjom większą kontrolę nad użyciem pamięci. Przedstawiono również różnice między modelami Direct3D 11 i Direct3D 12 do przekazywania różnych typów zasobów.

Przekazywanie różnych typów zasobów

W Direct3D 12 tworzysz jeden bufor, który umożliwia przesyłanie różnych typów danych zasobów, oraz kopiujesz dane zasobów do tego samego buforu w podobny sposób dla różnych typów danych. Poszczególne widoki są następnie tworzone w celu powiązania tych danych zasobów z potokiem grafiki w modelu powiązania zasobów Direct3D 12.

W Direct3D 11 tworzysz oddzielne bufory dla różnych typów danych zasobów (zwróć uwagę na różne BindFlags używane w poniższym przykładowym kodzie Direct3D 11), jawnie wiążesz każdy bufor zasobów z potokiem grafiki i aktualizujesz dane zasobów przy użyciu różnych metod w zależności od typu zasobów.

Zarówno w trybie Direct3D 12, jak i Direct3D 11, należy użyć zasobów przekazywania tylko wtedy, gdy procesor CPU zapisze dane raz, a procesor GPU odczytuje je raz.

W niektórych przypadkach,

  • procesor GPU odczytuje dane wiele razy lub
  • procesor GPU nie odczytuje danych liniowo ani
  • renderowanie jest już znacznie ograniczone przez procesor GPU.

W takich przypadkach lepszym rozwiązaniem może być użycie ID3D12GraphicsCommandList::CopyTextureRegion lub ID3D12GraphicsCommandList::CopyBufferRegion, aby skopiować dane buforu przesyłania do zasobu domyślnego.

Domyślny zasób może znajdować się w fizycznej pamięci wideo na dyskretnych procesorach GPU.

Przykład kodu: 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
    );

    // ...
}

Przykład kodu: 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) );
}

Zwróć uwagę na użycie struktur pomocniczych CD3DX12_HEAP_PROPERTIES i CD3DX12_RESOURCE_DESC.

Stałe

Aby ustawić stałe, wierzchołki i indeksy w stercie przesyłania lub odczytu, użyj następujących API.

Zasoby

Zasoby to koncepcja Direct3D, która abstrahuje użycie fizycznej pamięci GPU. Zasoby wymagają wirtualnej przestrzeni adresowej procesora GPU w celu uzyskania dostępu do pamięci fizycznej. Tworzenie zasobów jest wielowątkowe.

Istnieją trzy typy zasobów w odniesieniu do tworzenia wirtualnych adresów i elastyczności w trybie Direct3D 12.

Zatwierdzone zasoby

Przypisane zasoby to najbardziej powszechna koncepcja zasobów Direct3D na przestrzeni kolejnych generacji. Utworzenie takiego zasobu przydziela zakres adresów wirtualnych, niejawną stertę wystarczająco dużą, aby zmieścić cały zasób, oraz przypisuje zakres adresów wirtualnych do pamięci fizycznej zarządzanej przez stertę. Aby dopasować równoważność funkcjonalną z poprzednimi wersjami Direct3D, należy przekazać niejawne właściwości sterty. Zapoznaj się z ID3D12Device::CreateCommittedResource.

Zasoby zarezerwowane

Zasoby zarezerwowane są równoważne zasobom kafelków Direct3D 11. Podczas tworzenia jest przydzielany tylko wirtualny zakres adresów i nie jest mapowany na żadną stertę. Aplikacja później przypisze takie zasoby do stosów. Możliwości takich zasobów są obecnie niezmienione w Direct3D 11, ponieważ można je mapować na stertę z precyzją kafelka 64 KB przy użyciu UpdateTileMappings. Zobacz ID3D12Device::CreateReservedResource.

Umieszczone zasoby

Nowością w Direct3D 12 jest możliwość utworzenia sterty oddzielnie od zasobów. Następnie możesz zlokalizować wiele zasobów w jednym stercie. Można to zrobić bez tworzenia kafelków lub zasobów zarezerwowanych, co umożliwia tworzenie funkcji dla wszystkich typów zasobów, które można utworzyć bezpośrednio przez aplikację. Wiele zasobów może się nakładać, dlatego należy użyć ID3D12GraphicsCommandList::ResourceBarrier, aby poprawnie ponownie użyć pamięci fizycznej. Odwołaj się do ID3D12Device::CreatePlacedResource.

Analiza rozmiaru zasobu

Należy użyć refleksji rozmiaru zasobu, aby zrozumieć, jak dużo miejsca zajmują tekstury o nieznanych układach tekstur w stosach. Bufory są również obsługiwane, ale głównie jako udogodnienie.

Należy pamiętać o poważnych rozbieżnościach wyrównania, aby ułatwić bardziej gęste pakowanie zasobów.

Na przykład tablica jednoelementowa z jednobajtowym buforem zwraca Rozmiar 64 KB i Wyrównanie 64 KB, ponieważ bufory mogą być wyłącznie wyrównane do 64 KB.

Ponadto trójelementowa tablica zawierająca dwie tekstury wyrównane do pojedynczego texela o wielkości 64 KB każda oraz tekstura wyrównana do pojedynczego texela o wielkości 4 MB zgłaszają różne rozmiary w zależności od kolejności w tablicy. Jeśli tekstury wyrównane 4 MB są w środku, wynikowy Rozmiar wynosi 12 MB. W przeciwnym razie wynikowy rozmiar wynosi 8 MB. Zwrócone wyrównanie zawsze będzie mieć wartość 4 MB, co jest większe niż wszystkie inne wyrównania w tablicy zasobów.

Odwołaj się do następujących interfejsów API.

Wyrównanie buforu

Ograniczenia wyrównania buforu nie zmieniły się od Direct3D 11, w szczególności:

  • 4 MB dla tekstur z wieloma próbkami.
  • 64 KB dla tekstur i buforów z pojedynczym próbkowaniem.