Verwenden von Ressourcenbarrieren zum Synchronisieren von Ressourcenzuständen in Direct3D 12

Um die CPU-Gesamtauslastung zu reduzieren und das Multithreading und die Vorverarbeitung des Treibers zu ermöglichen, verschiebt Direct3D 12 die Verantwortung für die Ressourcenzustandsverwaltung vom Grafiktreiber auf die Anwendung. Ein Beispiel für den Zustand pro Ressource ist, ob derzeit über einen Shader Ressourcenansicht oder als Renderzielansicht auf eine Texturressource zugegriffen wird. In Direct3D 11 mussten Treiber diesen Zustand im Hintergrund nachverfolgen. Dies ist aus CPU-Sicht teuer und erschwert jede Art von Multithreaddesign erheblich. In Microsoft Direct3D 12 wird der größte Teil des Ressourcenstatus von der Anwendung mit einer einzelnen API verwaltet: ID3D12GraphicsCommandList::ResourceBarrier.

Verwenden der ResourceBarrier-API zum Verwalten des Ressourcenzustands pro Ressource

ResourceBarrier benachrichtigt den Grafiktreiber über Situationen, in denen der Treiber möglicherweise mehrere Zugriffe auf den Arbeitsspeicher synchronisieren muss, in dem eine Ressource gespeichert ist. Die -Methode wird mit einer oder mehreren Beschreibungsstrukturen der Ressourcenbarriere aufgerufen, die den Typ der deklarierten Ressourcenbarriere angeben.

Es gibt drei Arten von Ressourcenbarrieren:

  • Übergangsbarriere: Eine Übergangsbarriere gibt an, dass ein Satz von Unterressourcen zwischen verschiedenen Verwendungen übergehen soll. Eine D3D12 _ RESOURCE TRANSITION _ _ BARRIER-Struktur wird verwendet, um die Unterressource anzugeben, die sich übergibt, sowie den Vorher- und Nachher-Status der Unterressource.

    Das System überprüft, ob unterresource-Übergänge in einer Befehlsliste mit vorherigen Übergängen in derselben Befehlsliste konsistent sind. Die Debugebene verfolgt den Zustand der Unterressource weiter, um andere Fehler zu finden. Diese Überprüfung ist jedoch konservativer und nicht vollständig.

    Beachten Sie, dass Sie das Flag D3D12 _ RESOURCE _ BARRIER ALL _ _ SUBRESOURCES verwenden können, um anzugeben, dass alle Unterressourcen innerhalb einer Ressource umgestellt werden.

  • Aliasing-Barriere: Eine Aliasingbarriere gibt einen Übergang zwischen verwendungsbasierten zwei verschiedenen Ressourcen an, die überlappende Zuordnungen zum gleichen Heap aufweisen. Dies gilt sowohl für reservierte als auch für platzierte Ressourcen. Eine D3D12 _ RESOURCE _ ALIASING _ BARRIER-Struktur wird verwendet, um sowohl die before-Ressource als auch die after-Ressource anzugeben.

    Beachten Sie, dass eine oder beide Ressourcen NULL sein können, was darauf hinweist, dass jede gekachelte Ressource Aliasing verursachen kann. Weitere Informationen zur Verwendung von gekachelten Ressourcen finden Sie unter Kachelressourcen und Volumenkachelressourcen.

  • Unordered access view (UAV)-Barriere : Eine UAV-Barriere gibt an, dass alle UAV-Zugriffe , sowohl Lese- als auch Schreibzugriffe, auf eine bestimmte Ressource zwischen allen zukünftigen UAV-Zugriffen (Sowohl Lese- als auch Schreibzugriff) abgeschlossen werden müssen. Es ist nicht erforderlich, dass eine App eine UAV-Barriere zwischen zwei Zeichnen- oder Dispatchaufrufen setzt, die nur aus einem UAV lesen. Außerdem ist es nicht erforderlich, eine UAV-Barriere zwischen zwei Draw- oder Dispatch-Aufrufen zu setzen, die in dieselbe UAV schreiben, wenn die Anwendung weiß, dass es sicher ist, den UAV-Zugriff in beliebiger Reihenfolge auszuführen. Eine D3D12 _ RESOURCE _ UAV _ BARRIER-Struktur wird verwendet, um die UAV-Ressource anzugeben, für die die Barriere gilt. Die Anwendung kann NULL für die UAV der Barriere angeben, was darauf hinweist, dass für jeden UAV-Zugriff die Barriere erforderlich sein könnte.

