Использование барьеров ресурсов для синхронизации состояний ресурсов в Direct3D 12

Чтобы снизить общую загрузку ЦП и включить многопотоковую и предварительную обработку драйверов, Direct3D 12 переместит ответственность за управление состоянием каждого ресурса с графического драйвера на приложение. Примером состояния каждого ресурса является то, осуществляется ли в настоящее время доступ к ресурсу текстуры через представление ресурсов шейдера или в качестве целевого представления отрисовки. В Direct3D 11 драйверы должны были отслеживать это состояние в фоновом режиме. Это дорого с точки зрения ЦП и значительно усложняет любой вид многопотокового проектирования. В Microsoft Direct3D 12 большинством состояний каждого ресурса управляет приложение с одним API ID3D12GraphicsCommandList::ResourceBarrier.

Использование API ResourceBarrier для управления состоянием каждого ресурса

ResourceBarrier уведомляет графический драйвер о ситуациях, в которых драйверу может потребоваться синхронизировать несколько обращений к памяти, в которой хранится ресурс. Метод вызывается с одной или несколькими структурами описания барьеров ресурсов, указывающими тип объявляемого барьера ресурса.

Существует три типа барьеров ресурсов:

  • Переходный барьер . Барьер перехода указывает, что набор подресурсов переходит между различными способами использования. Структура D3D12_RESOURCE_TRANSITION_BARRIER используется для указания переходного подресурса, а также состояний до и после подресурса.

    Система проверяет, согласуются ли переходы подресурсов в списке команд с предыдущими переходами в том же списке команд. Уровень отладки дополнительно отслеживает состояние подресурса для поиска других ошибок, однако эта проверка является консервативной и не исчерпывающей.

    Обратите внимание, что флаг D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES можно использовать, чтобы указать, что все подресурсы в ресурсе переносятся.

  • Барьер псевдонима . Барьер псевдонима указывает на переход между использованием двух разных ресурсов, которые имеют перекрывающиеся сопоставления в одной куче. Это относится как к зарезервированным, так и к размещенным ресурсам. Структура D3D12_RESOURCE_ALIASING_BARRIER используется для указания ресурсов до и после .

    Обратите внимание, что один или оба ресурса могут иметь значение NULL, что означает, что любой ресурс с плитками может вызвать псевдоним. Дополнительные сведения об использовании мозаичного ресурса см. в разделах Мозаичные ресурсы и Ресурсы с плитками томов.

  • Барьер представления неупорядоченного доступа (UAV) — барьер БПЛА указывает, что все операции чтения или записи БПЛА к определенному ресурсу должны быть завершены между всеми будущими доступами БПЛА как на чтение, так и на запись. Приложению не нужно ставить барьер БПЛА между двумя вызовами draw или dispatch, которые считываются только из БПЛА. Кроме того, нет необходимости ставить барьер БПЛА между двумя вызовами draw или dispatch, которые записывают в один и тот же БПЛА, если приложение знает, что безопасно выполнять доступ к БПЛА в любом порядке. Структура D3D12_RESOURCE_UAV_BARRIER используется для указания ресурса БПЛА, к которому применяется барьер. Приложение может указать значение NULL для БПЛА барьера, что указывает на то, что для доступа к БПЛА может потребоваться барьер.

При вызове ResourceBarrier с массивом описаний барьеров ресурсов API ведет себя так, как если бы он был вызван один раз для каждого элемента в том порядке, в котором они были предоставлены.

В любой момент времени подресурс находится в ровно одном состоянии, определяемом набором флагов D3D12_RESOURCE_STATES , предоставленных ResourceBarrier. Приложение должно обеспечить согласие состояний до и после последовательных вызовов ResourceBarrier .

Совет

Приложения должны по возможности объединять несколько переходов в один вызов API.

 

Состояния ресурсов

Полный список состояний ресурсов, между которыми может переходить ресурс, см. в справочном разделе по перечислению D3D12_RESOURCE_STATES .

Сведения о разделенных барьерах ресурсов также см . в разделе D3D12_RESOURCE_BARRIER_FLAGS.

Начальные состояния ресурсов

Ресурсы могут создаваться с любым начальным состоянием, указанным пользователем (допустимо для описания ресурса), со следующими исключениями:

  • Куча отправки должна начинаться в состоянии D3D12_RESOURCE_STATE_GENERIC_READ которое представляет собой побитовое сочетание ИЛИ:
    • 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
  • Кучи обратного чтения должны начинаться в D3D12_RESOURCE_STATE_COPY_DEST состоянии.
  • Буферы обратной цепочки буферов автоматически запускают в D3D12_RESOURCE_STATE_COMMON состоянии.

Прежде чем куча станет целевой объектом операции копирования GPU, обычно кучу необходимо сначала перевести в состояние D3D12_RESOURCE_STATE_COPY_DEST. Однако ресурсы, созданные в кучах UPLOAD, должны запускаться в и не могут изменяться из состояния GENERIC_READ, так как запись будет выполнять только ЦП. И наоборот, зафиксированные ресурсы, созданные в кучах READBACK, должны запускаться в и не могут изменяться из состояния COPY_DEST.

