さまざまな種類のリソースのアップロード

1 つのバッファーを使用して定数バッファー データと頂点バッファー データの両方を GPU にアップロードする方法と、バッファー内にデータを適切にサブ割り当ておよび配置する方法を説明します。 1 つのバッファーを使用すると、メモリ使用量の柔軟性が向上し、アプリケーションはメモリ使用量をより厳密に制御できます。 また、さまざまな種類のリソースをアップロードするための Direct3D 11 モデルとDirect3D 12 モデルの違いを示します。

さまざまな種類のリソースをアップロードする

Direct3D 12では、アップロードするさまざまな種類のリソース データに対応する 1 つのバッファーを作成し、リソース データごとに同様の方法でリソース データを同じバッファーにコピーします。 その後、個々のビューが作成され、それらのリソース データが Direct3D 12 リソース バインド モデルのグラフィックス パイプラインにバインドされます。

Direct3D 11 では、さまざまな種類のリソース データに対して個別のバッファーを作成し (以下の Direct3D 11 サンプル コードで使用される異なる BindFlags バッファーに注意してください)、各リソース バッファーをグラフィックス パイプラインに明示的にバインドし、リソースの種類に基づいて異なるメソッドでリソース データを更新します。

Direct3D 12と Direct3D 11 の両方で、アップロード リソースは、CPU がデータを 1 回書き込み、GPU が 1 回読み取る場所でのみ使用する必要があります。

場合によっては

  • 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 を使用します。

リソース

リソースは、GPU 物理メモリの使用を抽象化する Direct3D の概念です。 リソースには、物理メモリにアクセスするための GPU 仮想アドレス空間が必要です。 リソース作成はフリー スレッドです。

仮想アドレスの作成とDirect3D 12の柔軟性に関しては、3 種類のリソースがあります。

コミット済みリソース

コミットされたリソースは、世代を超える Direct3D リソースの最も一般的なアイデアです。 このようなリソースを作成すると、仮想アドレス範囲 (リソース全体が収まる大きさの暗黙的ヒープ) が割り当てられ、このヒープによってカプセル化される物理メモリに仮想アドレス範囲がコミットされます。 関数型パリティと以前の Direct3D バージョンと一致させるには、暗黙的なヒープ プロパティを渡す必要があります。 「ID3D12Device::CreateCommittedResource」を参照してください。

予約済みリソース

予約済みリソースは、Direct3D 11 タイル リソースと同じです。 作成時には、仮想アドレス範囲のみが割り当てられ、ヒープにはマップされません。 そのようなリソースは、後でアプリケーションによってヒープにマップされます。 このようなリソースの機能は、 現在、UpdateTileMappings を使用して 64 KB のタイル粒度でヒープにマップできるため、Direct3D 11 から変更されていません。 ID3D12Device::CreateReservedResource を参照してください。

配置済みリソース

Direct3D 12の新機能として、リソースとは別にヒープを作成できます。 その後、1 つのヒープ内で複数のリソースを見つけることができます。 これを行うには、タイル化されたリソースや予約済みのリソースを作成せずに、アプリケーションで直接作成できるすべてのリソースの種類の機能を有効にします。 複数のリソースが重複する可能性があり、物理メモリを正しく再利用するには 、ID3D12GraphicsCommandList::ResourceBarrier を使用する必要があります。 ID3D12Device::CreatePlacedResource を参照してください。

リソース サイズのリフレクション

リソース サイズのリフレクションを使用して、不明なテクスチャ レイアウトを持つ空間テクスチャがヒープ内で必要な量を把握する必要があります。 バッファーもサポートされますが、それは主に便宜上の理由です。

リソースをより高密度にパックするには、大きなアラインメントの不一致に注意する必要があります。

たとえば、1 バイト バッファーを持つ単一要素配列では、バッファーを 64 KB だけ配置できるため、 Size は 64 KB、 Alignment は 64 KB を返します。

また、64 KB にアラインされた 2 つの単一テクセル テクスチャと 4 MB にアラインされた 1 つの単一テクセル テクスチャを含む 3 要素の配列は、配列の順序によって異なるサイズを報告します。 4 MB の整列テクスチャが中央にある場合、結果の Size は 12 MB になります。 それ以外の場合はサイズが 8 MB になります。 返される Alignment は常に 4 MB で、リソース配列内のすべてのアラインメントのスーパーセットです。

次の API を参照します。

バッファー アラインメント

バッファー配置の制限は Direct3D 11 から変更されていません。特に次のとおりです。

  • マルチサンプル テクスチャの場合は 4 MB。
  • シングルサンプル テクスチャおよびバッファーの場合は 64 KB。