Wenn ResourceBarrier mit einem Array von Beschreibungen zur Ressourcenbarriere aufgerufen wird, verhält sich die API so, als ob sie einmal für jedes Element in der Reihenfolge aufgerufen würde, in der sie bereitgestellt wurden.

Zu jedem Zeitpunkt befindet sich eine Unterressource in genau einem Zustand, der durch den Satz von D3D12 _ RESOURCE _ STATES-Flags bestimmt wird, die für ResourceBarrierbereitgestellt werden. Die Anwendung muss sicherstellen, dass der Vor- und Nachher-Status aufeinander folgender Aufrufe von ResourceBarrier zustimmt.

Tipp

Anwendungen sollten nach Möglichkeit mehrere Übergänge in einen API-Aufruf batchen.

Ressourcenzustände

Eine vollständige Liste der Ressourcenzustände, zwischen denen eine Ressource umsteigen kann, finden Sie im Referenzthema für die D3D12 _ RESOURCE _ STATES-Enumeration.

Informationen zu geteilten Ressourcenbarrieren finden Sie auch unter D3D12 _ RESOURCE _ BARRIER _ FLAGS.

Anfangszustände für Ressourcen

Ressourcen können mit einem beliebigen vom Benutzer angegebenen Anfangszustand (gültig für die Ressourcenbeschreibung) erstellt werden, mit folgenden Ausnahmen:

  • Hochladen Heaps müssen im Zustand D3D12 _ gestartet werden. RESOURCE _ STATE GENERIC READ ist eine _ _ bitweise OR-Kombination aus:
    • D3D12 _ RESOURCE _ STATE _ VERTEX UND CONSTANT _ _ _ BUFFER
    • D3D12 _ RESOURCE _ STATE _ INDEX _ BUFFER
    • D3D12 _ RESOURCE _ STATE _ COPY _ SOURCE
    • D3D12 _ RESOURCE _ STATE _ NON _ PIXEL _ SHADER _ RESOURCE
    • D3D12 _ RESOURCE STATE PIXEL _ _ _ _ SHADER-RESSOURCE
    • D3D12 _ RESOURCE _ STATE _ INDIRECT _ ARGUMENT
  • Rückleseheaps müssen im Zustand D3D12 RESOURCE STATE COPY DEST gestartet _ _ _ _ werden.
  • Swap chain back buffers automatically start out in the D3D12 _ RESOURCE STATE COMMON state _ _ .

Bevor ein Heap das Ziel eines GPU-Kopiervorgangs sein kann, muss der Heap normalerweise zuerst in den Zustand D3D12 _ RESOURCE STATE COPY DEST _ übergehen. _ _ Ressourcen, die auf UPLOADheaps erstellt werden, müssen jedoch in beginnen und können sich nicht vom _ GENERISCHEN READ-Zustand ändern, da nur die CPU schreibe. Umgekehrt müssen in READBACK-Heaps erstellte Ressourcen, für die ein Commit ausgeführt wurde, in beginnen und können sich nicht vom _ COPY DEST-Zustand ändern.

Ressourcenzustandseinschränkungen für Lese-/Schreibzugriff

Die Ressourcenzustandsnutzungsbits, die zum Beschreiben eines Ressourcenzustands verwendet werden, werden in schreibgeschützte und Lese-/Schreibzustände unterteilt. Das Referenzthema für D3D12 _ RESOURCE _ STATES gibt die Lese-/Schreibzugriffsebene für jedes Bit in der Enumeration an.

Für jede Ressource kann höchstens ein Lese-/Schreibbit festgelegt werden. Wenn ein Schreibbit festgelegt ist, kann kein schreibgeschütztes Bit für diese Ressource festgelegt werden. Wenn kein Schreibbit festgelegt ist, kann eine beliebige Anzahl von Lesebits festgelegt werden.

Ressourcenzustände für die Darstellung von Puffern

