Vorgehensweise: Verwenden dynamischer Ressourcen

Wichtige APIs

Sie erstellen und verwenden dynamische Ressourcen, wenn Ihre App Daten in diesen Ressourcen ändern muss. Sie können Texturen und Puffer für die dynamische Verwendung erstellen.

Wichtige Informationen

Technologien

Voraussetzungen

Es wird davon ausgegangen, dass Sie mit C+ vertraut sind. Sie müssen außerdem mit den grundlegenden Konzepten der Grafikprogrammierung vertraut sein.

Anweisungen

Schritt 1: Angeben der dynamischen Nutzung

Wenn Sie möchten, dass Ihre App Änderungen an Ressourcen vornehmen kann, müssen Sie diese Ressourcen beim Erstellen als dynamisch und beschreibbar angeben.

So geben Sie die dynamische Nutzung an

  1. Geben Sie die Ressourcennutzung als dynamisch an. Geben Sie beispielsweise den wert D3D11_USAGE_DYNAMIC im Usage-Member von D3D11_BUFFER_DESC für einen Vertex- oder Konstantenpuffer an, und geben Sie den D3D11_USAGE_DYNAMIC Wert im Usage-Element von D3D11_TEXTURE2D_DESC für eine 2D-Textur an.
  2. Geben Sie die Ressource als schreibbar an. Geben Sie beispielsweise den D3D11_CPU_ACCESS_WRITE-Wert im CPUAccessFlags-Member von D3D11_BUFFER_DESC oder D3D11_TEXTURE2D_DESC an.
  3. Übergeben Sie die Ressourcenbeschreibung an die Erstellungsfunktion. Übergeben Sie beispielsweise die Adresse von D3D11_BUFFER_DESC an ID3D11Device::CreateBuffer , und übergeben Sie die Adresse von D3D11_TEXTURE2D_DESC an ID3D11Device::CreateTexture2D.

Hier ist ein Beispiel für die Erstellung eines dynamischen Vertexpuffers:

    // Create a vertex buffer for a triangle.

    float2 triangleVertices[] =
    {
        float2(-0.5f, -0.5f),
        float2(0.0f, 0.5f),
        float2(0.5f, -0.5f),
    };

    D3D11_BUFFER_DESC vertexBufferDesc = { 0 };
    vertexBufferDesc.ByteWidth = sizeof(float2)* ARRAYSIZE(triangleVertices);
    vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;

    D3D11_SUBRESOURCE_DATA vertexBufferData;
    vertexBufferData.pSysMem = triangleVertices;
    vertexBufferData.SysMemPitch = 0;
    vertexBufferData.SysMemSlicePitch = 0;

    DX::ThrowIfFailed(
        m_d3dDevice->CreateBuffer(
        &vertexBufferDesc,
        &vertexBufferData,
        &vertexBuffer2
        )
        );

Schritt 2: Ändern einer dynamischen Ressource

Wenn Sie eine Ressource beim Erstellen als dynamisch und schreibbar angegeben haben, können Sie später Änderungen an dieser Ressource vornehmen.

So ändern Sie Daten in einer dynamischen Ressource

  1. Richten Sie die neuen Daten für die Ressource ein. Deklarieren Sie eine D3D11_MAPPED_SUBRESOURCE Typvariable, und initialisieren Sie sie mit null. Sie verwenden diese Variable, um die Ressource zu ändern.
  2. Deaktivieren Sie den Gpu-Zugriff (Graphics Processing Unit) auf die Daten, die Sie ändern möchten, und rufen Sie einen Zeiger auf den Speicher ab, der die Daten enthält. Um diesen Zeiger zu erhalten, übergeben Sie D3D11_MAP_WRITE_DISCARD an den MapType-Parameter , wenn Sie ID3D11DeviceContext::Map aufrufen. Legen Sie diesen Zeiger auf die Adresse der D3D11_MAPPED_SUBRESOURCE Variablen aus dem vorherigen Schritt fest.
  3. Schreiben Sie die neuen Daten in den Arbeitsspeicher.
  4. Rufen Sie ID3D11DeviceContext::Unmap auf, um den GPU-Zugriff auf die Daten erneut zu konfigurieren, wenn Sie mit dem Schreiben der neuen Daten fertig sind.

