在 Direct3D 12 中使用資源屏障同步處理資源狀態

為了減少整體 CPU 使用量並啟用驅動程式多執行緒處理和前置處理,Direct3D 12 會將每個資源狀態管理的責任從圖形驅動程式移至應用程式。 每個資源狀態的範例是紋理資源目前是透過著色器資源檢視或轉譯目標檢視來存取。 在 Direct3D 11 中,驅動程式必須在背景中追蹤此狀態。 從 CPU 的觀點來看,這很昂貴,而且會大幅使任何類型的多執行緒設計複雜。 在 Microsoft Direct3D 12 中,大部分的每個資源狀態都是由具有單一 API ID3D12GraphicsCommandList::ResourceBarrier的應用程式所管理。

使用 ResourceBarrier API 來管理個別資源狀態

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 資源。 應用程式可以為屏障的 UAV 指定 Null,這表示任何 UAV 存取都可能需要屏障。

使用資源屏障描述的陣列呼叫 ResourceBarrier 時,API 的行為就像是針對每個元素呼叫一次,依它們提供的順序。

在任何指定時間,子資源都處於一個狀態,由提供給ResourceBarrier的一組D3D12_RESOURCE_STATES旗標所決定。 應用程式必須確定連續呼叫 ResourceBarrier的前後狀態都同意。

提示

應用程式應該盡可能將多個轉換批次處理成一個 API 呼叫。

 

資源狀態

如需資源可在之間轉換的完整資源狀態清單,請參閱 D3D12_RESOURCE_STATES 列舉的參考主題。

如需分割資源屏障,另請參閱 D3D12_RESOURCE_BARRIER_FLAGS

資源的初始狀態

資源可以使用任何使用者指定的初始狀態來建立, (對資源描述) 有效,但有下列例外狀況:

  • 上傳堆積必須以位 OR 組合的狀態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狀態變更,因為只有 CPU 會執行寫入。 相反地,在 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涵蓋任何讀取案例。 您可以透過 COMMON 狀態存取任何資源,因為其處於單一狀態

1 個 WRITE 旗標,或 1 個以上的 READ 旗標

資源可以根據下表從 COMMON 狀態升級:

狀態旗標 緩衝區和Simultaneous-Access紋理 非同時存取紋理
VERTEX_AND_CONSTANT_BUFFER No
INDEX_BUFFER No
RENDER_TARGET No
UNORDERED_ACCESS No
DEPTH_WRITE * No
DEPTH_READ * No
NON_PIXEL_SHADER_RESOURCE Yes Yes
PIXEL_SHADER_RESOURCE Yes Yes
STREAM_OUT No
INDIRECT_ARGUMENT No
COPY_DEST Yes Yes
COPY_SOURCE Yes Yes
RESOLVE_DEST No
RESOLVE_SOURCE No
預測 No

 

*深度樣板資源必須是非同時存取紋理,因此永遠不會隱含地升級。

當此存取發生時,升級會像隱含資源屏障一樣。 針對後續存取,需要資源屏障才能視需要變更資源狀態。 請注意,從一個升級的讀取狀態升級為多個讀取狀態是有效的,但這不是寫入狀態的情況。
例如,如果處於通用狀態的資源升級為 Draw 呼叫中的PIXEL_SHADER_RESOURCE,它仍然可以升級為 NON_PIXEL_SHADER_RESOURCE |在另一個 Draw 呼叫中PIXEL_SHADER_RESOURCE。 不過,如果在寫入作業中使用,例如複製目的地、來自合併升級讀取狀態的資源狀態轉換屏障,請在這裡NON_PIXEL_SHADER_RESOURCE |需要PIXEL_SHADER_RESOURCE,才能COPY_DEST。
同樣地,如果從 COMMON 升級為COPY_DEST,仍需要障礙,才能從COPY_DEST轉換為RENDER_TARGET。

請注意,常見的狀態升級是「免費」,因為 GPU 不需要執行任何同步處理等候。 升級代表 COMMON 狀態的資源不應該需要額外的 GPU 工作或驅動程式追蹤來支援特定存取。

狀態衰減為通用

一般狀態升階的翻轉會變回D3D12_RESOURCE_STATE_COMMON。 符合特定需求的資源會被視為無狀態,且會在 GPU 完成 ExecuteCommandLists 作業執行時,有效地回到通用狀態。 在相同 ExecuteCommandLists 呼叫中一起執行的命令清單之間不會發生衰減。

在 GPU 上完成 ExecuteCommandLists 作業時,下列資源將會衰減:

  • 在複製佇列上存取的資源,
  • 在任何佇列類型上緩衝資源,
  • 已設定D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS旗標之任何佇列類型的紋理資源,
  • 任何隱含升級為唯讀狀態的資源。

就像常見的狀態升級一樣,衰減是免費的,因此不需要額外的同步處理。 結合常見的狀態升階和衰減有助於消除許多不必要的 ResourceBarrier 轉換。 在某些情況下,這可提供顯著的效能改善。

不論是否使用資源屏障或隱含升級,緩衝區和Simultaneous-Access資源都會衰減為通用狀態。

對於效能的影響

記錄明確 ResourceBarrier 在一般狀態的資源轉換時,正確的方式是使用D3D12_RESOURCE_STATE_COMMON或任何可提升的狀態做為 D3D12_RESOURCE_TRANSITION_BARRIER 結構中的 BeforeState 值。 這可讓傳統的狀態管理忽略緩衝區的自動衰減和同時存取紋理。 不過,可能不需要這樣做,因為避免使用已知處於常見狀態的資源轉換 ResourceBarrier 呼叫可能會大幅改善效能。 資源屏障可能很昂貴。 它們的設計目的是為了強制快取排清、記憶體配置變更和其他可能不需要的資源處於通用狀態的同步處理。 使用資源屏障從非通用狀態到目前處於常見狀態之資源的另一個非通用狀態的命令清單,可能會造成許多不必要的額外負荷。

此外,請避免明確的 ResourceBarrier 轉換至D3D12_RESOURCE_STATE_COMMON,除非絕對必要 (,例如下一個存取權位於 COPY 命令佇列上,這需要資源以一般狀態開始) 。 過度轉換至常見狀態可能會大幅降低 GPU 效能。

總而言之,每當其語意讓您不發出 ResourceBarrier 呼叫時,請嘗試依賴常見的狀態升階和衰減。

分割屏障

具有D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY旗標的資源轉換屏障會開始分割屏障,而且轉換屏障稱為擱置中。 當屏障擱置時,GPU 無法讀取或寫入 (子) 資源。 唯一可套用至 (子) 資源且具有擱置障礙的合法轉換屏障,是狀態D3D12_RESOURCE_BARRIER_FLAG_END_ONLY旗標相同,而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 來指出反緩衝器是做為轉譯目標,也會初始化框架資源 (,以在深度樣板緩衝區上呼叫 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 資源狀態屏障