共用方式為


上傳不同類型的資源

示範如何使用一個緩衝區,將常數緩衝區數據和頂點緩衝區數據上傳至 GPU,以及如何適當地子配置數據,並將數據放在緩衝區內。 使用單一緩衝區可增加記憶體使用量彈性,並讓應用程式更嚴格地控制記憶體使用量。 同時顯示用於上傳不同資源類型之 Direct3D 11 和 Direct3D 12 模型之間的差異。

上傳不同類型的資源

在 Direct3D 12 中,您會建立一個緩衝區來容納不同類型的資源數據以進行上傳,而您會以類似的方式將資源數據複製到相同的緩衝區,以不同的資源數據。 接著會建立個別檢視,將這些資源數據系結至 Direct3D 12 資源系結模型中的圖形管線。

在 Direct3D 11 中,您會為不同類型的資源數據建立不同的緩衝區(請注意下列 Direct3D 11 範例程式代碼中使用的不同 BindFlags ),明確地將每個資源緩衝區系結至圖形管線,並根據不同的資源類型使用不同的方法更新資源數據。

在 Direct3D 12 和 Direct3D 11 中,您應該只使用上傳資源,其中 CPU 會寫入數據一次,而 GPU 會讀取一次。

在某些情況下,

  • GPU 會多次讀取數據,或
  • GPU 不會以線性方式讀取數據,或
  • 轉譯已大幅限制 GPU。

在這些情況下,更好的選項可能是使用ID3D12GraphicsCommandList::CopyTextureRegionID3D12GraphicsCommandList::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_PROPERTIESCD3DX12_RESOURCE_DESC

常數

若要在上傳或讀取回寫堆積內設定常數、頂點和索引,請使用下列 API。

資源

資源是 Direct3D 概念,可抽象化 GPU 物理記憶體的使用方式。 資源需要 GPU 虛擬位址空間才能存取物理記憶體。 資源建立是自由線程的。

Direct3D 12 中的虛擬位址建立和彈性有三種類型的資源。

已認可的資源

認可資源是一代代最常見的 Direct3D 資源概念。 建立這類資源會配置虛擬位址範圍、足以容納整個資源的隱含堆積,並將虛擬位址範圍認可到堆積所封裝的實體記憶體。 必須傳遞隱含堆積屬性,以符合與舊版 Direct3D 的功能同位。 請參閱 ID3D12Device::CreateCommittedResource

保留的資源

保留的資源相當於 Direct3D 11 並排顯示的資源。 在其建立時,只會配置虛擬位址範圍,而不會對應至任何堆積。 應用程式稍後會將這類資源對應至堆積。 這類資源的功能目前與 Direct3D 11 沒有變更,因為它們可以使用 UpdateTileMappings 以 64KB 磚粒度對應至堆積。 請參閱 ID3D12Device::CreateReservedResource

放置的資源

Direct3D 12 的新功能,您可以建立與資源分開的堆積。 之後,您可以在單一堆積內找到多個資源。 您可以這麼做,而不需要建立並排或保留的資源,就能讓應用程式直接建立所有資源類型的功能。 多個資源可能會重疊,您必須使用 ID3D12GraphicsCommandList::ResourceBarrier 才能正確使用物理記憶體。 請參閱 ID3D12Device::CreatePlacedResource

資源大小反映

您必須使用資源大小反映,以了解堆積中具有未知紋理配置的空間紋理需要多少空間。 也支援緩衝區,但主要是為了方便起見。

您應該注意主要對齊差異,以協助更密集地封裝資源。

例如,具有單一位元組緩衝區的單一元素陣列會 傳回 Size 為 64KB,而 Alignment 為 64KB,因為緩衝區只能對齊 64KB。

此外,具有兩個單一材質 64KB 對齊紋理的三個元素陣列,以及單一紋素 4MB 對齊紋理會根據數位的順序報告不同的大小。 如果 4MB 對齊的紋理位於中間,則產生的 Size 為 12MB。 否則,產生的 Size 為 8MB。 傳回的 Alignment 一律為 4MB,也就是資源陣列中所有對齊的超集。

參考下列 API。

緩衝區對齊方式

緩衝區對齊限制並未從 Direct3D 11 變更,特別是:

  • 多樣本紋理為 4 MB。
  • 單一取樣紋理和緩衝區的 64 KB。