リソース データのコピーとアクセス (Direct3D 10)
リソースは、ビデオ メモリまたはシステム メモリに作成されていると考える必要がなくなりました。 または、ランタイムがメモリを管理する必要があるかどうか。 新しい WDDM (Windows ディスプレイ ドライバー モデル) のアーキテクチャにより、アプリケーションは異なる 使用 フラグを持つ Direct3D 10 リソースを作成して、アプリケーションがリソース データをどのように使用しようとしているかを示すようになりました。 新しいドライバー モデルは、リソースによって使用されるメモリを仮想化します。その後、オペレーティング システム/ドライバー/メモリ マネージャーは、予想される使用量を考えると、可能な限り最もパフォーマンスの高いメモリ領域にリソースを配置する必要があります。
既定のケースは、GPU で使用できるリソースを対象としています。 もちろん、リソース データを CPU で使用できる必要がある場合もあります。 パフォーマンスに影響を与えることなく、適切なプロセッサがリソース データにアクセスできるようにリソース データをコピーするには、API メソッドの仕組みに関する知識が必要になります。
リソース データのコピー
Direct3D が Create の呼び出しを実行すると、メモリ内にリソースが作成されます。 それらは、ビデオ メモリ、システム メモリ、または他の任意の種類のメモリに作成することができます。 WDDM ドライバー モデルはこのメモリを仮想化するため、アプリケーションはリソースが作成されるメモリの種類を把握する必要がありません。
理想的には、GPU が直ちにアクセスできるように、すべてのリソースをビデオ メモリに配置します。 ただし、CPU がリソース データを読み取ったり、CPU が書き込んだリソース データに GPU がアクセスしたりする必要がある場合があります。 Direct3D 10 では、アプリケーションに使用量の指定を要求することで、これらのさまざまなシナリオを処理し、必要に応じてリソース データをコピーするためのいくつかの方法を提供します。
リソースの作成方法によっては、そのデータに直接アクセスできないことがあります。 つまり、ソース リソースから適切なプロセッサーがアクセスできる別のリソースに、リソース データをコピーする必要があることを意味します。 Direct3D 10 では、既定のリソースには GPU から直接アクセスでき、動的リソースとステージング リソースには CPU から直接アクセスできます。
リソースが作成されると、その 使用状況 を変更することはできません。 その代わりに、あるリソースの内容を別の使用法で作成された別のリソースにコピーします。 Direct3D 10 では、この機能に 3 つの異なる方法が用意されています。 最初の 2 つのメソッド ( ID3D10Device::CopyResource と ID3D10Device::CopySubresourceRegion) は、あるリソースから別のリソースにリソース データをコピーするように設計されています。 3 番目のメソッド (ID3D10Device::UpdateSubresource) は、メモリからリソースにデータをコピーするように設計されています。
リソースには主に 2 種類があります。マッピング可能なものと、マッピング不可能なものです。 動的またはステージングの使用法で作成されたリソースはマッピング可能ですが、既定または固定の使用法で作成されたリソースはマッピング不可能です。
マッピング不可能なリソース間でのデータのコピーは、最も一般的なケースであり、適切に実行されるよう最適化されているため、非常に高速です。 これらのリソースは CPU から直接アクセスできないため、GPU から高速に操作できるように最適化されています。
マッピング可能なリソース間でのデータのコピーは、リソースの作成時の使用法によりパフォーマンスが異なるので、より問題が生じやすくなります。 たとえば、GPU は動的リソースをかなり高速で読み取ることができますが、書き込むことができません。また、GPU はステージング リソースの直接読み取り、または書き込みができません。
既定の使用状況を持つリソースからステージング使用量のリソースにデータをコピーするアプリケーション (CPU がデータを読み取れるようにするため、つまり GPU の読み取りバックの問題) は注意して行う必要があります。 この最後のケースの詳細については、「 リソース データへのアクセス 」を参照してください。
リソース データへのアクセス
リソースにアクセスするには、リソースをマッピングする必要があります。マッピングとは、基本的には、アプリケーションが CPU からメモリへのアクセスを行おうとすることです。 CPU から基になるメモリへアクセスできるようリソースをマッピングするプロセスでは、何らかのパフォーマンス ボトルネックが発生する場合があります。このため、このタスクをいつどのように実行するかについて、注意を払う必要があります。
アプリケーションが不適切なときにリソースをマッピングしようとすると、パフォーマンスが大幅に低下することがあります。 ある操作が終了する前に、アプリケーションがその操作の結果にアクセスしようとすると、パイプライン ストールが発生します。
不適切なときにマッピング操作を実行すると、GPU および CPU が互いに強制的に同期させられ、パフォーマンスの深刻な低下を引き起こす潜在的な可能性があります。 この同期化は、CPU がマッピング可能なリソースに GPU がリソースをコピーし終わる前に、アプリケーションがそのリソースにアクセスしようとすると発生します。
CPU は、D3D10_USAGE_STAGING フラグを使用して作成されたリソースからのみ読み取ることができます。 このフラグを使用して作成されたリソースはパイプラインの出力として設定できないため、CPU が GPU によって生成されたリソース内のデータを読み取る場合は、ステージング フラグを使用して作成されたリソースにデータをコピーする必要があります。 アプリケーションでは、 ID3D10Device::CopyResource メソッドまたは ID3D10Device::CopySubresourceRegion メソッドを使用して、1 つのリソースの内容を別のリソースにコピーすることでこれを行うことができます。 その後、アプリケーションは、適切な Map メソッドを呼び出すことによって、このリソースにアクセスできます。 リソースへのアクセスが不要になったら、アプリケーションは対応する Unmap メソッドを呼び出す必要があります。 たとえば、 ID3D10Texture2D::Map と ID3D10Texture2D::Unmap です。 異なる Map メソッドは、入力フラグに応じていくつかの特定の値を返します。 詳細については、「 マップの解説」セクション を参照してください。
注意
アプリケーションが Map メソッドを呼び出すと、アクセスするリソース データへのポインターを受け取ります。 ランタイムは、 機能レベルに応じて、ポインターが特定の配置を持っていることを確認します。 D3D_FEATURE_LEVEL_10_0以降の場合、ポインターは 16 バイトにアラインされます。 D3D_FEATURE_LEVEL_10_0未満の場合、ポインターは 4 バイトにアラインされます。 16 バイトのアラインメントにより、アプリケーションは再調整やコピーを行わずに、データに対して SSE 最適化操作をネイティブに実行できます。
パフォーマンスに関する考慮事項
PC は、主に 2 種類のプロセッサを含む並列アーキテクチャとして動作しているマシンと考えてください。2 種類のプロセッサとは、1 つ以上の CPU のものと、1 つ以上の GPU のものです。 どの並列アーキテクチャでも、最高のパフォーマンスが得られるのは、各プロセッサがアイドル状態にならないように十分なタスクがスケジュールされているときと、1 つのプロセッサの仕事が別のプロセッサの仕事を待機していないときです。
GPU および CPU の並列処理の最悪のケースのシナリオは、あるプロセッサが別のプロセッサが行った仕事の結果を待機する必要があるという場合です。 Direct3D 10 は 、ID3D10Device::CopyResource メソッドと ID3D10Device::CopySubresourceRegion メソッドを 非同期にすることで、このコストを削除しようとします。コピーは、必ずしもメソッドが返すまでに実行されるとは限りません。 この方法の利点は、Map が呼び出されて CPU がデータにアクセスするまで、データを実際にコピーするというパフォーマンスのコストをアプリケーションが負担しないことです。 データが実際にコピーされた後に Map メソッドが呼び出されると、パフォーマンスの損失が発生しません。 一方、データがコピーされる前に Map メソッドが呼び出される場合は、パイプライン ストールが発生します。
Direct3D 10 の非同期呼び出し (メソッドの大部分、特にレンダリング呼び出し) は、コマンド バッファーと呼ばれるものに格納されます。 このバッファーは、グラフィックス ドライバー内部にあり、Microsoft Windows のユーザー モードからカーネル モードへの負荷の大きい切り替えができるだけ発生しないように、基になるハードウェアへの呼び出しをバッチ処理するために使用されます。
以下の 4 つの状況のいずれかで、コマンド バッファーがフラッシュされ、ユーザー モードとカーネル モードの切り替えが発生します。
- Present が呼び出されます。
- ID3D10Device::Flush が呼び出されます。
- コマンド バッファーがいっぱいです。コマンド バッファーのサイズは動的で、オペレーティング システムとグラフィックス ドライバーで制御されます。
- CPU は、コマンド バッファーで実行を待っているコマンドの結果にアクセスする必要があります。
この 4 つの状況で、4 番目がパフォーマンスにおいて最も重要です。 アプリケーションが ID3D10Device::CopyResource または ID3D10Device::CopySubresourceRegion 呼び出しを発行した場合、この呼び出しはコマンド バッファーでキューに入れられます。 その後、コマンド バッファーがフラッシュされる前にコピー呼び出しのターゲットであったステージング リソースをアプリケーションがマップしようとすると、Copy メソッド呼び出しを実行する必要があるだけでなく、コマンド バッファー内の他のすべてのバッファー内のコマンドも実行する必要があるため、パイプラインの停止が発生します。 れにより、GPU がコマンド バッファーを空にし、CPU が必要とするリソースに最終的にデータを格納する間、CPU はステージング リソースへのアクセスを待機するので、GPU と CPU の同期が発生します。 GPU がコピーを終了すると、CPU はステージング リソースへのアクセスを開始します。しかしこの間、GPU はアイドル状態となります。
ランタイムにこれを頻繁に行うと、パフォーマンスが著しく低下します。 そのため、既定の使用法で作成されたリソースのマッピングは、注意して行う必要があります。 アプリケーションは、コマンド バッファーが空になるのに十分な時間待機し、対応するステージング リソースにマッピングしようとする前に、こうしたコマンドをすべて実行し終える必要があります。 アプリケーションはどれだけ待機すればよいのでしょうか。 少なくとも 2 フレームです。これにより CPU と GPU 間の並列処理が最大限利用できるからです。 GPU がどのように動作するかというと、アプリケーションがコマンド バッファーに呼び出しを送信することにより N 番目のフレームを処理する間、GPU は前のフレームである N-1 番目からの呼び出しの実行でビジー状態です。
そのため、アプリケーションがビデオ メモリから生成されたリソースをマップし、フレーム N で ID3D10Device::CopyResource または ID3D10Device::CopySubresourceRegion を 呼び出す場合、この呼び出しは、アプリケーションが次のフレームの呼び出しを送信するときに、フレーム N+1 で実際に実行を開始します。 コピーは、アプリケーションが N + 2 番目のフレームを処理しているときに完了します。
フレーム | GPU/CPU の状態 |
---|---|
× |
|
N+1 |
|
N+2 |
|
N+3 |
|
N+4 | ... |
関連トピック