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

Um die gesamte CPU-Auslastung zu reduzieren und Treiber-Multithreading und -Vorverarbeitung zu ermöglichen, verschiebt Direct3D 12 die Verantwortung für die Verwaltung des Ressourcenstatus vom Grafiktreiber auf die Anwendung. Ein Beispiel für den Ressourcenstatus ist, ob auf eine Texturressource derzeit über eine Shaderressourcenansicht oder als Renderzielansicht zugegriffen wird. In Direct3D 11 mussten Fahrer diesen Zustand im Hintergrund nachverfolgen. Dies ist aus CPU-Sicht teuer und erschwert jede Art von Multithreaddesign erheblich. In Microsoft Direct3D 12 wird der meiste Ressourcenstatus von der Anwendung mit einer einzigen API verwaltet, ID3D12GraphicsCommandList::ResourceBarrier.

Verwenden der ResourceBarrier-API zum Verwalten des Ressourcenstatus

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

Es gibt drei Arten von Ressourcenbarrieren:

  • Übergangsbarriere : Eine Übergangsbarriere gibt an, dass eine Gruppe von Unterressourcen zwischen verschiedenen Nutzungen wechselt. Eine D3D12_RESOURCE_TRANSITION_BARRIER-Struktur wird verwendet, um die Unterressource anzugeben, die übergangen wird, sowie die Vor - und Nachher-Zustände der Unterressource.

    Das System überprüft, ob Unterressourcenübergänge in einer Befehlsliste mit früheren Übergängen in derselben Befehlsliste konsistent sind. Die Debugebene verfolgt den Unterressourcenstatus weiter nach, um andere Fehler zu finden, aber diese Überprüfung ist konservativ 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 übergangen werden.

  • Aliasbarriere : Eine Aliasbarriere zeigt einen Übergang zwischen der Verwendung von zwei verschiedenen Ressourcen an, die sich überlappende Zuordnungen in denselben 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 Nachressource anzugeben.

    Beachten Sie, dass eine oder beide Ressourcen NULL sein können, was angibt, dass jede gekachelte Ressource Aliasing verursachen kann. Weitere Informationen zur Verwendung gekachelter Ressourcen finden Sie unter Gekachelte Ressourcen und volume tiled Resources.

  • UAV-Barriere (Unordered Access View) – Eine UAV-Barriere gibt an, dass alle UAV-Zugriffe auf eine bestimmte Ressource, sowohl Lese- als auch Schreibzugriffe, 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 Zeichen- oder Absendungsaufrufen platziert, die nur aus einer UAV gelesen werden. 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 angibt, dass jeder UAV-Zugriff die Barriere erfordern könnte.

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

Zu einem bestimmten Zeitpunkt befindet sich eine Unterressource in genau einem Zustand, der durch den Satz von D3D12_RESOURCE_STATES Flags bestimmt wird, die für ResourceBarrier bereitgestellt werden. Die Anwendung muss sicherstellen, dass die Vor - und Nachher-Zustände von aufeinander folgenden Aufrufen von ResourceBarrier zustimmen.

Tipp

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

 

Ressourcenstatus

Eine vollständige Liste der Ressourcenzustände, zwischen denen eine Ressource wechseln 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 erstellt werden (gültig für die Ressourcenbeschreibung), mit den folgenden Ausnahmen:

  • Upload-Heaps müssen im Zustand D3D12_RESOURCE_STATE_GENERIC_READ beginnen, der eine bitweise ODER Kombination aus ist:
    • D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER
    • D3D12_RESOURCE_STATE_INDEX_BUFFER
    • D3D12_RESOURCE_STATE_COPY_SOURCE
    • D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE
    • D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
    • D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT
  • Readback-Heaps müssen im zustand D3D12_RESOURCE_STATE_COPY_DEST beginnen.
  • Swap Chain Back-Puffer starten automatisch im D3D12_RESOURCE_STATE_COMMON Zustand.

Bevor ein Heap das Ziel eines GPU-Kopiervorgangs sein kann, muss der Heap normalerweise zuerst in den D3D12_RESOURCE_STATE_COPY_DEST Zustand übergehen. Ressourcen, die für UPLOAD-Heaps erstellt wurden, müssen jedoch in beginnen und können nicht aus dem GENERIC_READ Zustand wechseln, da nur die CPU schreibt. Umgekehrt müssen committe Ressourcen, die in READBACK-Heaps erstellt werden, in beginnen und können sich nicht aus dem COPY_DEST-Zustand ändern.

Lese-/Schreibstatuseinschränkungen für Ressourcen

Die Ressourcenzustandsverwendungsbits, die zum Beschreiben eines Ressourcenzustands verwendet werden, sind in schreibgeschützten und Lese-/Schreibstatus unterteilt. Das Referenzthema für die D3D12_RESOURCE_STATES gibt die Lese-/Schreibzugriffsebene für jedes Bit in der Enumeration an.