Ограничения состояния ресурсов для чтения и записи

Биты использования состояния ресурсов, используемые для описания состояния ресурса, делятся на состояния только для чтения и чтения и записи. Справочный раздел для D3D12_RESOURCE_STATES указывает уровень доступа для чтения и записи для каждого бита в перечислении.

Для любого ресурса можно задать не более одного бита чтения и записи. Если задан бит записи, то для этого ресурса нельзя задать бит только для чтения. Если бит записи не задан, можно задать любое количество битов чтения.

Состояния ресурсов для представления задних буферов

Перед представлением заднего буфера он должен находиться в D3D12_RESOURCE_STATE_COMMON состоянии. Обратите внимание, что D3D12_RESOURCE_STATE_PRESENT состояния ресурса является синонимом D3D12_RESOURCE_STATE_COMMON, и оба они имеют значение 0. Если метод IDXGISwapChain::P resent (или IDXGISwapChain1::P resent1) вызывается для ресурса, который в настоящее время не находится в этом состоянии, создается предупреждение уровня отладки.

Удаление ресурсов

При вызове ID3D12GraphicsCommandList::D iscardResource все подресурсы в ресурсе должны находиться в состоянии RENDER_TARGET или DEPTH_WRITE состоянии соответственно.

Неявные переходы состояния

Ресурсы могут быть "повышены" только из D3D12_RESOURCE_STATE_COMMON. Аналогичным образом, ресурсы будут только "распадаться" для D3D12_RESOURCE_STATE_COMMON.

Повышение общего состояния

Все буферные ресурсы, а также текстуры с флагом D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS неявно повышаются с D3D12_RESOURCE_STATE_COMMON до соответствующего состояния при первом доступе к GPU, включая GENERIC_READ для покрытия любого сценария чтения. Доступ к любому ресурсу в общем состоянии можно получить в том виде, в каком он был в одном состоянии с

1 флаг WRITE или 1 или более флагов READ

Ресурсы можно повысить из общего состояния на основе следующей таблицы:

Флаг состояния Буферы и текстуры Simultaneous-Access Текстуры без одновременного доступа
VERTEX_AND_CONSTANT_BUFFER Да Нет
INDEX_BUFFER Да Нет
RENDER_TARGET Да Нет
UNORDERED_ACCESS Да Нет
DEPTH_WRITE Нет* Нет
DEPTH_READ Нет* Нет
NON_PIXEL_SHADER_RESOURCE Да Да
PIXEL_SHADER_RESOURCE Да Да
STREAM_OUT Да Нет
INDIRECT_ARGUMENT Да Нет
COPY_DEST Да Да
COPY_SOURCE Да Да
RESOLVE_DEST Да Нет
RESOLVE_SOURCE Да Нет
ПРЕДИКАЦИЯ Да Нет

 

*Ресурсы трафарета глубины должны быть текстурами без одновременного доступа и поэтому никогда не могут быть неявно повышены.

Когда этот доступ происходит, повышение уровня действует как неявный барьер ресурсов. Для последующих обращений для изменения состояния ресурса при необходимости потребуются барьеры ресурсов. Обратите внимание, что повышение уровня из одного повышенного состояния чтения в несколько является допустимым, но это не относится к состояниям записи.
Например, если ресурс в общем состоянии повышен до PIXEL_SHADER_RESOURCE в вызове Draw, его все равно можно повысить до NON_PIXEL_SHADER_RESOURCE | PIXEL_SHADER_RESOURCE в другом вызове Draw. Однако, если он используется в операции записи, например в назначении копирования, барьер перехода состояния ресурса от объединенных повышенных состояний чтения, здесь NON_PIXEL_SHADER_RESOURCE | PIXEL_SHADER_RESOURCE требуется COPY_DEST.
Аналогичным образом, при повышении уровня с COMMON до COPY_DEST по-прежнему требуется барьер для перехода от COPY_DEST к RENDER_TARGET.

Обратите внимание, что обычное повышение состояния является "бесплатным", так как GPU не требуется выполнять какие-либо ожидания синхронизации. Повышение представляет собой тот факт, что ресурсы в общем состоянии не должны требовать дополнительной работы GPU или отслеживания драйверов для поддержки определенных доступа.

Упадок состояния до общего

Обратная сторона общего государства продвижения является распад обратно к D3D12_RESOURCE_STATE_COMMON. Ресурсы, отвечающие определенным требованиям, считаются без отслеживания состояния и фактически возвращаются в общее состояние, когда GPU завершает выполнение операции ExecuteCommandLists . Распад не происходит между списками команд, выполняемыми вместе в одном вызове ExecuteCommandLists .

