非同期プログラミング (DirectX と C++)

Applies to Windows and Windows Phone

このトピックでは、DirectX で非同期プログラミングやスレッディングを使う際のさまざまな考慮事項について取り上げます。DirectX に限定されない一般的な非同期プログラミング パターンについては、「C++ を使った非同期プログラミング」をご覧ください。

(JavaScript 言語と .NET 言語による非同期プログラミングについて詳しくは、「JavaScript での非同期プログラミング」と「.NET での非同期プログラミング」をご覧ください。)

非同期プログラミングと DirectX

DirectX の学習経験や使用経験にかかわらず、グラフィックス処理パイプラインはすべてシングル スレッドで実行することをお勧めします。 ゲーム内のシーンには、排他的アクセスを必要とするリソースがいたるところに使われています。ビットマップやシェーダーといった各種アセットはその代表的な例です。しかも、これらのリソースへのアクセスは、並列スレッド間ですべて同期させる必要があります。レンダリングは、複数のスレッドで並列化することの難しい処理です。

しかし、ゲームの構造に十分な複合性がある場合や、ゲームのパフォーマンスを強化する必要性がある場合、レンダリング パイプラインに直接関係しない一部のコンポーネントを非同期プログラミングによって並列化することは可能です。最近のハードウェアは、マルチ コアとハイパースレッディングに対応した CPU を搭載しているので、ぜひそれを活かしましょう。その確実な方法は、ゲームのコンポーネントのうち、Direct3D のデバイス コンテキストに直接アクセスする必要のないコンポーネントに非同期プログラミングを適用することです。たとえば、次のようなコンポーネントが該当します。

  • ファイル I/O
  • 物理
  • AI
  • ネットワーク
  • オーディオ
  • コントロール
  • XAML ベースの UI コンポーネント

こうしたコンポーネントについては、複数の同時実行スレッド上で処理することができます。ファイル I/O (特にアセットの読み込み) を非同期で行うと、数メガバイトや数百メガバイトのアセットを読み込んだりストリーミングしている間も、ゲームやアプリのインターフェイスを操作できるようになるため、マルチ スレッド化には非常に大きな意義があります。これらのスレッドは、並列パターン ライブラリと task パターン (PPLTasks.h に定義されている concurrency 名前空間) を使って作成、管理するのが最も簡単です。並列パターン ライブラリは、マルチ コアとハイパースレッディングに対応した CPU の利点をダイレクトに引き出し、体感的な読み込み時間から、CPU 計算やネットワーク処理の集中に伴う滞りや遅延にいたるまで、さまざまな側面を向上させます。

並列パターン ライブラリを使った非同期プログラミングについて詳しくは、「C++ を使った非同期プログラミング」をご覧ください。

次のコード サンプルは、DDS テクスチャの非同期読み込みのデモです。

  Windows ストア アプリのユーザー インターフェイスは、完全にシングルスレッド アパートメント (STA) で実行されます。DirectX ゲーム用の UI を XAML の相互運用機能を使って作成する場合、そのコントロールには、STA を使ってのみアクセスできます。

Direct3D デバイスでのマルチスレッド化

デバイス コンテキストのマルチスレッド化は、Direct3D 機能レベル 11_0 以上をサポートするグラフィックス デバイスでしか利用できません。しかし、ゲーム専用機など、数多くのプラットフォームに搭載されている強力な GPU は最大限に活用したいものです。たとえば、単純なケースでは、ヘッドアップ ディスプレイ (HUD) オーバーレイのレンダリングを 3D シーンのレンダリングとプロジェクションから切り離し、2 つのコンポーネントに独立した並列パイプラインを割り当てることができます。どちらのスレッドも、同じ ID3D11DeviceContext を使ってリソース オブジェクト (テクスチャ、メッシュ、シェーダーなど、各種アセット) を作成、管理する必要がありますが、ID3D11DeviceContext はシングル スレッドであり、安全にアクセスするには何らかの同期機構 (クリティカル セクションなど) を実装する必要があります。また、異なるスレッドのデバイス コンテキストに対し、(遅延レンダリング用の) 別々のコマンド リストを作成している間、これらのコマンド リストを同じ ID3D11DeviceContext インスタンスで同時に再生することはできません。

現在では、スレッド セーフな ID3D11Device を使ってリソース オブジェクトを作成することもできるようになりました。それならば、ID3D11DeviceContext は使わず、常に ID3D11Device を使えばよいのではないでしょうか。現時点では、一部のグラフィックス インターフェイスで、ドライバーがマルチスレッドをサポートしていない可能性があります。デバイスに対して問い合わせを行い、マルチスレッドをサポートしているかどうかを調べることもできますが、幅広いユーザーを対象とするために、あえてシングル スレッドの ID3D11DeviceContext を使って、リソース オブジェクトを管理する場合があります。もっとも、グラフィックス デバイス ドライバーがマルチスレッドやコマンド リストをサポートしていない場合、Direct3D 11 は、デバイス コンテキストへの同期アクセスを内部的に処理することを試み、コマンド リストがサポートされていなかった場合は、ソフトウェアによって実装された同等の機能を提供します。結果として、プラットフォームのグラフィックス インターフェイスについて、デバイス コンテキストへのマルチスレッド アクセスをドライバーがサポートしていない場合でもきちんと動作するマルチスレッド対応のコードを作成することが可能です。

開発しているアプリが、コマンド リストの処理用とフレームの表示用に別々のスレッドをサポートする場合、GPU はアクティブにしておきましょう。体感的な引っかかりや遅延を生じることなく適切なタイミングでフレームを表示すると共に、コマンド リストを処理することができます。この場合、スレッドごとに別個の ID3D11DeviceContext を使い、D3D11_RESOURCE_MISC_SHARED フラグを使ってリソース作成することによって、リソース (テクスチャなど) を共有します。このシナリオでは、処理用のスレッドで ID3D11DeviceContext::Flush を呼び出し、コマンド リストの実行を完了してから、表示用のスレッドで、リソース オブジェクトの処理結果を表示する必要があります。

遅延レンダリング

遅延レンダリングは、グラフィックス コマンドを後から再生できるようにコマンド リストに記録するもので、一方のスレッドでレンダリングを行いながら、別のスレッドで、レンダリングに使うコマンドを記録するしくみになっています。必要なコマンドがすべて揃った後、最終的な表示オブジェクト (フレーム バッファー、テクスチャなどのグラフィックス出力) を生成したスレッド上で、それらのコマンドを実行することができます。

遅延コンテキストは、(イミディエイト コンテキストを作成する) D3D11CreateDeviceD3D11CreateDeviceAndSwapChain ではなく、ID3D11Device::CreateDeferredContext を使って作成します。詳しくは、「イミディエイト レンダリングおよびディファード レンダリング」をご覧ください。

関連トピック

C++ を使った非同期プログラミング
アプリの開発 (C++ と DirectX)
Direct3D 11 でのマルチスレッドの概要
Windows ストア アプリのコードの記述 (DirectX と C++)

 

 

表示:
© 2014 Microsoft