Bevor ein Hintergrundpuffer angezeigt wird, muss er sich im Zustand D3D12 _ RESOURCE _ STATE COMMON _ befinden. Beachten Sie, dass der Ressourcenzustand D3D12 _ RESOURCE STATE PRESENT ein Synonym für _ _ D3D12 RESOURCE STATE COMMON ist _ und beide den Wert _ _ 0 aufweisen. Wenn IDXGISwapChain::P resent (oder IDXGISwapChain1::P resent1)für eine Ressource aufgerufen wird, die sich derzeit nicht in diesem Zustand befindet, wird eine Warnung der Debugebene ausgegeben.

Verwerfen von Ressourcen

Alle Unterressourcen in einer Ressource müssen für _ Renderziele bzw. Tiefenschablonenressourcen den Status RENDER TARGET bzw. DEPTH _ WRITE haben, wenn ID3D12GraphicsCommandList::D iscardResource aufgerufen wird.

Implizite Zustandsübergänge

Ressourcen können nur aus D3D12 RESOURCE STATE COMMON "heraufgestuft" _ _ _ werden. Ebenso werden Ressourcen nur zu D3D12 RESOURCE STATE COMMON "verfallen". _ _ _

Allgemeine Zustandsaufstufung

Alle Pufferressourcen sowie Texturen, für die das Flag D3D12 _ RESOURCE FLAG ALLOW SIMULTANEOUS ACCESS festgelegt _ _ _ _ ist, werden implizit von D3D12 _ RESOURCE STATE COMMON in den _ _ relevanten Zustand beim ersten GPU-Zugriff heraufgestuft, einschließlich GENERIC _ READ, um alle Leseszenarien abzudecken. Auf jede Ressource im Status COMMON kann so zugegriffen werden, als ob sie sich in einem einzelnen Zustand mit

1 WRITE-Flag oder 1 oder mehr READ-Flags

Ressourcen können basierend auf der folgenden Tabelle aus dem Status COMMON hergestuft werden:

Statusflag Puffer und Simultaneous-Access Texturen Texturen ohne gleichzeitigen Zugriff
SCHEITELPUNKT _ UND _ _ KONSTANTENPUFFER Ja Nein
_INDEXPUFFER Ja Nein
_RENDERZIEL Ja Nein
UNGEORDNETER _ ZUGRIFF Ja Nein
TIEFE _ SCHREIBEN Nein* Nein
_TIEFENLESEDAUER Nein* Nein
NICHT _ _ PIXEL-SHADERRESSOURCE _ Ja Ja
_ _ PIXEL-SHADERRESSOURCE Ja Ja
STREAM _ OUT Ja Nein
INDIREKTES _ ARGUMENT Ja Nein
COPY _ DEST Ja Ja
QUELLE _ KOPIEREN Ja Ja
RESOLVE _ DEST Ja Nein
RESOLVE _ SOURCE Ja Nein
PRÄDIKATION Ja Nein

*Tiefenschablonenressourcen müssen texturen ohne gleichzeitigen Zugriff sein und können daher nie implizit heraufgestuft werden.

Wenn dieser Zugriff erfolgt, verhält sich die Heraufstufung wie eine implizite Ressourcenbarriere. Bei nachfolgenden Zugriffen sind Ressourcenbarrieren erforderlich, um den Ressourcenzustand bei Bedarf zu ändern. Beachten Sie, dass die Heraufstufung von einem heraufgestuften Lesezustand in mehrere Lesezustände gültig ist, dies ist jedoch nicht der Fall bei Schreibzuständen.
Wenn z. B. eine Ressource im allgemeinen Zustand in einem Draw-Aufruf zu PIXEL SHADER RESOURCE (PIXEL _ _ SHADER-RESSOURCE) promotet wird, kann sie trotzdem zu NON_PIXEL _ _ SHADER-RESSOURCE | PIXEL _ _ SHADER-RESSOURCE in einem anderen Draw-Aufruf. Wenn sie jedoch in einem Schreibvorgang wie einem Kopierziel verwendet wird, handelt es sich hier NON_PIXEL SHADER RESOURCE | um eine Barriere für den Übergang des Ressourcenzustands von den kombinierten hergestuften _ _ Lesezuständen. DIE _ PIXEL-SHADER-RESSOURCE _ zum KOPIEREN _ DEST ist erforderlich.
Ebenso ist beim Heraufstufen von COMMON auf COPY _ DEST weiterhin eine Barriere erforderlich, um von COPY _ DEST zu RENDER TARGET übergehen zu _ können.

