Uso delle barriere delle risorse per sincronizzare gli stati delle risorse in Direct3D 12

Per ridurre l'utilizzo complessivo della CPU e abilitare il multi-threading e la pre-elaborazione del driver, Direct3D 12 sposta la responsabilità della gestione dello stato per risorsa dal driver grafico all'applicazione. Un esempio di stato per risorsa è se una risorsa trama è attualmente accessibile come tramite una visualizzazione risorsa shader o come visualizzazione destinazione di rendering. In Direct3D 11, i driver sono stati necessari per tenere traccia di questo stato in background. Questo è costoso dal punto di vista della CPU e complica significativamente qualsiasi tipo di progettazione multi-thread. In Microsoft Direct3D 12 la maggior parte dello stato per risorsa viene gestita dall'applicazione con un'unica API, ID3D12GraphicsCommandList::ResourceBarrier.

Uso dell'API ResourceBarrier per gestire lo stato per risorsa

ResourceBarrier informa il driver grafico di situazioni in cui il driver potrebbe dover sincronizzare più accessi alla memoria in cui viene archiviata una risorsa. Il metodo viene chiamato con una o più strutture di descrizione della barriera delle risorse che indicano il tipo di barriera di risorse dichiarata.

Esistono tre tipi di barriere alle risorse:

  • Barriera di transizione : una barriera di transizione indica che un set di sottorisorse passa tra diversi usi. Una struttura di D3D12_RESOURCE_TRANSITION_BARRIER viene usata per specificare la sottoresource che sta passando, nonché gli stati precedenti e successivi dell'oggetto subresource.

    Il sistema verifica che le transizioni di sottoresource in un elenco di comandi siano coerenti con le transizioni precedenti nello stesso elenco di comandi. Il livello di debug tiene traccia dello stato di sottoresource per trovare altri errori, tuttavia questa convalida è conservativa e non esaustiva.

    Si noti che è possibile usare il flag di D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES per specificare che tutte le sottoresource all'interno di una risorsa vengono transizioni.

  • Barriera di aliasing: una barriera di aliasing indica una transizione tra usi di due risorse diverse che hanno mapping sovrapposti nello stesso heap. Questo vale sia per le risorse riservate che per le risorse inserite. Una struttura D3D12_RESOURCE_ALIASING_BARRIER viene usata per specificare sia la risorsa prima che la risorsa dopo .

    Si noti che una o entrambe le risorse possono essere NULL, che indica che qualsiasi risorsa riquadri potrebbe causare l'aliasing. Per altre informazioni sull'uso delle risorse riquadri, vedere Risorse riquadri e Risorse riquadridel volume.

  • Barriera UAV (Unrdered Access View): una barriera UAV indica che tutti gli accessi UAV, sia in lettura che in scrittura, a una determinata risorsa devono completare tra qualsiasi accesso UAV futuro, sia lettura che scrittura. Non è necessario che un'app inserisca una barriera UAV tra due chiamate di disegno o di invio che leggono solo da un UAV. Inoltre, non è necessario mettere una barriera UAV tra due chiamate di disegno o invio che scrivono nello stesso UAV se l'applicazione sa che è sicuro eseguire l'accesso UAV in qualsiasi ordine. Una struttura D3D12_RESOURCE_UAV_BARRIER viene usata per specificare la risorsa UAV a cui si applica la barriera. L'applicazione può specificare NULL per l'UAV della barriera, che indica che qualsiasi accesso UAV potrebbe richiedere la barriera.

Quando ResourceBarrier viene chiamato con una matrice di descrizioni delle barriere di risorse, l'API si comporta come se fosse chiamata una volta per ogni elemento, nell'ordine in cui sono stati forniti.

In qualsiasi momento, una sottoresource si trova esattamente in uno stato, determinato dal set di flag D3D12_RESOURCE_STATES forniti a ResourceBarrier. L'applicazione deve assicurarsi che gli stati precedenti e successivi delle chiamate consecutive a ResourceBarrier accettino.

Suggerimento

Le applicazioni devono eseguire più transizioni in una chiamata API ovunque possibile.

 