Hier sehen Sie ein Beispiel für das Ändern eines dynamischen Vertexpuffers:

// This might get called as the result of a click event to double the size of the triangle.
void TriangleRenderer::MapDoubleVertices()
{
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));
    float2 vertices[] =
    {
        float2(-1.0f, -1.0f),
        float2(0.0f, 1.0f),
        float2(1.0f, -1.0f),
    };
    //  Disable GPU access to the vertex buffer data.
    auto m_d3dContext = m_deviceResources->GetD3DDeviceContext();
    m_d3dContext->Map(vertexBuffer2.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    //  Update the vertex buffer here.
    memcpy(mappedResource.pData, vertices, sizeof(vertices));
    //  Reenable GPU access to the vertex buffer data.
    m_d3dContext->Unmap(vertexBuffer2.Get(), 0);
}

Bemerkungen

Verwenden dynamischer Texturen

Es wird empfohlen, nur eine dynamische Textur pro Format und möglicherweise pro Größe zu erstellen. Es wird nicht empfohlen, dynamische Mipmaps, Cubes und Volumes aufgrund des zusätzlichen Mehraufwands bei der Zuordnung jeder Ebene zu erstellen. Für Mipmaps können Sie D3D11_MAP_WRITE_DISCARD nur auf der obersten Ebene angeben. Alle Ebenen werden verworfen, indem nur die oberste Ebene zugeordnet wird. Dieses Verhalten ist für Volumes und Cubes identisch. Bei Würfeln werden die oberste Ebene und das Gesicht 0 zugeordnet.

Verwenden dynamischer Puffer

Wenn Sie Map für einen statischen Vertexpuffer aufrufen, während die GPU den Puffer verwendet, erhalten Sie eine erhebliche Leistungseinbuße. In diesem Fall muss Map warten, bis die GPU die Vertex- oder Indexdaten aus dem Puffer gelesen hat, bevor Map zur aufrufenden App zurückkehren kann, was zu einer erheblichen Verzögerung führt. Das Aufrufen von Map und das anschließende Rendern aus einem statischen Puffer mehrmals pro Frame verhindert auch, dass die GPU Renderingbefehle puffert, da sie Befehle beenden muss, bevor der Kartenzeiger zurückgegeben wird. Ohne gepufferte Befehle verbleibt die GPU im Leerlauf, bis die App den Vertex- oder Indexpuffer auffüllt und einen Renderingbefehl ausgibt.

Im Idealfall würden sich die Vertex- oder Indexdaten nie ändern, aber dies ist nicht immer möglich. Es gibt viele Situationen, in denen die App Vertex- oder Indizieren von Daten in jedem Frame ändern muss, vielleicht sogar mehrmals pro Frame. In diesen Situationen wird empfohlen, den Vertex- oder Indexpuffer mit D3D11_USAGE_DYNAMIC zu erstellen. Dieses Verwendungsflag bewirkt, dass die Laufzeit für häufige Zuordnungsvorgänge optimiert wird. D3D11_USAGE_DYNAMIC ist nur nützlich, wenn der Puffer häufig zugeordnet wird; Wenn die Daten konstant bleiben sollen, platzieren Sie diese Daten in einem statischen Vertex- oder Indexpuffer.