Beachten Sie, dass die allgemeine Zustandsaufstufung "kostenlos" ist, da die GPU keine Synchronisierungswartevorgänge ausführen muss. Die Heraufstufung stellt die Tatsache dar, dass Ressourcen im Status COMMON keine zusätzliche GPU-Arbeit oder Treibernachverfolgung erfordern sollten, um bestimmte Zugriffe zu unterstützen.

Zustandsverfall auf "Allgemein"

Die Kehrseite der allgemeinen Zustands heraufstufung ist der Verfall auf D3D12 _ RESOURCE _ STATE _ COMMON. Ressourcen, die bestimmte Anforderungen erfüllen, gelten als zustandslos und kehren effektiv in den allgemeinen Zustand zurück, wenn die GPU die Ausführung eines ExecuteCommandLists-Vorgangs beendet. Der Verfall tritt nicht zwischen Befehlslisten auf, die zusammen im gleichen ExecuteCommandLists-Aufruf ausgeführt werden.

Die folgenden Ressourcen verfällt, wenn ein ExecuteCommandLists-Vorgang auf der GPU abgeschlossen wird:

  • Ressourcen, auf die in einer Kopierwarteschlange zugegriffen wird, oder
  • Puffern von Ressourcen für einen beliebigen Warteschlangentyp oder
  • Texturressourcen für einen beliebigen Warteschlangentyp, für den das Flag D3D12 _ RESOURCE FLAG ALLOW SIMULTANEOUS ACCESS festgelegt _ _ _ _ ist, oder
  • Jede Ressource, die implizit in einen schreibgeschützten Zustand heraufgestuft wird.

Wie bei der allgemeinen Zustandsaufstufung ist der Verfall frei, da keine zusätzliche Synchronisierung erforderlich ist. Die Kombination von allgemeiner Zustandsstufung und Verfall kann dazu beitragen, viele unnötige ResourceBarrier-Übergänge zu vermeiden. In einigen Fällen kann dies zu erheblichen Leistungsverbesserungen sorgen.

Puffer und Simultaneous-Access Ressourcen verfällt in den allgemeinen Zustand, unabhängig davon, ob sie explizit mithilfe von Ressourcenbarrieren übertragen oder implizit heraufgestuft wurden.

Folgen für die Leistung

Beim Aufzeichnen expliziter ResourceBarrier-Übergänge für Ressourcen im allgemeinen Zustand ist es richtig, entweder D3D12 _ RESOURCE STATE COMMON oder einen _ _ beliebigen heraufstützbaren Zustand als BeforeState-Wert in der D3D12 _ RESOURCE TRANSITION _ _ BARRIER-Struktur zu verwenden. Dies ermöglicht die herkömmliche Zustandsverwaltung, die den automatischen Verfall von Puffern und Texturen mit gleichzeitiger Zugriffssteuerung ignoriert. Dies ist jedoch möglicherweise nicht wünschenswert, da die Vermeidung von ResourceBarrier-Übergangsaufrufen mit Ressourcen, die bekanntermaßen im allgemeinen Zustand sind, die Leistung erheblich verbessern kann. Ressourcenbarrieren können teuer sein. Sie sind dafür konzipiert, Cachelöschungen, Änderungen am Speicherlayout und andere Synchronisierungen zu erzwingen, die für Ressourcen, die sich bereits im allgemeinen Zustand befinden, möglicherweise nicht erforderlich sind. Eine Befehlsliste, die eine Ressourcenbarriere von einem nicht gängigen Zustand zu einem anderen nicht allgemeinen Zustand für eine Ressource verwendet, die sich derzeit im allgemeinen Zustand befindet, kann zu viel nicht benötigtem Mehraufwand führen.

Vermeiden Sie außerdem explizite ResourceBarrier-Übergänge zu D3D12 _ RESOURCE STATE _ _ COMMON, sofern dies nicht unbedingt erforderlich ist (z. B. befindet sich der nächste Zugriff in einer COPY-Befehlswarteschlange, die erfordert, dass ressourcen im allgemeinen Zustand beginnen). Übermäßige Übergänge in den allgemeinen Zustand können die GPU-Leistung erheblich verlangsamen.

