次の方法で共有


リソース バリアを使用した Direct3D 12 でのリソース状態の同期

全体的な CPU 使用率を減らし、ドライバーのマルチスレッドと前処理を有効にするために、Direct3D 12 はリソースごとの状態管理の役割をグラフィックス ドライバーからアプリケーションに移動します。 リソースごとの状態の例として、テクスチャ リソースが現在シェーダー リソース ビューを介してアクセスされているか、レンダー ターゲット ビューとしてアクセスされているかが挙がります。 Direct3D 11 では、ドライバーはバックグラウンドでこの状態を追跡する必要がありました。 これは CPU の観点から見るとコストがかかり、あらゆる種類のマルチスレッド設計が大幅に複雑になります。 Microsoft Direct3D 12 では、ほとんどのリソースごとの状態は、1 つの 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 を指定できることに注意してください。これは、タイル化されたリソースによってエイリアスが発生する可能性があることを示します。 タイルリソースの使用方法の詳細については、「タイルリソース」および「ボリュームタイルリソース」を参照してください。

  • 順序なしアクセス ビュー (UAV) バリア。 UAV バリアは、特定のリソースに対するすべての読み取りまたは書き込み UAV アクセスが、その同じリソースへの将来の読み取りまたは書き込み UAV アクセスを開始する前に完了する必要があることを示します。 UAV からの 読み取 りのみを行う 2 つの描画呼び出しまたはディスパッチ呼び出しの間に、アプリが UAV バリアを配置する必要がない点を除きます。 また、アプリケーションが UAV アクセスを任意の順序で安全に実行できる場合は、同じ UAV に書き込む 2 つの描画呼び出しまたはディスパッチ呼び出しの間に UAV バリアを配置する必要はありません。 D3D12_RESOURCE_UAV_BARRIER構造体は、バリアが適用される UAV リソースを指定するために使用されます。 アプリケーションはバリアの 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 つだけです。 書き込みビットが設定されている場合、そのリソースに対して読み取り専用ビットを設定することはできません。 書き込みビットが設定されていない場合は、任意の数の読み取りビットを設定できます。

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

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

リソースの破棄

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

暗黙的な状態遷移

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

共通状態の昇格

D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS フラグが設定されたすべてのバッファ リソースおよびテクスチャは、最初の GPU アクセスで、D3D12_RESOURCE_STATE_COMMON から関連する状態へと自動的に昇格されます。これには、あらゆる読み取りシナリオに対応する GENERIC_READ が含まれます。 COMMON 状態のリソースは、まるで単一の状態であるかのようにアクセスできます。

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

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

州旗 バッファーと同時アクセステクスチャ 非同時アクセス テクスチャ
頂点および定数バッファ イエス いいえ
INDEX_BUFFER イエス いいえ
RENDER_TARGET イエス いいえ
UNORDERED_ACCESS イエス いいえ
DEPTH_WRITE(深度書き込み) いいえ* いいえ
DEPTH_READ いいえ* いいえ
NON_PIXEL_SHADER_RESOURCE イエス イエス
PIXEL_SHADER_RESOURCE イエス イエス
ストリームアウト イエス いいえ
間接的な引数 イエス いいえ
COPY_DEST イエス イエス
COPY_SOURCE イエス イエス
RESOLVE_DEST イエス いいえ
ソース解決 イエス いいえ
プレディケーション イエス いいえ

 

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

このアクセスが発生すると、昇格は暗黙的なリソース バリアのように機能します。 後続のアクセスでは、必要に応じてリソースの状態を変更するためにリソース バリアが必要になります。 1 つの昇格された読み取り状態から複数の読み取り状態への昇格は有効ですが、書き込み状態の場合は有効ではありません。
たとえば、共通状態のリソースが Draw 呼び出しで PIXEL_SHADER_RESOURCE に昇格された場合、別の Draw 呼び出しで NON_PIXEL_SHADER_RESOURCE | 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フラグを持つバリアで、保留中の遷移を完了させます。

分割バリアは、状態 A のリソースが後で状態 B で次に使用されるというヒントを GPU に提供します。 これにより、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 リソース状態バリア内の外観