リソース バリアを使用して、Direct3D 12 のリソースの状態を同期する

Direct3D 12 では、全体的な CPU 使用量を減らし、ドライバーのマルチスレッド処理と前処理を可能にするために、リソースごとの状態を管理する責任がグラフィックス ドライバーからアプリケーションに移されます。 リソースごとの状態とは、たとえば、テクスチャ リソースが現在シェーダー リソース ビューとしてアクセスされているか、それともレンダー ターゲット ビューとしてアクセスされているか、ということです。 Direct3D 11 では、この状態をバックグラウンドで追跡するためにドライバーが必要でした。 これは CPU の観点から高コストであり、どのようなマルチスレッド設計も著しく複雑にします。 Microsoft Direct3D 12 では、ほとんどのリソースごとの状態は、単一の API ID3D12GraphicsCommandList::ResourceBarrier を使用してアプリケーションによって管理されます。

ResourceBarrier API を使用してリソースごとの状態を管理する

ResourceBarrier は、グラフィックス ドライバーに対して、リソースが格納されているメモリへの複数のアクセスを同期させる必要がある可能性がある状況を通知します。 このメソッドは、宣言されているリソース バリアの種類を示す 1 つ以上のリソース バリア記述構造体を指定して呼び出されます。

リソース バリアには次の 3 種類があります。

  • 遷移バリア - 遷移バリアは、異なる使用法の間で一連のサブリソースの遷移を示します。 D3D12_RESOURCE_TRANSITION_BARRIER構造体は、移行中のサブリソースと、サブリソースの前後の状態を指定するために使用されます。

    このシステムは、コマンド リスト内のサブリソースの遷移が同じコマンド リスト内の前の遷移と一致していることを確認します。 デバッグ レイヤーはさらにサブリソースの状態を追跡して他のエラーを見つけますが、この検証は保守的であり、網羅的なものではありません。

    D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES フラグを使用して、リソース内のすべてのサブリソースが切り替わるよう指定できることに注意してください。

  • エイリアシング バリア - エイリアシング バリアは、同じヒープへのマッピングが重複している 2 つの異なるリソースの使用法間の遷移を示します。 これは、予約済みリソースと配置済みリソースの両方に適用されます。 D3D12_RESOURCE_ALIASING_BARRIER構造体は、before リソースと after リソースの両方を指定するために使用されます。

    一方または両方のリソースが NULL になる可能性があることに注意してください。これは、タイル リソースがエイリアシングを引き起こす可能性があることを示します。 タイル リソースの使用法の詳細については、「Tiled resources (タイル リソース)」および「ボリューム タイル リソース」を参照してください。

  • 順序指定されていないアクセス ビュー (UAV) バリア - UAV バリアは、特定のリソースへのすべての UAV アクセス (読み取りと書き込みの両方) が、今後の UAV アクセス (読み取りと書き込みの両方) の間に完了しなければならないことを示します。 アプリは UAV からの読み取りのみを行う 2 つの描画呼び出しまたはディスパッチ呼び出しの間に UAV バリアを配置する必要はありません。 また、アプリケーションが UAV アクセスを任意の順序で実行しても安全であることがわかっている場合は、同じ UAV に書き込む 2 つの描画呼び出しまたはディスパッチ呼び出しの間に UAV バリアを配置する必要はありません。 バリアが適用される UAV リソースを指定するには、D3D12_RESOURCE_UAV_BARRIER構造体が使用されます。 アプリケーションは、バリアの UAV に NULL を指定できます。これは、すべての UAV アクセスでバリアが必要になる可能性があることを示します。

ResourceBarrier がリソース バリア記述の配列で呼び出されると、API は、あたかも要素ごとに 1 回呼び出されたかのように、要素が指定された順序で動作します。

いつでも、サブリソースは、ResourceBarrier に指定されたD3D12_RESOURCE_STATES フラグのセットによって決定される、正確に 1 つの状態になります。 アプリケーションでは、連続する ResourceBarrier の呼び出しの前の "" の状態と "" の状態が一致することを保証する必要があります。

ヒント

アプリケーションでは、可能な限り、複数の遷移を 1 つの 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 ヒープで作成されたリソースは で開始する必要があり、CPU のみが書き込みを行っているため、GENERIC_READ状態から変更することはできません。 逆に、READBACK ヒープで作成されたコミット済みリソースは で開始する必要があり、COPY_DEST状態から変更することはできません。

