リソースの有効期間と同期

Direct3D 12 と同じく、DirectML アプリケーションは (未定義の動作を回避するために) オブジェクトの有効期間と CPU/GPU 間の同期を正しく管理する必要があります。 DirectML では、Direct3D 12 と同じリソース有効期間モデルに従います。

  • 2 つの CPU オブジェクト間の有効期間依存関係は、DirectML によって、強い参照カウントを使用して維持されます。 アプリケーションの開発者が CPU の有効期間依存関係を手動で管理する必要はありません。 たとえば、デバイスのすべての子は、その親デバイスへの強い参照を保持します。
  • GPU オブジェクト間の有効期間依存関係 (または CPU と GPU にまたがる依存関係) は、自動的には管理されません。 GPU リソースは、少なくともそのリソースを使用するすべての作業が GPU 上で実行完了となるまで有効でなければなりませんが、このことを確実にするのはアプリケーションの責任です。

DirectML デバイス

DirectML デバイスは、スレッド セーフなステートレス ファクトリ オブジェクトです。 デバイスのすべての子 (IDMLDeviceChild を参照) は、その親 DirectML デバイス (IDMLDevice を参照) への強い参照を保持します。 つまり、親デバイス インターフェイスはいつでも、どのデバイス子インターフェイスからでも取得できます。

同様に、DirectML デバイスは、その作成に使用された Direct3D 12 デバイスへの強い参照を保持します (ID3D12Device と、派生したインターフェイスを参照)。

DirectML デバイスはステートレスであるため、暗黙的にスレッド セーフになります。 DirectML デバイスに対するメソッドを複数のスレッドから同時に呼び出すときに、外部の同期は必要ありません。

しかし、Direct3D 12 デバイスとは異なり、DirectML デバイスはシングルトン オブジェクトではありません。 作成できる DirectML デバイスの数に制限はありません。 ただし、それぞれ別のデバイスに属している子を混在させることはできません。 たとえば、IDMLBindingTableIDMLCompiledOperator の 2 つはデバイスの子の種類です (どちらのインターフェイスも直接または間接的に IDMLDeviceChild から派生します)。 バインド テーブル (IDMLBindingTable) を使用して演算子 (IDMLCompiledOperator) のバインドを行うときに、この演算子とバインド テーブルがそれぞれ別の DirectML デバイス インスタンスに属していてはなりません。

DirectML デバイスはシングルトンではないため、デバイス削除はデバイス単位で発生します。つまり、Direct3D 12 デバイスのようなプロセス レベルのイベントではありません。 詳細については、「DirectML でのエラーとデバイス削除の処理」を参照してください。

GPU リソースの有効期間の要件

Direct3D 12 と同様に、DirectML では CPU と GPU の同期が自動的には行われません。また、リソースが GPU で使用されている間はそのリソースを確実に存続させることも自動的には行われません。 代わりに、これらを行うのはアプリケーションの責任です。

実行するコマンド リストの中に DirectML ディスパッチがあるときは、GPU リソースを使用する作業の GPU での実行が完了するまでそのリソースが存続していることを、アプリケーションが保証する必要があります。

IDMLCommandRecorder::RecordDispatch を DirectML 演算子に対して実行する場合は、次のオブジェクトが対象となります。

  • 実行される IDMLCompiledOperator (演算子の初期化を実行する場合は、代わりに IDMLOperatorInitializer)。
  • 演算子のバインドに使用されるバインド テーブルをバッキングする IDMLCompiledOperator
  • 演算子の入力/出力としてバインドされる ID3D12Resource オブジェクト。
  • 永続的または一時的なリソースとしてバインドされる ID3D12Resource オブジェクト (該当する場合)。
  • コマンド リストそのものをバッキングする ID3D12CommandAllocator

すべての DirectML インターフェイスが GPU リソースを表しているわけではありません。 たとえば、あるバインド テーブルを使用するすべてのディスパッチが GPU での実行を完了するまで、そのバインド テーブルを存続させておく必要はありません。 このような理由から、バインド テーブルそのものは GPU リソースを一切所有しません。 代わりに、記述子ヒープが所有します。 したがって、実行が完了するまで存続している必要があるオブジェクトは、基になる記述子ヒープであり、バインド テーブルそのものではありません。

同様の概念が Direct3D 12 にも存在します。 コマンドのアロケーターは、これを使用する実行が GPU 上ですべて完了するまで存続している必要があります。このアロケーターが GPU メモリを所有しているからです。 しかし、コマンド リストは GPU メモリを所有しているわけではないため、実行のために提出された後はすぐにリセットまたは解放することができます。

DirectML では、コンパイルされた演算子 (IDMLCompiledOperator) と演算子初期化子 (IDMLOperatorInitializer) のどちらも GPU リソースを直接所有するため、これらを使用するディスパッチがすべて GPU での実行を完了するまで存続している必要があります。 さらに、Direct3D 12 のリソース (コマンド アロケーター、記述子ヒープ、バッファーなど) を使用する場合は、これらのリソースも同様に、アプリケーションの責任で存続させる必要があります。

オブジェクトがまだ GPU で使用されているにもかかわらず、そのオブジェクトを解放すると、その結果は未定義の動作となり、デバイスの削除などのエラーが発生する可能性があります。

CPU と GPU の同期

DirectML 自身が、GPU で実行するための作業を提出することはありません。 代わりに、IDMLCommandRecorder::RecordDispatch メソッドがその作業のディスパッチをコマンド リストとして記録し、これが後で実行されます。 アプリケーションでは、そのコマンド リストを閉じて実行用に提出するために ID3D12CommandQueue::ExecuteCommandLists を呼び出す必要があります。つまり、他の Direct3D 12 コマンド リストと同様です。

DirectML 自身が GPU での実行用に作業を提出することはないため、フェンスを作成することはなく、形式を問わずどのような CPU/GPU 同期もアプリケーションに代わって行うことはありません。 提出された作業の実行が GPU 上で完了するまで待機する必要がある場合は、アプリケーションの責任で、適切な Direct3D 12 プリミティブを使用して待機する必要があります。 詳細については、ID3D12FenceID3D12CommandQueue::Signal を参照してください。

関連項目