Höchstens ein Lese-/Schreibbit kann für eine Ressource festgelegt werden. Wenn ein Schreibbit festgelegt ist, kann für diese Ressource kein schreibgeschütztes Bit 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 Backpuffer angezeigt wird, muss er sich im D3D12_RESOURCE_STATE_COMMON Zustand befinden. Beachten Sie, dass der Ressourcenstatus 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 Debugebenenwarnung ausgegeben.

Verwerfen von Ressourcen

Alle Unterressourcen in einer Ressource müssen sich für Renderziele bzw. Tiefenschablonenressourcen im zustand RENDER_TARGET oder DEPTH_WRITE befinden, wenn ID3D12GraphicsCommandList::D iscardResource aufgerufen wird.

Implizite Zustandsübergänge

Ressourcen können nur aus D3D12_RESOURCE_STATE_COMMON "heraufgestuft" werden. Auf ähnliche Weise werden Ressourcen nur auf D3D12_RESOURCE_STATE_COMMON "verfallen".

Allgemeine Zustandsaufstufung

Alle Pufferressourcen sowie Texturen mit dem D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS-Flags werden beim ersten GPU-Zugriff implizit von D3D12_RESOURCE_STATE_COMMON in den relevanten Zustand heraufgestuft, einschließlich GENERIC_READ, um jedes Leseszenario abzudecken. Auf jede Ressource im COMMON-Zustand kann wie in einem einzigen Zustand mit zugegriffen werden.

1 WRITE-Flag oder 1 oder mehr READ-Flags

Ressourcen können basierend auf der folgenden Tabelle vom STATUS COMMON heraufgestuft werden:

Statusflag Puffer und Simultaneous-Access Texturen Texturen ohne gleichzeitigen Zugriff
VERTEX_AND_CONSTANT_BUFFER Ja Nein
INDEX_BUFFER Ja Nein
RENDER_TARGET Ja Nein
UNORDERED_ACCESS Ja Nein
DEPTH_WRITE Nein* Nein
DEPTH_READ Nein* Nein
NON_PIXEL_SHADER_RESOURCE Ja Ja
PIXEL_SHADER_RESOURCE Ja Ja
STREAM_OUT Ja Nein
INDIRECT_ARGUMENT Ja Nein
COPY_DEST Ja Ja
COPY_SOURCE Ja Ja
RESOLVE_DEST Ja Nein
RESOLVE_SOURCE Ja Nein
PRÄDICATION Ja Nein

 

*Tiefenschablonenressourcen müssen Texturen ohne gleichzeitigen Zugriff sein und können daher niemals implizit höhergestuft werden.

Wenn dieser Zugriff erfolgt, verhält sich die Heraufstufung wie eine implizite Ressourcenbarriere. Für nachfolgende Zugriffe sind Ressourcenbarrieren erforderlich, um den Ressourcenzustand bei Bedarf zu ändern. Beachten Sie, dass die Heraufstufung von einem höhergestuften Lesezustand in den Zustand mehrerer Lesevorgänge gültig ist, dies ist jedoch für Schreibzustände nicht der Fall.
Wenn beispielsweise eine Ressource im allgemeinen Zustand in PIXEL_SHADER_RESOURCE in einem Draw-Aufruf höhergestuft wird, kann sie trotzdem zu NON_PIXEL_SHADER_RESOURCE | PIXEL_SHADER_RESOURCE in einem anderen Draw-Aufruf. Wenn sie jedoch in einem Schreibvorgang verwendet wird, z. B. einem Kopierziel, einer Ressourcenzustandsübergangsbarriere aus den kombinierten höhergestuften Lesezuständen, NON_PIXEL_SHADER_RESOURCE | PIXEL_SHADER_RESOURCE ist erforderlich, um COPY_DEST.
Wenn sie von COMMON auf COPY_DEST heraufgestuft wird, ist weiterhin eine Barriere erforderlich, um von COPY_DEST zu RENDER_TARGET.

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

Zustandsverfall zu allgemein

Die Kehrseite der Allgemeinen Zustandsaufstufung wird auf D3D12_RESOURCE_STATE_COMMON zurückverklingt. 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. Zwischen Befehlslisten, die im gleichen ExecuteCommandLists-Aufruf zusammen ausgeführt werden, tritt kein Verfall auf.

Die folgenden Ressourcen verfallen, wenn ein ExecuteCommandLists-Vorgang auf der GPU abgeschlossen ist:

  • Ressourcen, auf die in einer Kopierwarteschlange zugegriffen wird, oder
  • Pufferressourcen für einen beliebigen Warteschlangentyp oder
  • Texturressourcen für jeden Warteschlangentyp, für den das flag D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS festgelegt ist, oder
  • Jede Ressource, die implizit in einen schreibgeschützten Zustand höhergestuft wird.