Versuchen Sie zusammenfassend, sich auf allgemeine Zustandsaufstufung und -verfall zu verlassen, wenn ihre Semantik Ihnen den Weg freigibt, ohne ResourceBarrier-Aufrufe auszugeben.

Split Barriers

Eine Ressourcenübergangsbarriere mit dem FLAG D3D12 _ RESOURCE BARRIER FLAG BEGIN ONLY beginnt mit einer _ _ _ _ Aufteilungsbarriere, und die Übergangsbarriere wird als ausstehend bezeichnet. Während die Barriere aussteht, kann die (untergeordnete) Ressource nicht von der GPU gelesen oder geschrieben werden. Die einzige gesetzliche Übergangsbarriere, die auf eine (untergeordnete) Ressource mit einer ausstehenden Barriere angewendet werden kann, ist eine mit denselben Zuständen vor und nach und dem Flag D3D12 _ RESOURCE BARRIER FLAG END _ _ _ _ ONLY, das den ausstehenden Übergang abschließt.

Split-Barrieren geben der GPU Hinweise darauf, dass eine Ressource im Zustand A später als Nächstes in Zustand B verwendet wird. Dadurch erhält die GPU die Möglichkeit, die Übergangsworkload zu optimieren, wodurch möglicherweise Ausführungsabstände reduziert oder beseitigt werden. Das Ausstellen der barriere nur am Ende garantiert, dass alle GPU-Übergangsarbeiten abgeschlossen sind, bevor Sie mit dem nächsten Befehl fortfahren.

Die Verwendung von Split Barriers kann zur Verbesserung der Leistung beitragen, insbesondere in Szenarios mit mehreren Engines oder wenn Ressourcen in einer oder mehreren Befehlslisten nur selten gelesen/geschrieben werden.

Beispielszenario für Ressourcenbarrieren

Die folgenden Codeausschnitte zeigen die Verwendung der ResourceBarrier-Methode in einem Multithreadingbeispiel.

Erstellen der Tiefenschablonenansicht, Übergang in einen beschreibbaren Zustand.

// Create the depth stencil.
{
    CD3DX12_RESOURCE_DESC shadowTextureDesc(
        D3D12_RESOURCE_DIMENSION_TEXTURE2D,
        0,
        static_cast<UINT>(m_viewport.Width), 
        static_cast<UINT>(m_viewport.Height), 
        1,
        1,
        DXGI_FORMAT_D32_FLOAT,
        1, 
        0,
        D3D12_TEXTURE_LAYOUT_UNKNOWN,
        D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE);

    D3D12_CLEAR_VALUE clearValue;    // Performance tip: Tell the runtime at resource creation the desired clear value.
    clearValue.Format = DXGI_FORMAT_D32_FLOAT;
    clearValue.DepthStencil.Depth = 1.0f;
    clearValue.DepthStencil.Stencil = 0;

    ThrowIfFailed(m_device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &shadowTextureDesc,
        D3D12_RESOURCE_STATE_DEPTH_WRITE,
        &clearValue,
        IID_PPV_ARGS(&m_depthStencil)));

    // Create the depth stencil view.
    m_device->CreateDepthStencilView(m_depthStencil.Get(), nullptr, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
}

Erstellen der Scheitelpunktpufferansicht, zuerst ändern Sie sie von einem allgemeinen Zustand in ein Ziel, dann von einem Ziel in einen generischen lesbaren Zustand.

// Create the vertex buffer.
{
    ThrowIfFailed(m_device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::VertexDataSize),
        D3D12_RESOURCE_STATE_COPY_DEST,
        nullptr,
        IID_PPV_ARGS(&m_vertexBuffer)));

    {
        ThrowIfFailed(m_device->CreateCommittedResource(
            &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
            D3D12_HEAP_FLAG_NONE,
            &CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::VertexDataSize),
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&m_vertexBufferUpload)));

        // Copy data to the upload heap and then schedule a copy 
        // from the upload heap to the vertex buffer.
        D3D12_SUBRESOURCE_DATA vertexData = {};
        vertexData.pData = pAssetData + SampleAssets::VertexDataOffset;
        vertexData.RowPitch = SampleAssets::VertexDataSize;
        vertexData.SlicePitch = vertexData.RowPitch;

        PIXBeginEvent(commandList.Get(), 0, L"Copy vertex buffer data to default resource...");

        UpdateSubresources<1>(commandList.Get(), m_vertexBuffer.Get(), m_vertexBufferUpload.Get(), 0, 0, 1, &vertexData);
        commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_vertexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER));

        PIXEndEvent(commandList.Get());
    }