После завершения операции ExecuteCommandLists на GPU следующие ресурсы упадет:

  • Ресурсы, к которых осуществляется доступ в очереди копирования, или
  • Буферизация ресурсов в очереди любого типа или
  • Текстура ресурсов для любого типа очереди, для которых установлен флаг D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS , или
  • Любой ресурс неявно повышен до состояния только для чтения.

Как и обычное повышение состояния, упадок является свободным, так как дополнительная синхронизация не требуется. Сочетание общего уровня состояния и упадка может помочь устранить многие ненужные переходы ResourceBarrier . В некоторых случаях это может значительно повысить производительность.

Буферы и Simultaneous-Access ресурсы будут переходить к общему состоянию независимо от того, были ли они явно перенесены с помощью барьеров ресурсов или неявно повышены.

Влияние на производительность

При записи явных переходов ResourceBarrier для ресурсов в общем состоянии правильно использовать D3D12_RESOURCE_STATE_COMMON или любое промотабельное состояние в качестве значения BeforeState в структуре D3D12_RESOURCE_TRANSITION_BARRIER. Это обеспечивает традиционное управление состоянием, которое игнорирует автоматическое затухание буферов и текстур с одновременным доступом. Однако это может оказаться нежелательным, так как избежать перехода вызовов ResourceBarrier с ресурсами, которые, как известно, в общем состоянии, могут значительно повысить производительность. Барьеры ресурсов могут быть дорогостоящими. Они предназначены для принудительной очистки кэша, изменения макета памяти и другой синхронизации, которая может не потребоваться для ресурсов, которые уже находятся в общем состоянии. Список команд, в котором используется барьер ресурсов от нестандартного состояния до другого не общего состояния на ресурсе, который в настоящее время находится в общем состоянии, может привести к большим ненужным издержкам.

Кроме того, избегайте явных переходов ResourceBarrier на D3D12_RESOURCE_STATE_COMMON если не требуется (например, следующий доступ осуществляется в очереди команд COPY, для которой требуется, чтобы ресурсы запускались в общем состоянии). Чрезмерные переходы в общее состояние могут значительно снизить производительность GPU.

Таким образом, старайтесь полагаться на повышение общего состояния и угасание всякий раз, когда его семантика позволяет уйти без вызовов ResourceBarrier .

Разделение барьеров

Барьер перехода ресурсов с флагом D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY начинается с барьера разделения, и переходный барьер считается ожидающей. Пока барьер находится в состоянии ожидания, ресурс (вложенный) не может быть прочитан или записан GPU. Единственным юридическим барьером перехода, который можно применить к ресурсу (sub)с ожидающим барьером, является барьер с одинаковыми состояниями до и после и флагом D3D12_RESOURCE_BARRIER_FLAG_END_ONLY, который завершает ожидающий переход.

Разделенные барьеры предоставляют GPU подсказки о том, что ресурс в состоянии A будет использоваться позже в состоянии B . Это дает GPU возможность оптимизировать рабочую нагрузку перехода, что может привести к сокращению или устранению остановок выполнения. Выдача барьера только для завершения гарантирует, что все операции по переходу GPU будут завершены перед переходом к следующей команде.

Использование разбиения барьеров может помочь повысить производительность, особенно в сценариях с несколькими подсистемами или когда ресурсы для чтения и записи перемещаются разреженно в одном или нескольких списках команд.

Пример сценария с барьером ресурсов

В следующих фрагментах кода показано использование метода ResourceBarrier в многопотоковом примере.

Создание представления трафарета глубины с переводом его в записываемое состояние.

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

Создайте представление буфера вершин, сначала изменив его из общего состояния в целевое, а затем с конечного на универсальное удобочитаемое состояние.

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

Создайте представление буфера индекса, сначала изменив его из общего состояния в целевое, а затем с конечного на универсальное удобочитаемое состояние.

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

Создание текстур и представлений ресурсов шейдера. Текстура изменяется из общего состояния в назначение, а затем из назначения в ресурс шейдера пикселей.

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

Начало кадра; Это не только использует ResourceBarrier для указания того, что backbuffer будет использоваться в качестве целевого объекта отрисовки, но и инициализирует ресурс кадра (который вызывает ResourceBarrier в буфере трафарета глубины).

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

Окончание кадра, указывающее, что для представления теперь используется задний буфер.

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

Инициализация ресурса кадра, вызываемого при запуске кадра, переводит буфер трафарета глубины в записываемый.

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

Барьеры переключаются в середине кадра, переходя от теневой карты от записываемой к читаемой.

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 вызывается при завершении фрейма, что переводит карту теней в общее состояние.

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

Пример повышения общего состояния и распада

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

Пример разбиения барьеров

В следующем примере показано, как использовать барьер разделения для уменьшения остановок конвейера. В следующем коде не используются барьеры разделения:

 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

В следующем коде используются барьеры разделения:

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: барьеры ресурсов и отслеживание состояния

Синхронизация с несколькими движками

Отправка работы в Direct3D 12

Взгляд на барьеры состояния ресурсов D3D12