Stati delle risorse

Per l'elenco completo degli stati delle risorse tra cui una risorsa può passare, vedere l'argomento di riferimento per l'enumerazione D3D12_RESOURCE_STATES .

Per le barriere delle risorse suddivise, vedere anche D3D12_RESOURCE_BARRIER_FLAGS.

Stati iniziali per le risorse

Le risorse possono essere create con qualsiasi stato iniziale specificato dall'utente (valido per la descrizione della risorsa), con le eccezioni seguenti:

  • L'heaps di caricamento deve iniziare nello stato D3D12_RESOURCE_STATE_GENERIC_READ che è una combinazione OR bit per bit di:
    • 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
  • Gli heaps di readback devono iniziare nello stato di D3D12_RESOURCE_STATE_COPY_DEST.
  • I buffer back della catena di scambio iniziano automaticamente nello stato di D3D12_RESOURCE_STATE_COMMON.

Prima che un heap possa essere la destinazione di un'operazione di copia GPU, normalmente l'heap deve essere prima di tutto passato allo stato di D3D12_RESOURCE_STATE_COPY_DEST. Tuttavia, le risorse create in UPLOAD heaps devono iniziare e non possono cambiare dallo stato GENERIC_READ perché solo la CPU eseguirà la scrittura. Al contrario, le risorse di commit create negli heaps READBACK devono iniziare e non possono cambiare dallo stato COPY_DEST.

Restrizioni sullo stato delle risorse di lettura/scrittura

I bit di utilizzo dello stato delle risorse usati per descrivere uno stato della risorsa sono suddivisi in stati di sola lettura e lettura/scrittura. L'argomento di riferimento per la D3D12_RESOURCE_STATES indica il livello di accesso in lettura/scrittura per ogni bit nell'enumerazione.

Al massimo, è possibile impostare un solo bit di lettura/scrittura per qualsiasi risorsa. Se viene impostato un bit di scrittura, non è possibile impostare un bit di sola lettura per tale risorsa. Se non viene impostato alcun bit di scrittura, è possibile impostare qualsiasi numero di bit di lettura.

Stati delle risorse per la presentazione di buffer indietro

Prima che venga presentato un buffer back, deve trovarsi nello stato di D3D12_RESOURCE_STATE_COMMON. Nota, lo stato della risorsa D3D12_RESOURCE_STATE_PRESENT è un sinonimo di D3D12_RESOURCE_STATE_COMMON e entrambi hanno un valore pari a 0. Se IDXGISwapChain::P resent (o IDXGISwapChain1::P resent1) viene chiamato su una risorsa che non è attualmente in questo stato, viene generato un avviso del livello di debug.

Eliminazione delle risorse

Tutte le sottorisorse in una risorsa devono trovarsi nello stato RENDER_TARGET o DEPTH_WRITE, rispettivamente per le risorse di destinazione/profondità/stencil di rendering, quando viene chiamato ID3D12GraphicsCommandList::D iscardResource .

Transizioni di stato implicite

Le risorse possono essere "promosse" solo dall'D3D12_RESOURCE_STATE_COMMON. Analogamente, le risorse verranno "decadite" solo per D3D12_RESOURCE_STATE_COMMON.

Promozione dello stato comune

Tutte le risorse del buffer e le trame con il set di flag di D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS vengono promosse in modo implicito da D3D12_RESOURCE_STATE_COMMON allo stato pertinente per il primo accesso alla GPU, incluse le GENERIC_READ per coprire qualsiasi scenario di lettura. Qualsiasi risorsa nello stato COMMON può essere accessibile come in uno stato singolo con

1 Flag WRITE o 1 o più flag READ

Le risorse possono essere promosse dallo stato COMMON in base alla tabella seguente:

Flag di stato Buffer e trame Simultaneous-Access Trame di accesso non simultanee
VERTEX_AND_CONSTANT_BUFFER No
INDEX_BUFFER No
RENDER_TARGET No
UNORDERED_ACCESS No
DEPTH_WRITE No* No
DEPTH_READ No* No
NON_PIXEL_SHADER_RESOURCE
PIXEL_SHADER_RESOURCE
STREAM_OUT No
INDIRECT_ARGUMENT No
COPY_DEST
COPY_SOURCE
RESOLVE_DEST No
RESOLVE_SOURCE No
PREDICAZIONE No

 

*Le risorse depth-stencil devono essere trame ad accesso non simultaneo e quindi non possono mai essere promosse in modo implicito.

Quando si verifica questo accesso, l'innalzamento di livello agisce come una barriera di risorse implicita. Per gli accessi successivi, saranno necessarie barriere alle risorse per modificare lo stato della risorsa, se necessario. Si noti che l'innalzamento di livello da uno stato di lettura alzato di livello in più stati di lettura è valido, ma questo non è il caso per gli stati di scrittura.
Ad esempio, se una risorsa nello stato comune viene promossa a PIXEL_SHADER_RESOURCE in una chiamata Draw, può comunque essere promossa a NON_PIXEL_SHADER_RESOURCE | PIXEL_SHADER_RESOURCE in un'altra chiamata Draw. Tuttavia, se viene usato in un'operazione di scrittura, ad esempio una destinazione di copia, una barriera di transizione dello stato della risorsa dagli stati di lettura promossi combinati, qui NON_PIXEL_SHADER_RESOURCE | PIXEL_SHADER_RESOURCE, per COPY_DEST necessario.
Analogamente, se promossa da COMMON a COPY_DEST, è comunque necessaria una barriera per passare da COPY_DEST a RENDER_TARGET.

Si noti che l'innalzamento di stato comune è "gratuito" in quanto non è necessario che la GPU esegua alcuna attesa di sincronizzazione. L'innalzamento di livello rappresenta il fatto che le risorse nello stato COMMON non devono richiedere attività aggiuntive della GPU o il rilevamento dei driver per supportare determinati accessi.

Decadimento dello stato in comune

Il lato capovolgimento della promozione dello stato comune è di nuovo decadimento a D3D12_RESOURCE_STATE_COMMON. Le risorse che soddisfano determinati requisiti sono considerate senza stato e restituiscono efficacemente allo stato comune quando la GPU termina l'esecuzione di un'operazione ExecuteCommandLists . Il decadimento non si verifica tra gli elenchi di comandi eseguiti insieme nella stessa chiamata ExecuteCommandLists .

Le risorse seguenti decadono quando viene completata un'operazione ExecuteCommandLists nella GPU:

  • Risorse a cui si accede in una coda di copia o
  • Memorizzare nel buffer le risorse in qualsiasi tipo di coda o
  • Risorse trama in qualsiasi tipo di coda con il flag D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS impostato o
  • Qualsiasi risorsa alzata in modo implicito a uno stato di sola lettura.

Come la promozione dello stato comune, il decadimento è gratuito in quanto non è necessaria alcuna sincronizzazione aggiuntiva. La combinazione di promozione e decadimento dello stato comune può contribuire a eliminare molte transizioni ResourceBarrier non necessarie. In alcuni casi, ciò può offrire miglioramenti significativi delle prestazioni.

I buffer e le risorse Simultaneous-Access decadono allo stato comune indipendentemente dal fatto che siano stati trasferiti in modo esplicito usando barriere di risorse o promossi in modo implicito.

Implicazioni per le prestazioni

Quando si registrano transizioni ResourceBarrier esplicite sulle risorse nello stato comune, è corretto usare D3D12_RESOURCE_STATE_COMMON o qualsiasi stato promozionale come valore BeforeState nella struttura D3D12_RESOURCE_TRANSITION_BARRIER. Ciò consente la gestione dello stato tradizionale che ignora il decadimento automatico dei buffer e le trame di accesso simultaneo. Ciò potrebbe non essere auspicabile, poiché evitare chiamate ResourceBarrier di transizione con risorse note allo stato comune può migliorare significativamente le prestazioni. Le barriere alle risorse possono essere costose. Sono progettati per forzare lo scaricamento della cache, le modifiche del layout della memoria e altre sincronizzazioni che potrebbero non essere necessarie per le risorse già nello stato comune. Un elenco di comandi che usa una barriera di risorse da uno stato non comune a un altro stato non comune in una risorsa attualmente nello stato comune può introdurre un sovraccarico non necessario.

