Uso de barreras de recursos para sincronizar los estados de los recursos en Direct3D 12

Para reducir el uso general de la CPU y habilitar el procesamiento previo y multiproceso del controlador, Direct3D 12 mueve la responsabilidad de la administración del estado por recurso del controlador de gráficos a la aplicación. Un ejemplo de estado por recurso es si actualmente se accede a un recurso de textura como a través de una vista de recursos del sombreador o como una vista de destino de representación. En Direct3D 11, se requerían controladores para realizar el seguimiento de este estado en segundo plano. Esto es costoso desde una perspectiva de CPU y complica significativamente cualquier tipo de diseño multiproceso. En Microsoft Direct3D 12, la aplicación administra la mayoría del estado por recurso con una sola API, ID3D12GraphicsCommandList::ResourceBarrier.

Uso de resourceBarrier API para administrar el estado por recurso

ResourceBarrier notifica al controlador gráfico de situaciones en las que es posible que el controlador tenga que sincronizar varios accesos a la memoria en la que se almacena un recurso. Se llama al método con una o varias estructuras de descripción de barrera de recursos que indican el tipo de barrera de recursos que se declara.

Hay tres tipos de barreras de recursos:

  • Barrera de transición : una barrera de transición indica que un conjunto de subrecursos realiza una transición entre distintos usos. Se usa una estructura D3D12_RESOURCE_TRANSITION_BARRIER para especificar el subrecurso que realiza la transición, así como los estados antes y después del subrecurso.

    El sistema comprueba que las transiciones de subrecursos de una lista de comandos son coherentes con las transiciones anteriores en la misma lista de comandos. La capa de depuración realiza un seguimiento del estado del subrecurso para encontrar otros errores, pero esta validación es conservadora y no exhaustiva.

    Tenga en cuenta que puede usar la marca D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES para especificar que se están realizando la transición de todos los subrecursos de un recurso.

  • Barrera de alias: una barrera de alias indica una transición entre los usos de dos recursos diferentes que tienen asignaciones superpuestas en el mismo montón. Esto se aplica a los recursos reservados y colocados. Se usa una estructura D3D12_RESOURCE_ALIASING_BARRIER para especificar tanto el recurso anterior como el recurso posterior .

    Tenga en cuenta que uno o ambos recursos pueden ser NULL, lo que indica que cualquier recurso en mosaico podría provocar alias. Para obtener más información sobre el uso de recursos en mosaico, vea Recursos en mosaico y Recursos en mosaico de volumen.

  • Barrera de la vista de acceso sin ordenar (UAV): una barrera UAV indica que todos los accesos UAV, tanto de lectura como de escritura, a un recurso determinado deben completarse entre cualquier acceso UAV futuro, tanto de lectura como de escritura. No es necesario que una aplicación coloque una barrera UAV entre dos llamadas de dibujo o envío que solo leen de un UAV. Además, no es necesario colocar una barrera UAV entre dos llamadas de dibujo o envío que escriben en el mismo UAV si la aplicación sabe que es seguro ejecutar el acceso UAV en cualquier orden. Se usa una estructura D3D12_RESOURCE_UAV_BARRIER para especificar el recurso UAV al que se aplica la barrera. La aplicación puede especificar NULL para el UAV de la barrera, lo que indica que cualquier acceso UAV podría requerir la barrera.

Cuando se llama a ResourceBarrier con una matriz de descripciones de barreras de recursos, la API se comporta como si se llamara una vez para cada elemento, en el orden en que se proporcionaron.

En un momento dado, un subrecurso se encuentra exactamente en un estado determinado por el conjunto de marcas de D3D12_RESOURCE_STATES proporcionadas a ResourceBarrier. La aplicación debe asegurarse de que los estados antes y después de las llamadas consecutivas a ResourceBarrier están de acuerdo.

Sugerencia

Las aplicaciones deben procesar por lotes varias transiciones en una llamada API siempre que sea posible.

 

Estados de recursos

Para obtener la lista completa de estados de recursos entre los que un recurso puede realizar la transición, consulte el tema de referencia de la enumeración D3D12_RESOURCE_STATES .

Para las barreras de recursos divididos, consulte también D3D12_RESOURCE_BARRIER_FLAGS.

Estados iniciales de los recursos

Los recursos se pueden crear con cualquier estado inicial especificado por el usuario (válido para la descripción del recurso), con las siguientes excepciones:

  • Los montones de carga deben comenzar en el estado D3D12_RESOURCE_STATE_GENERIC_READ que es una combinación OR bit a bit de:
    • 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
  • Los montones de lectura diferida deben comenzar en el estado D3D12_RESOURCE_STATE_COPY_DEST.
  • Los búferes de retroceso de la cadena de intercambio se inician automáticamente en estado de D3D12_RESOURCE_STATE_COMMON.