Erstellen der Indexpufferansicht, zuerst ändern Sie sie von einem allgemeinen Zustand in ein Ziel, dann von einem Ziel in einen generischen lesbaren Zustand.

// Create the index buffer.
{
    ThrowIfFailed(m_device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::IndexDataSize),
        D3D12_RESOURCE_STATE_COPY_DEST,
        nullptr,
        IID_PPV_ARGS(&m_indexBuffer)));

    {
        ThrowIfFailed(m_device->CreateCommittedResource(
            &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
            D3D12_HEAP_FLAG_NONE,
            &CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::IndexDataSize),
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&m_indexBufferUpload)));

        // Copy data to the upload heap and then schedule a copy 
        // from the upload heap to the index buffer.
        D3D12_SUBRESOURCE_DATA indexData = {};
        indexData.pData = pAssetData + SampleAssets::IndexDataOffset;
        indexData.RowPitch = SampleAssets::IndexDataSize;
        indexData.SlicePitch = indexData.RowPitch;

        PIXBeginEvent(commandList.Get(), 0, L"Copy index buffer data to default resource...");

        UpdateSubresources<1>(commandList.Get(), m_indexBuffer.Get(), m_indexBufferUpload.Get(), 0, 0, 1, &indexData);
        commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_indexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER));

        PIXEndEvent(commandList.Get());
    }

    // Initialize the index buffer view.
    m_indexBufferView.BufferLocation = m_indexBuffer->GetGPUVirtualAddress();
    m_indexBufferView.SizeInBytes = SampleAssets::IndexDataSize;
    m_indexBufferView.Format = SampleAssets::StandardIndexFormat;
}

Erstellen von Texturen und Shaderressourcenansichten. Die Textur wird von einem allgemeinen Zustand in ein Ziel und dann von einem Ziel in eine Pixel-Shaderressource geändert.

    // Create each texture and SRV descriptor.
    const UINT srvCount = _countof(SampleAssets::Textures);
    PIXBeginEvent(commandList.Get(), 0, L"Copy diffuse and normal texture data to default resources...");
    for (int i = 0; i < srvCount; i++)
    {
        // Describe and create a Texture2D.
        const SampleAssets::TextureResource &tex = SampleAssets::Textures[i];
        CD3DX12_RESOURCE_DESC texDesc(
            D3D12_RESOURCE_DIMENSION_TEXTURE2D,
            0,
            tex.Width, 
            tex.Height, 
            1,
            static_cast<UINT16>(tex.MipLevels),
            tex.Format,
            1, 
            0,
            D3D12_TEXTURE_LAYOUT_UNKNOWN,
            D3D12_RESOURCE_FLAG_NONE);

        ThrowIfFailed(m_device->CreateCommittedResource(
            &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
            D3D12_HEAP_FLAG_NONE,
            &texDesc,
            D3D12_RESOURCE_STATE_COPY_DEST,
            nullptr,
            IID_PPV_ARGS(&m_textures[i])));

        {
            const UINT subresourceCount = texDesc.DepthOrArraySize * texDesc.MipLevels;
            UINT64 uploadBufferSize = GetRequiredIntermediateSize(m_textures[i].Get(), 0, subresourceCount);
            ThrowIfFailed(m_device->CreateCommittedResource(
                &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
                D3D12_HEAP_FLAG_NONE,
                &CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize),
                D3D12_RESOURCE_STATE_GENERIC_READ,
                nullptr,
                IID_PPV_ARGS(&m_textureUploads[i])));

            // Copy data to the intermediate upload heap and then schedule a copy 
            // from the upload heap to the Texture2D.
            D3D12_SUBRESOURCE_DATA textureData = {};
            textureData.pData = pAssetData + tex.Data->Offset;
            textureData.RowPitch = tex.Data->Pitch;
            textureData.SlicePitch = tex.Data->Size;

            UpdateSubresources(commandList.Get(), m_textures[i].Get(), m_textureUploads[i].Get(), 0, 0, subresourceCount, &textureData);
            commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_textures[i].Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
        }

        // Describe and create an SRV.
        D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
        srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
        srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        srvDesc.Format = tex.Format;
        srvDesc.Texture2D.MipLevels = tex.MipLevels;
        srvDesc.Texture2D.MostDetailedMip = 0;
        srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
        m_device->CreateShaderResourceView(m_textures[i].Get(), &srvDesc, cbvSrvHandle);

        // Move to the next descriptor slot.
        cbvSrvHandle.Offset(cbvSrvDescriptorSize);
    }

