Direct3D 12相互運用機能

D3D12 を使用して、コンポーネント化されたアプリケーションを作成できます。

相互運用の概要

D3D12 は非常に強力であり、アプリケーションはコンソールのような効率でグラフィックス コードを記述できますが、すべてのアプリケーションがホイールを再発明し、レンダリング エンジン全体をゼロから書き込む必要はありません。 場合によっては、別のコンポーネントまたはライブラリが既に優れている場合や、コードの一部のパフォーマンスが、その正確性と読みやすさほど重要ではない場合があります。

このセクションでは、次の相互運用手法について説明します。

  • 同じデバイス上の D3D12 と D3D12
  • 異なるデバイス上の D3D12 と D3D12
  • D3D12 と、同じデバイス上の D3D11、D3D10、または D2D の任意の組み合わせ
  • D3D12 と、さまざまなデバイスで D3D11、D3D10、または D2D の任意の組み合わせ
  • D3D12 と GDI、または D3D12 と D3D11 および GDI

相互運用機能を使用する理由

アプリケーションで D3D12 と他の API との相互運用が必要になる理由はいくつかあります。 次に例をいくつか示します。

  • 増分移植: アプリケーション全体を D3D10 または D3D11 から D3D12 に移植し、移植プロセスの中間段階で機能させます (テストとデバッグを有効にします)。
  • ブラック ボックス コード: コードの残りの部分を移植するときに、アプリケーションの特定の部分をそのまま残したい。 たとえば、ゲームの UI 要素を移植する必要がない場合があります。
  • 変更できないコンポーネント: ターゲット D3D12 に書き込まれていない、アプリケーションが所有していないコンポーネントを使用する必要があります。
  • 新しいコンポーネント: アプリケーション全体を移植するのではなく、D3D12 を使用して記述された新しいコンポーネントを使用する必要があります。

D3D12 の相互運用には、次の 4 つのメイン手法があります。

  • アプリは、開いているコマンド リストをコンポーネントに提供することを選択できます。これにより、いくつかの追加のレンダリング コマンドが既にバインドされているレンダー ターゲットに記録されます。 これは、D3D11 の別のコンポーネントに準備されたデバイス コンテキストを提供することと同じであり、既にバインドされているバック バッファーに UI/テキストを追加する場合などに適しています。
  • アプリは、目的のターゲット リソースと共に、コンポーネントにコマンド キューを提供することを選択できます。 これは、D3D11 で ClearState または DeviceContextState API を使用して、クリーンデバイス コンテキストを別のコンポーネントに提供する場合と同じです。 これは、D2D などのコンポーネントの動作方法です。
  • コンポーネントは、コマンド リストを生成するモデルを選択できます。これは、アプリが後で申請を担当する可能性があります。 コンポーネントの境界を越えて少なくとも 1 つのリソースを提供する必要があります。 D3D11 では遅延コンテキストを使用してこの同じ手法を使用できますが、D3D12 のパフォーマンスの方が望ましいです。
  • 各コンポーネントには独自のキューやデバイスがあり、アプリとコンポーネントは、コンポーネントの境界を越えてリソースと同期情報を共有する必要があります。 これは従来 ISurfaceQueueの と似ています。 IDXGIKeyedMutex の方が最新です。

これらのシナリオの違いは、コンポーネントの境界間で正確に共有される点です。 デバイスは共有されているものと見なされますが、基本的にステートレスであるため、実際には関係ありません。 キー オブジェクトは、コマンド リスト、コマンド キュー、同期オブジェクト、およびリソースです。 これらのそれぞれが、それらを共有する際に独自の複雑さを持っています。

コマンド リストの共有

相互運用の最も簡単な方法では、エンジンの一部とコマンド リストのみを共有する必要があります。 レンダリング操作が完了すると、コマンド リストの所有権は呼び出し元に戻ります。 コマンド リストの所有権は、スタックを通じてトレースできます。 コマンド リストはシングル スレッドであるため、この手法を使用してアプリで独自の革新的な操作を実行する方法はありません。

コマンド キューの共有

同じプロセスでデバイスを共有する複数のコンポーネントの最も一般的な手法です。

コマンド キューが共有の単位である場合は、コンポーネントを呼び出して、未処理のすべてのコマンド リストをすぐにコマンド キューに送信する必要があることを通知する必要があります (また、内部コマンド キューを同期する必要があります)。 これは D3D11 Flush API と同等であり、アプリケーションが独自のコマンド リストまたは同期プリミティブを送信できる唯一の方法です。

同期プリミティブの共有

独自のデバイスやコマンド キューで動作するコンポーネントに対して想定されるパターンは、作業の開始時に ID3D12Fence または共有ハンドルと UINT64 ペアを受け入れることです。このペアは待機し、次に 2 番目の ID3D12Fence または共有ハンドル、およびすべての作業が完了したときに通知する UINT64 ペアを受け入れます。 このパターンは、 IDXGIKeyedMutex と DWM/DXGI フリップ モデル同期設計の両方の現在の実装と一致します。

リソースを共有する

複数のコンポーネントを活用する D3D12 アプリを作成する最も複雑な部分は、コンポーネントの境界を越えて共有されるリソースを処理する方法です。 これは主にリソース状態の概念によるものです。 リソース状態設計の一部の側面はコマンド リスト内の同期に対処するためのものですが、コマンド リスト間に影響を与えるものもあります。リソース レイアウトと、リソース データにアクセスする有効な一連の操作またはパフォーマンス特性に影響します。

この複雑な問題に対処するには 2 つのパターンがありますが、どちらも本質的にコンポーネント間の契約を伴います。

  • コントラクトは、コンポーネント開発者が定義し、文書化できます。 これは、"作業の開始時にリソースが既定の状態である必要があり、作業が完了したときに既定の状態に戻される" などの単純な場合や、中間深度解決を強制せずに深度バッファーを共有できるようにする、より複雑なルールが存在する可能性があります。
  • コントラクトは、リソースがコンポーネントの境界を越えて共有されるときに、実行時にアプリケーションによって定義できます。 これは、同じ 2 つの情報で構成されます。リソースが使用を開始したときにリソースが存在する状態と、コンポーネントが終了したときにそれを残す状態です。

相互運用モデルの選択

ほとんどの D3D12 アプリケーションでは、コマンド キューの共有が理想的なモデルである可能性があります。 これにより、作業の作成と送信の完全な所有権が可能になり、冗長なキューを持つことによる追加のメモリ オーバーヘッドがなく、GPU 同期プリミティブの処理によるパフォーマンスへの影響もありません。

同期プリミティブの共有は、コンポーネントが種類や優先度などのさまざまなキュー プロパティを処理する必要がある場合、または共有がプロセスの境界をまたがる必要がある場合に必要です。

コマンド リストの共有または生成は、サード パーティのコンポーネントによって外部で広く使用されるわけではありませんが、ゲーム エンジンの内部にあるコンポーネントで広く使用されている可能性があります。

相互運用 API

12 の Direct3D 11 トピックでは、このトピックで説明する相互運用の種類に関連する API サーフェスの多くを使用する手順について説明します。

また、WINDOWS グラフィックス API 間でサーフェスを共有するために使用できる ID3D12Device::CreateSharedHandle メソッドも参照してください。