Поделиться через


Использование барьеров ресурсов для синхронизации состояний ресурсов в 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) - Барьер UAV указывает, что все операции UAV, как чтение, так и запись, должны быть завершены между любыми последующими операциями UAV, как чтение, так и запись. Приложению не нужно помещать барьер UAV между двумя вызовами рендеринга или диспетчеризации, которые считываются только из UAV. Кроме того, нет необходимости устанавливать барьер UAV между двумя вызовами рисования или отправки, которые записывают в один и тот же UAV, если приложение знает, что безопасно выполнять доступ к UAV в любом порядке. Структура D3D12_RESOURCE_UAV_BARRIER используется для указания ресурса UAV, к которому применяется барьер. Приложение может указать значение NULL для барьера для UAV, что указывает на то, что любой доступ к UAV может потребовать барьера.

Когда 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)
    • 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::Present (или IDXGISwapChain1::Present1) вызывается для ресурса, который в настоящее время не находится в нужном состоянии, создается предупреждение слоя отладки.

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

Все подресурсы в ресурсе должны находиться в состоянии RENDER_TARGET или DEPTH_WRITE для ресурсов рендеринга и ресурсов глубины-стенсил соответственно, когда вызывается ID3D12GraphicsCommandList::DiscardResource.

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

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

Общегосударственное продвижение

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

1 флаг ЗАПИСИ или 1 или более флагов ЧТЕНИЯ

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

Государственный флаг Буферы и текстуры Simultaneous-Access Текстуры, отличные отSimultaneous-Access
Буфер вершин и констант Да Нет
INDEX_BUFFER Да Нет
ЦЕЛЬ_ОТРИСОВКИ Да Нет
НЕУПОРЯДОЧЕННЫЙ_ДОСТУП Да Нет
DEPTH_WRITE Нет* Нет
DEPTH_READ Нет* Нет
НЕ-ПИКСЕЛЬНЫЙ_ШАДЕР_РЕСУРС Да Да
PIXEL_SHADER_RESOURCE Да Да
STREAM_OUT Да Нет
КОСВЕННЫЙ_АРГУМЕНТ Да Нет
COPY_DEST Да Да
ИСТОЧНИК_КОПИИ Да Да
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. Единственный допустимый барьер перехода, который можно применить к подресурсу с ожидающим барьером, — это барьер с тем же состоянием перед и после с флагом 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));
}

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

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