Anfang eines Rahmens; hierbei wird nicht nur ResourceBarrier verwendet, um anzugeben, dass der Backbuffer als Renderziel verwendet werden soll, sondern auch die Frameressource initialisiert (die ResourceBarrier für den Tiefenschablonenpuffer aufruft).

// Assemble the CommandListPre command list.
void D3D12Multithreading::BeginFrame()
{
    m_pCurrentFrameResource->Init();

    // Indicate that the back buffer will be used as a render target.
    m_pCurrentFrameResource->m_commandLists[CommandListPre]->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

    // Clear the render target and depth stencil.
    const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
    m_pCurrentFrameResource->m_commandLists[CommandListPre]->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
    m_pCurrentFrameResource->m_commandLists[CommandListPre]->ClearDepthStencilView(m_dsvHeap->GetCPUDescriptorHandleForHeapStart(), D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);

    ThrowIfFailed(m_pCurrentFrameResource->m_commandLists[CommandListPre]->Close());
}

// Assemble the CommandListMid command list.
void D3D12Multithreading::MidFrame()
{
    // Transition our shadow map from the shadow pass to readable in the scene pass.
    m_pCurrentFrameResource->SwapBarriers();

    ThrowIfFailed(m_pCurrentFrameResource->m_commandLists[CommandListMid]->Close());
}

Beenden eines Frames, der angibt, dass der Hintergrundpuffer jetzt zur Darstellung verwendet wird.

// Assemble the CommandListPost command list.
void D3D12Multithreading::EndFrame()
{
    m_pCurrentFrameResource->Finish();

    // Indicate that the back buffer will now be used to present.
    m_pCurrentFrameResource->m_commandLists[CommandListPost]->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));

    ThrowIfFailed(m_pCurrentFrameResource->m_commandLists[CommandListPost]->Close());
}

Beim Initialisieren einer Frameressource, die beim Start eines Frames aufgerufen wird, wird der Tiefenschablonenpuffer in schreibbar übergestellt.

void FrameResource::Init()
{
    // Reset the command allocators and lists for the main thread.
    for (int i = 0; i < CommandListCount; i++)
    {
        ThrowIfFailed(m_commandAllocators[i]->Reset());
        ThrowIfFailed(m_commandLists[i]->Reset(m_commandAllocators[i].Get(), m_pipelineState.Get()));
    }

    // Clear the depth stencil buffer in preparation for rendering the shadow map.
    m_commandLists[CommandListPre]->ClearDepthStencilView(m_shadowDepthView, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);

    // Reset the worker command allocators and lists.
    for (int i = 0; i < NumContexts; i++)
    {
        ThrowIfFailed(m_shadowCommandAllocators[i]->Reset());
        ThrowIfFailed(m_shadowCommandLists[i]->Reset(m_shadowCommandAllocators[i].Get(), m_pipelineStateShadowMap.Get()));

        ThrowIfFailed(m_sceneCommandAllocators[i]->Reset());
        ThrowIfFailed(m_sceneCommandLists[i]->Reset(m_sceneCommandAllocators[i].Get(), m_pipelineState.Get()));
    }
}

Barrieren werden in der Mitte des Frames getauscht, wodurch die Schattenkarte von schreibbar in lesbar übergehen kann.

void FrameResource::SwapBarriers()
{
    // Transition the shadow map from writeable to readable.
    m_commandLists[CommandListMid]->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_shadowTexture.Get(), D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
}

Finish wird aufgerufen, wenn ein Frame beendet wird, wodurch die Schattenkarte in einen allgemeinen Zustand übergehen kann.

void FrameResource::Finish()
{
    m_commandLists[CommandListPost]->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_shadowTexture.Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE));
}