Evitare inoltre transizioni esplicite di ResourceBarrier a D3D12_RESOURCE_STATE_COMMON a meno che non sia assolutamente necessario (ad esempio, l'accesso successivo si trova in una coda di comandi COPY che richiede un inizio delle risorse nello stato comune). Transizioni eccessive allo stato comune possono rallentare notevolmente le prestazioni della GPU.

In sintesi, provare a fare affidamento su promozione e decadimento dello stato comune ogni volta che la semantica consente di uscire senza eseguire chiamate ResourceBarrier .

Barriere suddivise

Una barriera di transizione delle risorse con il flag di D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY inizia una barriera divisa e si dice che la barriera di transizione sia in sospeso. Mentre la barriera è in sospeso, la risorsa (sotto)non può essere letta o scritta dalla GPU. L'unica barriera legale di transizione che può essere applicata a una risorsa (sub)con una barriera in sospeso è una con lo stesso stato precedente e successivo e il flag di D3D12_RESOURCE_BARRIER_FLAG_END_ONLY, che completa la transizione in sospeso.

Le barriere suddivise forniscono suggerimenti per la GPU che una risorsa nello stato A verrà usata successivamente nello stato B . In questo modo la GPU offre la possibilità di ottimizzare il carico di lavoro di transizione, riducendo o eliminando blocchi di esecuzione. L'emissione della barriera di sola fine garantisce che tutte le operazioni di transizione della GPU vengano completate prima di passare al comando successivo.

L'uso di barriere suddivise consente di migliorare le prestazioni, in particolare negli scenari multi-motore o in cui le risorse vengono in lettura/scrittura in modo sparse in uno o più elenchi di comandi.Using split barriers can help to improve performance, specialmente in multi-engine scenarios or where resources are read/write transitioned sparse in one or more command list.

Scenario di esempio di barriera di risorse

I frammenti di codice seguenti illustrano l'uso del metodo ResourceBarrier in un esempio di multithreading.

Creazione della visualizzazione dello stencil di profondità, passandola a uno stato scrivibile.

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

Creazione della vista buffer dei vertici, prima di tutto modificarla da uno stato comune a una destinazione, quindi da una destinazione a uno stato leggibile generico.

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

Creazione della vista buffer di indice, prima di tutto modificarla da uno stato comune a una destinazione, quindi da una destinazione a uno stato leggibile generico.

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

Creazione di trame e viste delle risorse shader. La trama viene modificata da uno stato comune a una destinazione e quindi da una destinazione a una risorsa pixel shader.

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

Inizio di un frame; Questo non solo usa ResourceBarrier per indicare che il backbuffer deve essere usato come destinazione di rendering, ma inizializza anche la risorsa frame (che chiama ResourceBarrier nel buffer depth stencil).

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

Terminando un frame, a indicare che il buffer nascosto viene ora usato per presentare.

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

Inizializzazione di una risorsa frame, chiamata all'inizio di un frame, esegue la transizione del buffer depth stencil in modo che sia scrivibile.

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

Le barriere vengono scambiate a metà fotogramma, passando la mappa ombreggiatura da scrivibile a leggibile.

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 viene chiamato al termine di un frame, passando la mappa shadow a uno stato comune.

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

Esempio di promozione e decadimento dello stato comune

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

Esempio di barriere suddivise

L'esempio seguente illustra come usare una barriera divisa per ridurre gli stalli della pipeline. Il codice seguente non usa barriere suddivise:

 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

Il codice seguente usa le barriere suddivise:

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

Esercitazioni video di apprendimento avanzato DirectX: Barriere alle risorse e rilevamento dello stato

Sincronizzazione multi-motore

Invio di lavoro in Direct3D 12

Uno sguardo all'interno delle barriere dello stato delle risorse D3D12