Freigeben über


Hochladen verschiedener Arten von Ressourcen

Zeigt, wie Sie einen Puffer verwenden, um sowohl Konstantenpufferdaten als auch Vertexpufferdaten in die GPU hochzuladen, und wie Daten ordnungsgemäß untergeordnet und in Puffern platziert werden. Die Verwendung eines einzelnen Puffers erhöht die Flexibilität der Speichernutzung und bietet Anwendungen eine strengere Kontrolle über die Speicherauslastung. Zeigt auch die Unterschiede zwischen den Direct3D 11- und Direct3D 12-Modellen für das Hochladen verschiedener Arten von Ressourcen an.

Hochladen verschiedener Arten von Ressourcen

In Direct3D 12 erstellen Sie einen Puffer, um verschiedene Arten von Ressourcendaten für das Hochladen aufzunehmen, und kopieren Ressourcendaten auf ähnliche Weise in denselben Puffer für unterschiedliche Ressourcendaten. Anschließend werden einzelne Ansichten erstellt, um diese Ressourcendaten an die Grafikpipeline im Direct3D 12-Ressourcenbindungsmodell zu binden.

In Direct3D 11 erstellen Sie separate Puffer für verschiedene Arten von Ressourcendaten (beachten Sie die unterschiedlichen BindFlags , die im folgenden Direct3D 11-Beispielcode verwendet werden), binden jeden Ressourcenpuffer explizit an die Grafikpipeline und aktualisieren die Ressourcendaten mit unterschiedlichen Methoden basierend auf verschiedenen Ressourcentypen.

Sowohl in Direct3D 12 als auch in Direct3D 11 sollten Sie Uploadressourcen nur dort verwenden, wo die CPU die Daten einmal schreibt, und die GPU liest sie einmal.

In einigen Fällen

  • die GPU die Daten mehrmals liest, oder
  • die GPU die Daten nicht linear liest, oder
  • das Rendering ist bereits erheblich GPU-eingeschränkt.

In diesen Fällen kann die bessere Option sein, ID3D12GraphicsCommandList::CopyTextureRegion oder ID3D12GraphicsCommandList::CopyBufferRegion zu verwenden, um die Uploadpufferdaten in eine Standardressource zu kopieren.

Eine Standardressource kann sich im physischen Videospeicher auf diskreten GPUs befinden.

Codebeispiel: 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
    );

    // ...
}

Codebeispiel: 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) );
}

Beachten Sie die Verwendung der Hilfsstrukturen CD3DX12_HEAP_PROPERTIES und CD3DX12_RESOURCE_DESC.

Konstanten

Verwenden Sie die folgenden APIs, um Konstanten, Scheitelpunkte und Indizes innerhalb eines Upload- oder Readback-Heaps festzulegen.

Ressourcen

Ressourcen sind das Direct3D-Konzept, das die Nutzung des physischen GPU-Arbeitsspeichers abstrahiert. Ressourcen benötigen virtuellen GPU-Adressraum, um auf physischen Arbeitsspeicher zuzugreifen. Die Ressourcenerstellung erfolgt im Freethread.

Es gibt drei Arten von Ressourcen in Bezug auf die Erstellung virtueller Adressen und die Flexibilität in Direct3D 12.

Zugesagte Ressourcen

Engagierte Ressourcen sind die häufigste Idee von Direct3D-Ressourcen über Generationen hinweg. Beim Erstellen einer solchen Ressource wird ein virtueller Adressbereich zugewiesen, ein impliziter Heap, der groß genug ist, um für die gesamte Ressource geeignet zu sein, und committet den virtuellen Adressbereich in den physischen Arbeitsspeicher, der vom Heap gekapselt wird. Die impliziten Heapeigenschaften müssen übergeben werden, um die funktionale Parität mit früheren Direct3D-Versionen zu erfüllen. Weitere Informationen finden Sie unter ID3D12Device::CreateCommittedResource.

Reservierte Ressourcen

Reservierte Ressourcen entsprechen den gekachelten Direct3D 11-Ressourcen. Bei der Erstellung wird nur ein virtueller Adressbereich zugeordnet und keinem Heap zugeordnet. Die Anwendung zuordnen solche Ressourcen später heaps. Die Funktionen solcher Ressourcen sind derzeit gegenüber Direct3D 11 unverändert, da sie mit UpdateTileMappings einem Heap mit einer Kachelgranularität von 64 KB zugeordnet werden können. Weitere Informationen finden Sie unter ID3D12Device::CreateReservedResource.

Platzierte Ressourcen

Neu für Direct3D 12 können Sie Heaps getrennt von Ressourcen erstellen. Anschließend können Sie mehrere Ressourcen innerhalb eines einzelnen Heaps suchen. Sie können dies tun, ohne gekachelte oder reservierte Ressourcen zu erstellen, sodass die Funktionen für alle Ressourcentypen aktiviert werden, die direkt von Ihrer Anwendung erstellt werden können. Mehrere Ressourcen können sich überschneiden, und Sie müssen die ID3D12GraphicsCommandList::ResourceBarrier verwenden, um den physischen Arbeitsspeicher ordnungsgemäß zu verwenden. Weitere Informationen finden Sie unter ID3D12Device::CreatePlacedResource.

Reflektion der Ressourcengröße

Sie müssen die Ressourcengrößenreflektion verwenden, um zu verstehen, wie viel Raumtexturen mit unbekannten Texturlayouts in Heaps erforderlich sind. Puffer werden ebenfalls unterstützt, aber vor allem aus Bequemlichkeit.

Sie sollten sich der großen Abweichungen bei der Ausrichtung bewusst sein, um Ressourcen dichter zu packen.

Beispielsweise gibt ein Array mit einem Ein-Byte-Puffer eine Größe von 64 KB und eine Ausrichtung von 64 KB zurück, da Puffer nur 64 KB ausgerichtet sein können.

Außerdem meldet ein Array mit drei Elementen mit zwei mit einem Texel 64 KB ausgerichteten Texturen und einer mit einem Texel 4 MB ausgerichteten Textur unterschiedliche Größen basierend auf der Reihenfolge des Arrays. Wenn sich die 4 MB ausgerichteten Texturen in der Mitte befindet, ist die resultierende Größe 12 MB. Andernfalls beträgt die resultierende Größe 8 MB. Die zurückgegebene Ausrichtung würde immer 4 MB betragen, die Übermenge aller Ausrichtungen im Ressourcenarray.

Verweisen Sie auf die folgenden APIs.

Pufferausrichtung

Die Pufferausrichtungseinschränkungen wurden gegenüber Direct3D 11 nicht geändert, insbesondere:

  • 4 MB für Texturen mit mehreren Beispielen.
  • 64 KB für Einzelmustertexturen und Puffer.