Antes de que un montón pueda ser el destino de una operación de copia de GPU, normalmente el montón debe pasar primero al estado D3D12_RESOURCE_STATE_COPY_DEST. Sin embargo, los recursos creados en montones upload deben iniciarse y no pueden cambiar del estado de GENERIC_READ, ya que solo la CPU realizará la escritura. Por el contrario, los recursos confirmados creados en montones readback deben iniciarse y no pueden cambiar del estado de COPY_DEST.

Restricciones de estado de recursos de lectura y escritura

Los bits de uso del estado de recurso que se usan para describir un estado de recurso se dividen en estados de solo lectura y de lectura y escritura. El tema de referencia de la D3D12_RESOURCE_STATES indica el nivel de acceso de lectura y escritura para cada bit de la enumeración.

Como máximo, solo se puede establecer un bit de lectura y escritura para cualquier recurso. Si se establece un bit de escritura, no se puede establecer ningún bit de solo lectura para ese recurso. Si no se establece ningún bit de escritura, se puede establecer cualquier número de bits de lectura.

Estados de recursos para presentar búferes de reserva

Antes de presentar un búfer de reserva, debe estar en estado D3D12_RESOURCE_STATE_COMMON. Tenga en cuenta que el estado del recurso D3D12_RESOURCE_STATE_PRESENT es un sinónimo de D3D12_RESOURCE_STATE_COMMON y ambos tienen un valor de 0. Si se llama a IDXGISwapChain::P resent (o IDXGISwapChain1::P resent1) en un recurso que no está actualmente en este estado, se emite una advertencia de capa de depuración.

Descartar recursos

Todos los subrecursos de un recurso deben estar en estado de RENDER_TARGET o DEPTH_WRITE estado, para los recursos de galería de símbolos de profundidad o destinos de representación, respectivamente, cuando se llama a ID3D12GraphicsCommandList::D iscardResource .

Transiciones de estado implícitas

Los recursos solo se pueden "promocionar" fuera de D3D12_RESOURCE_STATE_COMMON. Del mismo modo, los recursos solo se "desintegrarán" a D3D12_RESOURCE_STATE_COMMON.

Promoción de estado común

Todos los recursos de búfer, así como las texturas con el conjunto de marcas de D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS, se promueven implícitamente de D3D12_RESOURCE_STATE_COMMON al estado pertinente en el primer acceso a GPU, incluida la GENERIC_READ para cubrir cualquier escenario de lectura. Se puede acceder a cualquier recurso del estado COMMON, ya que se encontraba en un solo estado con

1 marca WRITE, o 1 o más marcas DE LECTURA

Los recursos se pueden promover desde el estado COMMON en función de la tabla siguiente:

Marca de estado Búferes y texturas de Simultaneous-Access Texturas de acceso no simultáneo
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
PREDICACIÓN No

 

*Los recursos de galería de símbolos de profundidad deben ser texturas de acceso no simultáneo y, por tanto, nunca se pueden promover implícitamente.

Cuando se produce este acceso, la promoción actúa como una barrera de recursos implícita. En el caso de los accesos posteriores, se requerirán barreras de recursos para cambiar el estado del recurso si es necesario. Tenga en cuenta que la promoción de un estado de lectura promocionado a varios estados de lectura es válido, pero esto no es el caso de los estados de escritura.
Por ejemplo, si un recurso en estado común se promueve a PIXEL_SHADER_RESOURCE en una llamada a Draw, todavía se puede promover a NON_PIXEL_SHADER_RESOURCE | PIXEL_SHADER_RESOURCE en otra llamada a Draw. Sin embargo, si se usa en una operación de escritura, como un destino de copia, una barrera de transición de estado de recurso de los estados de lectura promocionados combinados, aquí NON_PIXEL_SHADER_RESOURCE | PIXEL_SHADER_RESOURCE, para COPY_DEST es necesario.
De forma similar, si se promueve de COMMON a COPY_DEST, se sigue necesitando una barrera para pasar de COPY_DEST a RENDER_TARGET.

Tenga en cuenta que la promoción de estado común es "gratis" en que no es necesario que la GPU realice ninguna espera de sincronización. La promoción representa el hecho de que los recursos en estado COMÚN no deben requerir trabajo adicional de GPU o seguimiento de controladores para admitir determinados accesos.

Decaimiento de estado en común

El lado invertido de la promoción de estado común vuelve a D3D12_RESOURCE_STATE_COMMON. Los recursos que cumplen ciertos requisitos se consideran sin estado y vuelven eficazmente al estado común cuando la GPU finaliza la ejecución de una operación ExecuteCommandLists . La descomposición no se produce entre las listas de comandos ejecutadas juntas en la misma llamada a ExecuteCommandLists .

Los siguientes recursos se desintegrarán cuando se complete una operación ExecuteCommandLists en la GPU:

  • Recursos a los que se accede en una cola de copia o
  • Almacenar en búfer los recursos en cualquier tipo de cola o
  • Recursos de textura en cualquier tipo de cola que tenga establecida la marca D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS o
  • Cualquier recurso promocionado implícitamente a un estado de solo lectura.