Wie die allgemeine Zustandsaufstufung ist der Verfall frei, da keine zusätzliche Synchronisierung erforderlich ist. Die Kombination von allgemeiner Statusaufstufung und -verfall kann dazu beitragen, viele unnötige ResourceBarrier-Übergänge zu vermeiden. In einigen Fällen kann dies zu erheblichen Leistungsverbesserungen führen.

Puffer und Simultaneous-Access Ressourcen verfallen in den gemeinsamen Zustand, unabhängig davon, ob sie explizit mithilfe von Ressourcenbarrieren überwechselt 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 promotable-Zustand als BeforeState-Wert in der D3D12_RESOURCE_TRANSITION_BARRIER-Struktur zu verwenden. Dies ermöglicht eine herkömmliche Zustandsverwaltung, die den automatischen Verfall von Puffern und Texturen für gleichzeitigen Zugriff ignoriert. Dies ist jedoch möglicherweise nicht wünschenswert, da das Vermeiden von ResourceBarrier-Übergangsaufrufen mit Ressourcen, von denen bekannt ist, dass sie sich im allgemeinen Zustand befinden, die Leistung erheblich verbessern kann. Ressourcenbarrieren können teuer sein. Sie wurden entwickelt, um Cacheleerungen, Änderungen des Speicherlayouts und andere Synchronisierungen zu erzwingen, die möglicherweise nicht für Ressourcen erforderlich sind, die sich bereits im allgemeinen Zustand befinden. Eine Befehlsliste, die eine Ressourcenbarriere von einem nicht allgemeinen Zustand in einen anderen nicht gemeinsamen Zustand für eine Ressource verwendet, die sich derzeit im gemeinsamen Zustand befindet, kann viel unnötigen Mehraufwand verursachen.

Vermeiden Sie außerdem explizite ResourceBarrier-Übergänge zu D3D12_RESOURCE_STATE_COMMON, es sei denn, es ist unbedingt erforderlich (z. B. erfolgt der nächste Zugriff auf eine 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.

Zusammenfassend können Sie versuchen, sich auf allgemeine Zustandsaufstufung und -verfallen zu verlassen, wann immer die Semantik es Ihnen ermöglicht, ohne ResourceBarrier-Aufrufe auszugeben.

Geteilte Barrieren

Eine Ressourcenübergangsbarriere mit dem Flag D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY beginnt eine Split-Barriere, und die Übergangsbarriere wird als ausstehend bezeichnet. Während die Barriere aussteht, kann die (Unter-)Ressource nicht von der GPU gelesen oder geschrieben werden. Die einzige rechtliche Übergangsbarriere, die auf eine (Sub-)Ressource mit einer ausstehenden Barriere angewendet werden kann, ist eine mit demselben Vor - und Nachher-Status und dem D3D12_RESOURCE_BARRIER_FLAG_END_ONLY Flag, das den anstehenden Übergang abschließt.

Split Barrieren geben der GPU Hinweise darauf, dass eine Ressource im Zustand A später im Zustand B verwendet wird. Dadurch hat die GPU die Möglichkeit, die Übergangsworkload zu optimieren und möglicherweise Ausführungshinderungen zu reduzieren oder zu beseitigen. Wenn Sie die End-only-Barriere ausgeben, wird sichergestellt, dass alle GPU-Übergangsarbeiten abgeschlossen sind, bevor Sie mit dem nächsten Befehl übergehen.

Die Verwendung von Geteilten Barrieren kann dazu beitragen, die Leistung zu verbessern, insbesondere in Szenarien mit mehreren Engines oder bei denen Ressourcen nur spärlich über eine oder mehrere Befehlslisten hinweg gelesen/geschrieben werden.

Beispielszenario für Ressourcenbarrieren

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

Erstellen der Tiefenschablonenansicht und Überführung in einen schreibbaren 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 sie die Scheitelpunktpufferansicht, und ändern Sie sie zuerst 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 sie die Indexpufferansicht, und ändern Sie sie zuerst 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 Shaderressourcensichten 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);
    }

Beginnen eines Frames; dabei wird nicht nur ResourceBarrier verwendet, um anzugeben, dass der Backbuffer als Renderziel verwendet werden soll, sondern auch die Frameressource (die ResourceBarrier im 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 Backpuffer jetzt für die 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 Starten 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 Bilds getauscht, wodurch die Schattenkarte von schreibbar in lesbar überwechselt wird.

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 gemeinsamen Zustand übergehen wird.

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

Beispiel für allgemeine 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

Das folgende Beispiel zeigt, wie Sie eine geteilte Barriere 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

Der folgende Code verwendet geteilte 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

    // 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

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

Synchronisierung mit mehreren Modulen

Arbeitsübermittlung in Direct3D 12

Ein Blick in die D3D12-Ressourcenzustandsbarrieren