Allgemeines Beispiel für Zustandsaufstufung und -verfall

    // Create a buffer resource using D3D12_RESOURCE_STATE_COMMON as the init state
    ID3D12Resource *pResource;
    CreateCommittedVertexBufferInCommonState(1024, &pResource);

    // Copy data to the buffer without the need for a barrier.
    // Promotes pResource state to D3D12_RESOURCE_STATE_COPY_DEST.
    pCommandList->CopyBufferRegion(pResource, 0, pOtherResource, 0, 1024); 

    // To use pResource as a vertex buffer a transition barrier is needed.
    // Note the StateBefore is D3D12_RESOURCE_STATE_COPY_DEST.
    D3D12_RESOURCE_BARRIER BarrierDesc = {};
    BarrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    BarrierDesc.Transition.pResource = pResource;
    BarrierDesc.Transition.Subresource = 0;
    BarrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
    BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
    pCommandList->ResourceBarrier( 1, &BarrierDesc );

    // Use pResource as a vertex buffer
    D3D12_VERTEX_BUFFER_VIEW vbView;
    vbView.BufferLocation = pResource->GetGPUVirtualAddress();
    vbView.SizeInBytes = 1024;
    vbView.StrideInBytes = sizeof(MyVertex);

    pCommandList->IASetVertexBuffers(0, 1, &vbView);
    pCommandList->Draw();

    pCommandQueue->ExecuteCommandLists(1, &pCommandList); 
    pCommandList->Reset(pAllocator, pPipelineState);

    // Since the reset command list will be executed in a separate call to 
    // ExecuteCommandLists, the previous state of pResource
    // will have decayed to D3D12_RESOURCE_STATE_COMMON so, again, no barrier is needed
    pCommandList->CopyBufferRegion(pResource, 0, pDifferentResource, 0, 1024);

    FinishRecordingCommandList(pCommandList);
    pCommandQueue->ExecuteCommandLists(1, &pCommandList); 

    WaitForQueue(pCommandQueue);

    // The previous ExecuteCommandLists call has finished so 
    // pResource has decayed to D3D12_RESOURCE_STATE_COMMON

Beispiel für geteilte Barrieren

Im folgenden Beispiel wird gezeigt, wie Sie eine Teilungsbarriere verwenden, um Pipelinesperren zu reduzieren. Der folgende Code verwendet keine geteilten Barrieren:

 D3D12_RESOURCE_BARRIER BarrierDesc = {};
    BarrierDesc.Type = D3D12_RESOURCE_BARRIER_TRANSITION;
    BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_NONE;
    BarrierDesc.Transition.pResource = pResource;
    BarrierDesc.Transition.Subresource = 0;
    BarrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
    BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;

    pCommandList->ResourceBarrier( 1, &BarrierDesc );
    
    Write(pResource); // ... render to pResource
    OtherStuff(); // .. other gpu work

    // Transition pResource to PIXEL_SHADER_RESOURCE
    BarrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
    BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
    
    pCommandList->ResourceBarrier( 1, &BarrierDesc );

    Read(pResource); // ... read from pResource

Im folgenden Code werden Getrennte Barrieren verwendet:

D3D12_RESOURCE_BARRIER BarrierDesc = {};
    BarrierDesc.Type = D3D12_RESOURCE_BARRIER_TRANSITION;
    BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_NONE;
    BarrierDesc.Transition.pResource = pResource;
    BarrierDesc.Transition.Subresource = 0;
    BarrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
    BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;

    pCommandList->ResourceBarrier( 1, &BarrierDesc );
    
    Write(pResource); // ... render to pResource

    // Done writing to pResource. Start barrier to PIXEL_SHADER_RESOURCE and
    // then do other work
    BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_BEGIN_ONLY;
    BarrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
    BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
    pCommandList->ResourceBarrier( 1, &BarrierDesc );

    OtherStuff(); // .. other gpu work

    // Need to read from pResource so end barrier
    BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_END_ONLY;

    pCommandList->ResourceBarrier( 1, &BarrierDesc );
    Read(pResource); // ... read from pResource

Videotutorials für erweitertes DirectX-Lernen: Ressourcenbarrieren und Zustandsnachverfolgung

Synchronisierung mit mehreren Modulen

Arbeitsübermittlung in Direct3D 12

Ein Blick in D3D12-Ressourcenzustandsbarrieren