Al igual que la promoción de estado común, la descomposición es libre en que no se necesita ninguna sincronización adicional. La combinación de la promoción de estado común y la disminución pueden ayudar a eliminar muchas transiciones innecesarias de ResourceBarrier . En algunos casos, esto puede proporcionar mejoras de rendimiento significativas.

Los búferes y los recursos Simultaneous-Access se desintegran al estado común, independientemente de si se han pasado explícitamente mediante barreras de recursos o promocionados implícitamente.

Implicaciones de rendimiento

Al grabar transiciones explícitas de ResourceBarrier en los recursos en estado común, es correcto usar D3D12_RESOURCE_STATE_COMMON o cualquier estado promotable como valor BeforeState en la estructura D3D12_RESOURCE_TRANSITION_BARRIER. Esto permite la administración de estado tradicional que omite la descomposición automática de los búferes y las texturas de acceso simultáneo. Sin embargo, esto puede no ser deseable, ya que evitar las llamadas de ResourceBarrier con recursos conocidos como estar en el estado común puede mejorar significativamente el rendimiento. Las barreras de recursos pueden ser costosas. Están diseñados para forzar el vaciado de la memoria caché, los cambios de diseño de memoria y otras sincronizaciones que pueden no ser necesarias para los recursos que ya están en estado común. Una lista de comandos que usa una barrera de recursos de un estado no común a otro estado no común en un recurso que se encuentra actualmente en el estado común puede presentar una gran sobrecarga innecesaria.

Además, evite las transiciones explícitas de ResourceBarrier a D3D12_RESOURCE_STATE_COMMON a menos que sea absolutamente necesario (por ejemplo, el siguiente acceso está en una cola de comandos COPY que requiere que los recursos comiencen en el estado común). Las transiciones excesivas al estado común pueden ralentizar considerablemente el rendimiento de la GPU.

En resumen, intente confiar en la promoción de estado común y decaiga siempre que su semántica le permita salir sin emitir llamadas a ResourceBarrier .

Barreras divididas

Una barrera de transición de recursos con la marca D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY comienza una barrera dividida y se dice que la barrera de transición está pendiente. Aunque la barrera está pendiente del recurso (sub)no se puede leer ni escribir mediante la GPU. La única barrera de transición legal que se puede aplicar a un recurso (sub)con una barrera pendiente es una con los mismos estados antes y después y después y la marca de D3D12_RESOURCE_BARRIER_FLAG_END_ONLY, que la barrera completa la transición pendiente.

Las barreras divididas proporcionan sugerencias a la GPU que un recurso en el estado A se usará en el estado B algún tiempo más adelante. Esto proporciona a la GPU la opción de optimizar la carga de trabajo de transición, lo que posiblemente reduce o elimina los puestos de ejecución. La emisión de la barrera de solo finalización garantiza que se complete todo el trabajo de transición de GPU antes de pasar al siguiente comando.

El uso de barreras divididas puede ayudar a mejorar el rendimiento, especialmente en escenarios de varios motores o en los que los recursos son de lectura y escritura con transición dispersa en una o varias listas de comandos.

Escenario de ejemplo de barrera de recursos

Los fragmentos de código siguientes muestran el uso del método ResourceBarrier en un ejemplo de varios subprocesos.

Crear la vista de galería de símbolos de profundidad y pasarla a un estado grabable.

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

Crear la vista del búfer de vértices, cambiarla primero de un estado común a un destino y, a continuación, de un destino a un estado legible genérico.

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

Crear la vista de búfer de índice, cambiarla primero de un estado común a un destino y, a continuación, de un destino a un estado legible genérico.

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

Creación de texturas y vistas de recursos de sombreador. La textura se cambia de un estado común a un destino y, a continuación, de un destino a un recurso de sombreador de píxeles.

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

Comienzo de un marco; Esto no solo usa ResourceBarrier para indicar que el búfer de reserva se va a usar como destino de representación, sino que también inicializa el recurso de fotograma (que llama a ResourceBarrier en el búfer de galería de símbolos de profundidad).

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

Finalizar un marco, que indica que el búfer de reserva ahora se usa para presentar.

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

Al inicializar un recurso de marco, se le llama al iniciar un marco, realiza la transición del búfer de galería de símbolos de profundidad a que se pueda escribir.

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

Las barreras se intercambian en el marco medio, realizando la transición del mapa de sombras de escritura a legible.

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

Se llama a Finish cuando finaliza un marco, realizando la transición del mapa de sombras a un estado común.

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

Ejemplo común de promoción y de descomposición de estado

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

Ejemplo de barreras divididas

En el ejemplo siguiente se muestra cómo usar una barrera dividida para reducir los puestos de canalización. El código siguiente no usa barreras divididas:

 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

El código siguiente usa barreras divididas:

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

Tutoriales en vídeo de aprendizaje avanzado de DirectX: Barreras de recursos y seguimiento de estado

Sincronización de varios motores

Envío de trabajo en Direct3D 12

Un aspecto dentro de las barreras de estado de recursos D3D12