非同期プログラミング (DirectX および C++)
このトピックでは、DirectX で非同期プログラミングとスレッド処理を使用する場合に考慮すべきさまざまな点について説明します。
非同期プログラミングと DirectX
DirectX について学習しているだけの場合、または経験がある場合でも、すべてのグラフィックス処理パイプラインを 1 つのスレッドに配置することを検討してください。 ゲーム内の特定のシーンには、ビットマップ、シェーダー、排他アクセスを必要とするその他のアセットなどの一般的なリソースがあります。 これらの同じリソースでは、並列スレッド間でこれらのリソースへのアクセスを同期する必要があります。 レンダリングは、複数のスレッド間で並列化するのは困難なプロセスです。
ただし、ゲームが十分に複雑な場合、またはパフォーマンスを向上させる場合は、非同期プログラミングを使用して、レンダリング パイプラインに固有ではないコンポーネントの一部を並列化できます。 最新のハードウェアには複数のコア CPU とハイパースレッド CPU が搭載されており、アプリはこれを活用する必要があります。 これを確認するには、次のような Direct3D デバイス コンテキストに直接アクセスする必要のないゲームの一部のコンポーネントに対して非同期プログラミングを使用します。
- ファイル I/O
- physics
- AI
- networking
- audio
- controls
- XAML ベースの UI コンポーネント
アプリは、複数の同時実行スレッドでこれらのコンポーネントを処理できます。 ファイル I/O (特に資産の読み込み) は、ゲームまたはアプリが対話型状態になる可能性があり、数 MB (または数百) の資産が読み込まれたりストリーミングされたりするため、非同期読み込みによって大きなメリットがあります。 これらのスレッドを作成および管理する最も簡単な方法は、 Parallel Patterns Library と task パターンを使用することです。これは、PPLTasks.h で定義されている concurrency 名前空間に含まれています。 Parallel Patterns Library を使用するとは複数のコア CPU とハイパースレッド化された CPU を直接利用し、認識される読み込み時間から、集中的な CPU 計算やネットワーク処理に伴う遅延や遅延まで、すべてを改善できます。
注 ユニバーサル Windows プラットフォーム (UWP) アプリでは、ユーザー インターフェイスは完全にシングルスレッド アパートメント (STA) で実行されます。 XAML 相互運用機能を使用して DirectX ゲームの UI を作成する場合は、STA を使用してのみコントロールにアクセスできます。
Direct3D デバイスを使用したマルチスレッド
デバイス コンテキストのマルチスレッドは、Direct3D 機能レベル 11_0 以降をサポートするグラフィックス デバイスでのみ使用できます。 ただし、専用のゲーム プラットフォームなど、多くのプラットフォームで強力な GPU を最大限に使用したい場合があります。 最も簡単なケースでは、ヘッドアップ ディスプレイ (HUD) オーバーレイのレンダリングを 3D シーンのレンダリングとプロジェクションから分離し、両方のコンポーネントに個別の並列パイプラインを使用させる必要がある場合があります。 どちらのスレッドも同じ ID3D11DeviceContext を使用してリソース オブジェクト (テクスチャ、メッシュ、シェーダー、およびその他のアセット) を作成および管理する必要があります。ただし、これはシングル スレッドであり、安全にアクセスするために何らかの同期メカニズム (重要なセクションなど) を実装する必要があります。 また、異なるスレッド (遅延レンダリング用) でデバイス コンテキスト用に個別のコマンド リストを作成できますが、同じ ID3D11DeviceContext インスタンスでこれらのコマンド リストを同時に再生することはできません。
アプリでは、マルチスレッドに安全な ID3D11Device を使用してリソース オブジェクトを作成することもできます。 では、ID3D11DeviceContext の代わりに常に ID3D11Device を使用しないのはなぜですか。 現時点では、一部のグラフィックス インターフェイスでは、マルチスレッドのドライバーサポートが利用できない場合があります。 デバイスに対してクエリを実行して、マルチスレッドがサポートされているかどうかを調べることもできますが、最も広範な対象ユーザーに到達しようとしている場合は、リソース オブジェクト管理のためにシングルスレッドの ID3D11DeviceContext を使用できます。 つまり、グラフィックス デバイス ドライバーがマルチスレッドまたはコマンド リストをサポートしていない場合、Direct3D 11 はデバイス コンテキストへの同期されたアクセスを内部的に処理しようとします。コマンド リストがサポートされていない場合は、ソフトウェアの実装が提供されます。 その結果、マルチスレッド デバイス コンテキスト アクセスのドライバーサポートがないグラフィックス インターフェイスを持つプラットフォームで実行されるマルチスレッド コードを記述できます。
アプリでコマンド リストの処理とフレームの表示用に個別のスレッドがサポートされている場合は、GPU をアクティブにし、コマンド リストを処理しながらフレームを適切なタイミングで表示しながら、目に見える吃音や遅延を発生させないようにする必要があります。 この場合、スレッドごとに別個の ID3D11DeviceContext を使用し、D3D11_RESOURCE_MISC_SHARED フラグを指定してリソース作成することによって、リソース (テクスチャなど) を共有します。 このシナリオでは、 ID3D11DeviceContext::Flush を処理スレッドで呼び出して、表示スレッドでリソース オブジェクトの処理結果を表示する前にコマンド リストの実行を完了する必要があります。
遅延レンダリング
遅延レンダリングでは、グラフィックス コマンドをコマンド リストに記録して、他のタイミングで再生できるようにし、追加のスレッドでレンダリングするためのコマンドを記録しながら、1 つのスレッドでのレンダリングをサポートするように設計されています。 これらのコマンドが完了したら、最終的な表示オブジェクト (フレーム バッファー、テクスチャ、またはその他のグラフィックス出力) を生成するスレッドで実行できます。
ID3D11Device::CreateDeferredContext (D3D11CreateDevice または D3D11CreateDeviceAndSwapChain を使用して遅延コンテキストを作成し、即時コンテキストを作成します)。 詳細については、「 Immediate and Deferred Renderingを参照してください。
関連トピック