Direct2D パフォーマンスの向上更新日: 2009 年 4 月 28 日 ダウンロードPDC08_Direct2D_Advanced_Performance_Techniques_JPN.docx (Word 形式、95 KB) 目次:
Mike Jacobs 対象:Windows® 7 要約:Direct2D のパフォーマンスを向上する技法について説明します。Direct2D は 2D (2 次元) グラフィックスを作成するための新しいグラフィックス API です。Direct3D 10 に基づく Direct2D は、次世代グラフィックス ハードウェアの能力を活かし、解像度に依存しないイミディエイト モードの 2D API を Win32 開発者に提供します。このホワイト ペーパーでは、2D アプリケーションのパフォーマンスを向上する技法に重点を置いて説明します。Direct2D は大きな API なので、特定のタスクの実現に複数の手法をとることができますが、中にはパフォーマンスの観点からすれば最善の結果をもたらさない手法もあります。この API が実現できるパフォーマンスを活かすため、一般にパフォーマンスが向上する技法に注目します。 法的通知:このドキュメントは暫定版であり、このソフトウェアの最終的な製品版の発売時に実質的に変更されることがあります。 1. はじめにDirect2D は Microsoft® Win32® 開発者向けの次世代 2D API で、高品質のレンダリング機能と高いパフォーマンスを備えています。Direct2D はハードウェア アクセラレータを使いますが、API の構成要素を適切な方法で使用しないと、最適なパフォーマンスが得られない場合があります。希望するレンダリング結果を実現するために、Direct2D ではリソースの使用方法とパフォーマンスとの間でさまざまなトレードオフが可能になっています。さまざまなアプローチの中には、API によって実現できるレベルまでパフォーマンスを高めないものもあります。このドキュメントでは、このようなアプローチを避け、レンダリングが高速な 2D アプリケーションで最高のパフォーマンスを実現する方法を開発者が理解できるようお手伝いします。また、パフォーマンスを向上する技法を、リソースの使用方法、ビットマップ、相互運用性などといった観点に分類して説明します。 ページのトップへ 2. リソースの使用方法リソースを効率的に使用する方法を理解するには、まず、リソースとは何かを理解することが重要です。リソースとは、使用するビデオ メモリまたはシステム メモリのいずれかに行われる、なんらかの割り当てのことです。たとえば、ビットマップやブラシなどです。 Direct2D では、ソフトウェアにもハードウェアにもリソースを作成できます。ハードウェア上でリソースの作成や削除を行うと、ビデオ カードと通信するオーバーヘッドが必要となるため、操作の負荷が高くなります。では、Direct2D でコンテンツを任意のターゲットにレンダリングする方法を見てみましょう。 Direct2D では、すべてのレンダリング コマンドが BeginDraw から EndDraw までの間に発行されます。これにより、Direct2D API によって RenderTarget が使用中であることを示します。レンダリング操作を呼び出す前に BeginDraw メソッドを呼び出す必要があります。BeginDraw を呼び出したら、通常はレンダリング コマンドから成る 1 つのバッチが構築されますが、以下のいずれかの状況が満たされるまで、バッチ内のコマンドの処理が遅延されます。 EndDraw に達する: EndDraw が呼び出されると、あらゆるバッチ描画操作が完了し、操作の状態を返すことになります。 flush() を明示的に呼び出す: flush メソッドは、バッチを処理し、保留中のすべてコマンドを発行します。 レンダリング コマンドを保持するバッファーがいっぱいになる: レンダリング コマンドを保持するバッファーの容量は無限ではないため、上記の 2 つの条件が満たされる前に飽和状態になるとレンダリング コマンドがフラッシュされます。 プリミティブがフラッシュされるまでは、ビットマップやブラシといった対応するリソースへの内部参照は保持されます。 リソースを再利用する前述のように、ハードウェア上でのリソースの作成と削除には大きな負荷がかかります。そのため、できる限り再利用します。現実的なシナリオとして、ゲーム開発のシナリオにおけるビットマップの作成を例にとってみましょう。ほとんどの場合、ゲームのあるシーンを形成するビットマップは、後続のフレームからフレームへのレンダリングに必要になるさまざまなバリエーションすべてと同時に一括して作成されます。実際に当該シーンがレンダリングや再レンダリングされる時点では、それらのビットマップが作り直されるのではなく、単純に再利用されます。 注: この規則はウィンドウのサイズ変更操作には当てはまりません。ウィンドウのコンテンツは、ウィンドウのサイズが変更されるたびに描き直されるため、互換性のあるレンダー ターゲットや、おそらく一部のレイヤー リソースなど、倍率に依存するリソースは作り直す必要があります。このことはレンダリングされるシーン全体の品質を維持するうえで重要です。 フラッシュの使用を制限するflush は、レンダリング コマンドのバッチを処理することになるため、ほとんどの状況では使用を避けます。全般的なガイドラインとして、ほとんどの一般的なシナリオでは Direct2D にリソースの管理を委ねます。 静的コンテンツのレンダリングに関する一般的ガイドライン一般に、静的なコンテンツはフレーム間で変化しないため、互換性のあるレンダー ターゲット、A8 ターゲット、およびメッシュを使用します。 詳しく説明する前に、レンダー ターゲットとは何かを定義しましょう。レンダリングを行うには、アプリケーションから Direct2D RenderTarget にコマンドを発行しなければなりません。RenderTarget は描画操作を受け取り、操作結果を格納します。たとえば、HWND に描画するには ID2D1HwndRenderTarget を作成します。 ここで、それぞれについて詳しく見てみましょう。
ページのトップへ 3. ビットマップ「リソースの使用方法」で説明したように、ハードウェアでのリソースの作成や削除は、非常に負荷の高い操作です。ビットマップは、非常に使用頻度が高い種類のリソースです。ビデオ カード上にビットマップを作成するのは非常に負荷が高くなるため、この場合も再利用することでアプリケーションの動作を速めることができます。 大きなビットマップを作成するビデオ カードには、通常、メモリ割り当ての最小サイズがあります。このサイズよりも小さな割り当てが要求されると、この最小サイズのリソースが割り当てられ、余ったメモリが無駄になり、他の用途にも利用できなくなります。そこで、優れた技法としては、小さなビットマップを多数必要とする場合、大きなビットマップを 1 つ作成し、その中に小さなビットマップ コンテンツをすべて格納するようにします。その後、小さなビットマップを必要とする場所に、大きなビットマップのサブ領域を読み取ることができます。このような技法を一般にアトラスと呼び、ビットマップ作成のオーバーヘッドと、小さなビットマップ割り当てによるメモリの無駄を削減するとメリットがあります。お勧めの技法としては、ビットマップのサイズを 64 KB 以上にし、4 KB 未満のビットマップの数を制限するようにします。 ビットマップのアトラスを作成する"ビットマップのアトラス" が威力を発揮するシナリオはいくつかあります。小さなビットマップを大きなビットマップの内部に格納することができます。そして、このような小さなビットマップは、描画対象の四角形を指定することで、いつでも大きなビットマップから取り出すことができます。たとえば、アプリケーションで複数のアイコンを描画する必要があるとします。このようなアイコン関連のビットマップをあらかじめ大きなビットマップ内に複数格納し、レンダリング時にこの大きなビットマップから取り出して描画することができます。 注: ビデオ メモリ内に作成された Direct2D ビットマップは、格納されているアダプターによってサポートされるビットマップの最大サイズの制約を受けるため、基になるハードウェアによってサポートされているサイズよりも大きなビットマップを作成するとエラーになる場合があります。 共有ビットマップを作成する共有ビットマップを作成すると、呼び出し側が高度な機能を備えている場合、既存のオブジェクトが直接サポートし、レンダー ターゲットと互換性のある Direct2D ビットマップ オブジェクトを作成できます。共有ビットマップにより、複数のサーフェスの作成を回避し、パフォーマンスのオーバーヘッド削減にも役立ちます。 注: 共有ビットマップを使用できるかどうかは、一般に、ソフトウェア、または DXGI と相互運用可能なターゲットに制限されるため、注意が必要です。 ビットマップをコピーするDXGI サーフェスの作成は負荷の高い操作になるため、できる限り既存のサーフェスを再利用するのが望ましいと言えます。また、ソフトウェア内であっても、微細な部分を除いてビットマップがほぼ目的の形式になっている場合は、すべてのビットマップを削除して作り直すよりも、その微細部分を更新する方が適切です。この操作を実現する場合 CreateCompatibleRenderTarget も使用できますが、一般にレンダリングはコピーよりも負荷が高い操作です。これは、キャッシュの局所性を向上するために、ハードウェアは実際には、ビットマップを指定したのと同じ順序でメモリに格納していない (いわば "撹拌" されている) ためです。この撹拌は、ドライバー (処理が遅いため性能の低いコンピューターでの使用する場合のみ)、または GPU のメモリ マネージャーによって CPU からは隠されています。レンダリングの際のレンダー ターゲットへのデータの書き込み方法には制約があるため、サーフェスへのレンダリングが必要ないとわかっている場合は、一般にレンダー ターゲットは撹拌されないか、達成可能な最善の撹拌よりも見劣りする撹拌になります。したがって、ソースから IBitmap に四角形をコピーするための CopyFrom* メソッドが用意されています。 CopyFrom は以下の 3 つのどの形式でも使用できます。
破線にタイル状のビットマップを使用する基になるアルゴリズムの品質と正確性の高さのために、破線のレンダリングは非常に負荷の高い操作になります。直線のジオメトリを含まない場合はたいてい、タイル状のビットマップを使用すると同じ効果が得られます。 ページのトップへ 4. Direct2D でテキストを描画するDirect2D のテキスト レンダリング機能には、以下の 2 種類が用意されています。1 つ目は、RenderTarget->DrawText 呼び出しとして公開され、呼び出し側が文字列や書式設定のパラメーターか、DirectWrite テキスト レイアウトのいずれかを、描画される Direct2D に渡すことができます。これは、大半の呼び出し側にとって適切な方法です。2 つ目は、DrawingContext->DrawGlyphRun 呼び出しとして公開され、レンダリングする字体の位置を既に認識しているユーザーにラスター化を提供するように設計されています。 Direct2D を使用してテキストを描画する際のパフォーマンスを向上するのに役立つ一般的な規則がいくつかあります。 ピクセル形式を把握するレンダー ターゲットを作成するときに、そのレンダー ターゲットで使用されるピクセル形式を指定できます。アルファ チャネルは、カバレッジ値や不透明度に関する情報を指定するピクセル形式の一部です。レンダー ターゲットがアルファ チャネルを使用しない場合は、IGNORE_ALPHA フラグを指定してターゲットを作成します。こうすることで、不要なアルファ チャネルをレンダリングする時間を省きます。 DrawTextLayout と DrawTextアプリケーションでは、DrawText と DrawTextLayout のどちらを使用しても、DirectWrite API によって書式設定されたテキストを簡単にレンダリングすることができます。DrawTextLayout は既存の DWriteTextLayout オブジェクトを RenderTarget に描画しますが、DrawText は渡されたパラメーターに基づいて、呼び出し側向けに DWrite レイアウトを構成します。同じテキストをレンダリングする必要がある場合は、DrawText ではなく DrawTextLayout を使用します。これは、DrawText だと呼び出しが行われるたびに毎回レイアウトが作成されるためです。 A8 レンダー ターゲットにコンテンツをキャッシュするA8 レンダー ターゲットには、8 ビット アルファ チャネルを表すチャネルを 1 つだけ備えたピクセル形式があります。詳細については、「リソースの使用方法」を参照してください。 テキストをアニメーションとして動かすとパフォーマンスが低下する場合、A8 レンダー ターゲットにコンテンツをキャッシュし、FillOpacityMask(Gamma_1_0) を使用して塗りつぶします。この場合、A8 レンダー ターゲットにキャッシュされたコンテンツがビットマップとして描画されるため、処理が速くなります。 注: このモードには Microsoft® ClearType® を利用できないというデメリットがあります。 エイリアス表示テキストとアンチエイリアス表示テキスト一般に、テキストの品質が最優先事項でなければ、アンチエイリアス表示テキストではなくエイリアス表示テキストを使用します。たとえば、テキストが非常に小さく、絶えず前後に動くような場合、それがエイリアス表示なのかアンチエイリアス表示なのかを見分けるのも難しいためです。 ページのトップへ 5. ジオメトリのレンダリングDrawGeometry の代わりに固有の DrawPrimitive を使用する汎用の DrawGeometry の代わりに DrawRectangle のような固有の DrawPrimitive 呼び出しを使用します。DrawRectangle を使用する方が、ジオメトリを既に把握しているためレンダリングが速くなるためです。 詳細定義:クリップ: すべてのレンダリング コマンドがクリップされる四角形領域を指します。 レイヤー: 論理的にはレンダー ターゲット上に合成される、一連の描画プリミティブを保持するリソースの一種です。 クリップを最初の選択肢にする非常に一般的なアプリケーション シナリオを見てみましょう。レンダー ターゲットのある領域をクリップする必要があり、そのクリップされる領域がレンダー ターゲットの軸に沿っているとします。 この場合、レイヤーではなくクリップ四角形を使用する方が適切です。アンチエイリアス表示のジオメトリよりも、エイリアス表示のジオメトリに対するパフォーマンスが向上します。ただし、軸に沿ったクリップ全般には、(不透明マスクやブラシを使用しない) 四角形のジオメトリ マスクのみを備えたクリップ四角形を使用します。 軸に沿っていないジオメトリのクリップは、コンピューターで管理する負荷が非常に高くなります。したがって、レイヤーが唯一の選択肢になります。 レイヤーが役に立つと言っているのではありません。レイヤーは優れた効果を生み出す強力なレンダリング技法を提供しますが、同時にとても負荷の高い技法です。ハードウェア内でもソフトウェア内でも、レイヤーでなければ実現できない効果以外にはレイヤーを使わない、というのが一般的な経験則です。各レイヤーは、PushLayer 関数と PopLayer 関数の間に生成される結果を格納するバッキング サーフェスに関連付けられます。このバッキング サーフェスの作成は、他のサーフェスの作成と同様、本質的に負荷の高い操作です。 ページのトップへ 6. GDI との相互運用性Direct2D は GDI とシームレスに相互運用できます。新しい Direct2D レンダリングを使って既存の GDI アプリケーションを拡張したり、古い GDI コードを新しく Direct2D でレンダリングするアプリケーションに組み込んだりする際に、この相互運用性が非常に役立ちます。 ただし、GDI と Direct2D を同時に操作する場合、留意する点がいくつかあります。 頻繁な切り替えを避けるDirect2D API は、GDI コードと Direct2D コードを容易に混在させることができるように設計されていますが、このような混在を行うとパフォーマンスが大幅に低下するため注意が必要です。 BindDC 関数に対してサイズの大きな四角形は避けるDirect2D レンダー ターゲットに GDI デバイス コンテキストをバインドする場合、BindDC 関数を使用します。この関数は、四角形のサブ領域をパラメーターの 1 つとして受け取ります。最適なパフォーマンスを得るために、このバインドにはサイズが最も小さい四角形を使用します。 BindDC 関数の定義は以下のようになります。
ドライバーの推奨事項ハードウェア内でレンダリングする際に最適なパフォーマンスを得るには、以下のドライバーを使用します。
WDDM 1.1 が現在使用中のドライバーかどうかを調べるには、[スタート] メニューの [ファイル名を指定して実行] ダイアログ ボックスに「DxDiag」と入力します。WDDM 1.1 が存在していると、その番号が明示されます。存在していなければ、何も表示されません。 注: 上記の情報は、以下のように表示されます。 ページのトップへ 7. シーンの複雑性レンダリングが必要なシーンでパフォーマンス上問題となる場所を分析するには、そのシーンがフィル レートによる制約を受けるのか、頂点による制約を受けるのかを調べることで有益な情報を取得できます。
シーンの複雑さを把握するレンダー ターゲットのサイズを変更することで、シーンの複雑性を分析することができます。レンダー ターゲットのサイズを小さくするにつれてパフォーマンスが明らかに向上する場合、アプリケーションはフィル レートの制約を受けています。それ以外の場合は、シーンの複雑さがパフォーマンスのボトルネックです。 上記の観点から、シーンがフィル レートの制約を受けている場合は、レンダー ターゲットのサイズを縮小してパフォーマンスを向上することをお勧めします。これは、レンダー ターゲットのサイズに比例してレンダリングするピクセル数が減少するためです。 頂点による制約を受けるシナリオでパフォーマンスを向上するには、ジオメトリの複雑さを緩和します。ただし、これは品質を犠牲にすることになるため、目標の品質と要求されるパフォーマンスとを注意深く比較検討する必要があります。 ページのトップへ 8. まとめDirect2D はパフォーマンスの向上を目的としたハードウェア アクセラレータですが、パフォーマンスのスループットを最大限に引き出すには、その機能の使用方法を正しく理解することが重要です。また、ここで説明した技法は、一般的なシナリオを基にしているため、すべてのアプリケーションのシナリオに当てはまるものではありません。したがって、アプリケーションの動作を十分理解し、事前に目標とするパフォーマンスを把握することが、望ましい結果の実現につながります。 |
ページのトップへ