リソースの状態の読み取り/書き込みに関する制限

リソース状態を記述するために使用されるリソース状態使用法ビットは、読み取り専用状態と読み取り/書き込み状態に分けられます。 D3D12_RESOURCE_STATESのリファレンス トピックでは、列挙体の各ビットの読み取り/書き込みアクセス レベルを示します。

1 つのリソースに対して、最大で 1 つの読み取り/書き込みビットしか設定できません。 書き込みビットが設定されている場合は、そのリソースに読み取り専用ビットを設定することはできません。 書き込みビットが設定されていない場合は、任意の数の読み取りビットを設定できます。

バック バッファーを表示するためのリソースの状態

バック バッファーが表示される前に、D3D12_RESOURCE_STATE_COMMON状態である必要があります。 リソースの状態D3D12_RESOURCE_STATE_PRESENTはD3D12_RESOURCE_STATE_COMMONのシノニムであり、どちらも 0 の値を持つことに注意してください。 現在この状態ではないリソースに対して IDXGISwapChain::Present (または IDXGISwapChain1::Present1) が呼び出されると、デバッグ レイヤーの警告が出力されます。

リソースの破棄

リソース内のすべてのサブリソースは、 ID3D12GraphicsCommandList::D iscardResource が呼び出されたときに、レンダー ターゲット/深度ステンシル リソースのRENDER_TARGET状態またはDEPTH_WRITE状態である必要があります。

暗黙的な状態遷移

リソースは、D3D12_RESOURCE_STATE_COMMON外でのみ "昇格" できます。 同様に、リソースはD3D12_RESOURCE_STATE_COMMONにのみ "減衰" します。

共通状態の昇格

すべてのバッファー リソースと、D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS フラグが設定されたテクスチャは、読み取りシナリオに対応するGENERIC_READを含め、最初の GPU アクセス時にD3D12_RESOURCE_STATE_COMMONから関連する状態に暗黙的に昇格されます。 COMMON 状態のリソースには、

1 WRITE フラグ、または 1 つ以上の READ フラグ

リソースは、次の表に基づいて COMMON 状態から昇格させることができます。

状態フラグ バッファーおよび同時アクセス テクスチャ 非同時アクセス テクスチャ
VERTEX_AND_CONSTANT_BUFFER はい いいえ
INDEX_BUFFER はい いいえ
RENDER_TARGET はい いいえ
UNORDERED_ACCESS はい いいえ
DEPTH_WRITE いいえ* いいえ
DEPTH_READ いいえ* いいえ
NON_PIXEL_SHADER_RESOURCE はい Yes
PIXEL_SHADER_RESOURCE はい Yes
STREAM_OUT はい いいえ
INDIRECT_ARGUMENT はい いいえ
COPY_DEST Yes はい
COPY_SOURCE はい はい
RESOLVE_DEST はい いいえ
RESOLVE_SOURCE はい いいえ
PREDICATION はい いいえ

 

*深度ステンシル リソースは非同時アクセス テクスチャである必要があるため、暗黙的に昇格することはできません。

このアクセスが発生すると、昇格は暗黙のリソース バリアのように動作します。 その後のアクセスでは、必要に応じてリソースの状態を変更するためにリソース バリアが必要になります。 1 つの昇格された読み取り状態から複数の読み取り状態への昇格は有効ですが、書き込み状態の場合は有効ではないことに注意してください。
たとえば、共通状態のリソースが 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_TRANSITION_BARRIER構造体の BeforeState 値としてD3D12_RESOURCE_STATE_COMMONまたは任意の昇格可能な状態を使用するのが正しいです。 これにより、バッファーの自動降格と同時アクセス テクスチャを無視する従来の状態管理が可能になります。 ただし、これは望ましくない場合があります。共通状態にあることがわかっているリソースで遷移の 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 の遷移作業が終了したことが保証されます。

分割バリアを使用すると、特にマルチエンジンのシナリオや、1 つ以上のコマンド リスト全体にわたってリソースの読み取り/書き込み遷移がまばらに発生する場合に、パフォーマンスの向上に役立ちます。

リソース バリアのシナリオ例

次のスニペットは、マルチスレッドのサンプルでの 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 リソース状態バリア内の外観