Um bei Verwendung dynamischer Vertexpuffer eine Leistungsverbesserung zu erzielen, muss Ihre App Map mit den entsprechenden D3D11_MAP-Werten aufrufen. D3D11_MAP_WRITE_DISCARD gibt an, dass die App die alten Scheitelpunkt- oder Indexdaten nicht im Puffer behalten muss. Wenn die GPU weiterhin den Puffer verwendet, wenn Sie Map mit D3D11_MAP_WRITE_DISCARD aufrufen, gibt die Laufzeit einen Zeiger auf einen neuen Speicherbereich anstelle der alten Pufferdaten zurück. Dadurch kann die GPU weiterhin die alten Daten verwenden, während die App Daten im neuen Puffer platziert. In der App ist keine zusätzliche Speicherverwaltung erforderlich. Der alte Puffer wird automatisch wiederverwendet oder zerstört, wenn die GPU damit fertig ist.

Hinweis

Wenn Sie einen Puffer mit D3D11_MAP_WRITE_DISCARD zuordnen, verwirft die Laufzeit immer den gesamten Puffer. Sie können Informationen in nicht zugeordneten Bereichen des Puffers nicht beibehalten, indem Sie einen Offset ungleich Null oder ein Feld mit eingeschränkter Größe angeben.

 

Es gibt Fälle, in denen die Datenmenge, die die App pro Karte speichern muss, klein ist, z. B. das Hinzufügen von vier Scheitelpunkten zum Rendern eines Sprites. D3D11_MAP_WRITE_NO_OVERWRITE gibt an, dass die App keine Daten überschreibt, die bereits im dynamischen Puffer verwendet werden. Der Map-Aufruf gibt einen Zeiger auf die alten Daten zurück, mit dem die App neue Daten in nicht verwendeten Regionen des Scheitelpunkts oder Indexpuffers hinzufügen kann. Die App darf keine Scheitelpunkte oder Indizes ändern, die in einem Zeichnungsvorgang verwendet werden, da sie möglicherweise noch von der GPU verwendet werden. Es wird empfohlen, dass die App dann D3D11_MAP_WRITE_DISCARD verwendet, nachdem der dynamische Puffer voll ist, um einen neuen Speicherbereich zu empfangen, der die alten Vertex- oder Indexdaten verwirft, nachdem die GPU abgeschlossen ist.

Der asynchrone Abfragemechanismus ist nützlich, um zu bestimmen, ob Scheitelpunkte weiterhin von der GPU verwendet werden. Geben Sie eine Abfrage vom Typ D3D11_QUERY_EVENT nach dem letzten Draw-Aufruf aus, der die Scheitelpunkte verwendet. Die Scheitelpunkte werden nicht mehr verwendet, wenn ID3D11DeviceContext::GetData S_OK zurückgibt. Wenn Sie einen Puffer mit D3D11_MAP_WRITE_DISCARD oder keine Kartenwerten zuordnen, ist immer sichergestellt, dass die Scheitelpunkte ordnungsgemäß mit der GPU synchronisiert werden. Wenn Sie jedoch keine Kartenwerte zuordnen, kommt es zu den zuvor beschriebenen Leistungseinbußen.

Hinweis

Die Direct3D 11.1-Runtime, die ab Windows 8 verfügbar ist, ermöglicht die Zuordnung dynamischer Konstantenpuffer und Shaderressourcensichten (SRVs) dynamischer Puffer mit D3D11_MAP_WRITE_NO_OVERWRITE. Die Direct3D 11- und früheren Runtimes beschränkten die Zuordnung von teilweisen Aktualisierungen ohne Überschreibung auf Vertex- oder Indexpuffer. Um festzustellen, ob ein Direct3D-Gerät diese Features unterstützt, rufen Sie ID3D11Device::CheckFeatureSupport mit D3D11_FEATURE_D3D11_OPTIONS auf. CheckFeatureSupport füllt Member einer D3D11_FEATURE_DATA_D3D11_OPTIONS-Struktur mit den Features des Geräts aus. Die relevanten Member hier sind MapNoOverwriteOnDynamicConstantBuffer und MapNoOverwriteOnDynamicBufferSRV.

 

Verwenden von Direct3D 11