手記
Windows 10 上のアプリでは、DirectComposition の代わりに Windows.UI.Composition API を使用することをお勧めします。 詳細については、「ビジュアル レイヤーを使用してデスクトップ アプリをモダン化する」を参照してください。
このトピックでは、Microsoft DirectComposition を構成するコンポーネントについて説明します。 次のセクションで構成されます。
ソフトウェア コンポーネント
DirectComposition は、次の主要なソフトウェア コンポーネントで構成されます。
- コンポーネント オブジェクト モデル (COM) ベースのパブリック API を実装するユーザー モード アプリケーション ライブラリ (dcomp.dll)。
- デスクトップ ウィンドウ マネージャー (DWM) プロセス (dwm.exe) でホストされ、実際のデスクトップコンポジションを実行するユーザー モードコンポジション エンジン (dwmcore.dll)。
- アプリケーションからコンポジション エンジンにコマンドをマーシャリングするカーネル モード オブジェクト データベース (win32k.sysの一部)。
コンポジション エンジンの 1 つのインスタンスは、すべてのアプリケーションの DirectComposition コンポジション ツリーと、デスクトップ全体を表す DWM コンポジション ツリーを処理します。 カーネル モード オブジェクト データベースとユーザー モードコンポジション エンジンの両方がセッションごとに 1 回インスタンス化されるため、複数のユーザーを持つターミナル サーバー マシンには、両方のコンポーネントの複数のインスタンスがあります。
次の図は、DirectComposition の主要なコンポーネントと、それらが相互にどのように関連しているかを示しています。
直接配置の最上位アーキテクチャする
アプリケーション ライブラリ
DirectComposition アプリケーション ライブラリは、dcomp.dll からエクスポートされ、デバイス オブジェクトへのインターフェイス ポインターを返す単一のフラット エントリ ポイントを持つパブリック COM ベースの API です。 さらに、デバイス オブジェクトには他のすべてのオブジェクトを作成するためのメソッドがあり、各オブジェクトはインターフェイス ポインターで表されます。 すべての DirectComposition インターフェイスは、IUnknown インターフェイスを継承し、完全に実装します。 DirectComposition インターフェイスを受け入れるすべてのメソッドは、インターフェイスが dcomp.dll 内で実装されているかどうか、または別のコンポーネントによって実装されているかどうかを確認します。 DirectComposition は拡張可能ではないため、インターフェイスをパラメーターとして受け取るメソッドは、インターフェイスが dcomp.dllに実装されていない場合E_INVALIDARG返します。 API には特別な特権は必要ありません。これは、最も低いレベルのアクセスで実行されているプロセスによって呼び出すことができます。 ただし、API はセッション 0 では動作しないため、サービスには適していません。 これらの点で、DirectComposition API は他の Microsoft DirectX API 、特に Direct2D、Microsoft Direct3D、および Microsoft DirectWrite に似ています。
コンポジション エンジンは非同期実行専用に設計されているため、DirectComposition API のオブジェクト プロパティは書き込み専用です。 すべてのプロパティにはセッター メソッドがありますが、getter メソッドはありません。 プロパティの読み取りはリソースを大量に消費するだけでなく、コンポジション エンジンが返す値がすぐに無効になる可能性があるため、不正確になる可能性もあります。 これは、たとえば、独立したアニメーションが読み取られるプロパティにバインドされている場合に発生する可能性があります。
API はスレッド セーフです。 アプリケーションは、任意のスレッドからいつでも任意のメソッドを呼び出すことができます。 ただし、多くの API メソッドは特定のシーケンスで呼び出す必要があるため、同期を行わないと、スレッドのインターリーブ方法によっては、アプリケーションで予期しない動作が発生する可能性があります。 たとえば、2 つのスレッドが同じオブジェクトの同じプロパティを同時に異なる値に変更した場合、アプリケーションは 2 つの値のうち、プロパティの最終的な値を予測できません。 同様に、2 つのスレッドが同じデバイス Commit を呼び出した場合、1 つのスレッドで commit を 呼び出すと、Commitを呼び出したコマンドだけでなく、両方のスレッドによって発行されたすべてのコマンドのバッチが送信されるため、どちらのスレッドも真のトランザクション動作を受け取りません。
システムは、デバイス オブジェクトごとにすべての内部状態を維持します。 アプリケーションが 2 つ以上の DirectComposition デバイス オブジェクトを作成する場合、アプリケーションは 2 つの間で独立したバッチとその他の状態を維持できます。
すべての DirectComposition オブジェクトには、デバイス オブジェクトアフィニティがあります。特定のデバイス オブジェクトによって作成されたオブジェクトは、そのデバイス オブジェクトでのみ使用でき、同じデバイス オブジェクトによって作成された他のオブジェクトにのみ関連付けることができます。 つまり、各デバイス オブジェクトは、機能の個別の不整合な島です。 1 つの例外はビジュアル クラスであり、ビジュアルが親とは異なるデバイス オブジェクトに属できるビジュアル ツリーの構築を許可します。 これにより、1 つの DirectComposition デバイス オブジェクトを共有しなくても、アプリケーションとコントロールで単一のコンポジション ツリーを管理できるシナリオが可能になります。
コンポジション エンジン
DirectComposition コンポジション エンジンは、任意のアプリケーション プロセスとは別の専用プロセスで実行されます。 1 つのコンポジション プロセス (dwm.exe) は、セッション内のすべてのアプリケーションをサポートします。 各アプリケーションは、所有するウィンドウごとに 2 つのビジュアル ツリーを作成できます。 すべてのツリーは、DWM の構成構造も含む、より大きなビジュアル ツリーのサブツリーとして実際に実装されます。 DWM は、セッション内のデスクトップごとに 1 つの大きなビジュアル ツリーを構築します。 このアーキテクチャの主な利点を次に示します。
- コンポジション エンジンは、すべてのアプリケーション ビットマップとビジュアル ツリーにアクセスできます。これにより、クロスプロセス ウィンドウの相互運用性とコンポジションが可能になります。
- コンポジション エンジンは、任意のアプリケーション プロセスとは別の信頼されたシステム プロセスで実行され、アクセス権が低いアプリケーションで保護されたコンテンツを安全に構成できます。
- コンポジション エンジンは、特定のウィンドウが完全に隠されていることを検出し、ウィンドウ用に作成される CPU およびグラフィックス処理装置 (GPU) リソースの無駄を回避できます。
- コンポジション エンジンは画面バック バッファーに直接構成できるため、プロセスごとのコンポジション エンジンに必要な追加のコピーが不要になります。
- すべてのアプリケーションで構成用の 1 つの Direct3D デバイスを共有します。これによって、メモリを大幅に節約できます
ビジュアル ツリーは、保持された構造です。 DirectComposition API は、アトミックに処理される変更のバッチで構造を編集するメソッドを公開します。 DirectComposition API のルート オブジェクトはデバイス オブジェクトであり、他のすべての DirectComposition オブジェクトのファクトリとして機能し、Commitというメソッドが含まれています。 コンポジション エンジンは、アプリケーションが Commitを呼び出すまで、アプリケーションがビジュアル ツリーに加えた変更を反映しません。この時点で、最後の コミット 以降のすべての変更が 1 つのトランザクションとして処理されます。
Commit を呼び出す必要がある点は、コンポジション エンジンが非同期的に実行されるため、Commit を呼び出す呼び出しの間に複数の異なるフレーム表示できる点を除いて、"フレーム" の概念に似ています。 DirectComposition では、フレーム はコンポジション エンジンの 1 回の反復であり、commit を する 2 つの呼び出しの間にアプリケーションによって費やされる間隔は、バッチと呼ばれます。
DirectComposition は、DirectComposition API に対するすべてのアプリケーション呼び出しをバッチ処理します。 win32k.sys セッション ドライバーに実装されているカーネル オブジェクト データベースには、API 呼び出しに関連付けられているすべての状態情報が格納されます。
コンポジション エンジンは、ディスプレイ内の垂直ブランクごとに 1 つのフレームを生成します。 フレームは垂直ブランクで開始され、後続の垂直ブランクをターゲットとします。 フレームが開始されると、コンポジション エンジンは保留中のすべてのバッチを取得し、そのフレームにコマンドを含めます。 アプリケーションがコミット呼び出すと、バッチは保留中のキューに配置され、保留中のキューはフレームの先頭でアトミックにフラッシュされます。 したがって、フレームの先頭をマークする 1 つの時点があります。 このポイントより前に送信されたバッチはすべてフレームに含まれますが、後で送信されたバッチは、次のフレームが処理されるまで待機する必要があります。 完全なコンポジション ループは次のとおりです。
- 次の垂直ブランクの時間を見積もる。
- 保留中のすべてのバッチを取得します。
- 取得したバッチを処理します。
- 手順 1 で推定した時間を使用して、すべてのアニメーションを更新します。
- 再構成する必要がある画面の領域を決定します。
- ダーティ リージョンを再作成します。
- 各画面の背面バッファーと前面バッファーを反転してフレームを表示します。
- 何も構成されておらず、手順 6 と 7 で示されていない場合は、バッチがコミットされるのを待ちます。
- 次の垂直空白を待ちます。
1 つのビデオ アダプターに複数のモニターが接続されている場合、コンポジション エンジンはプライマリ モニターの垂直ブランクを使用してコンポジション ループを駆動し、アニメーションサンプリング時間を設定します。 各モニターは個別のフルスクリーンフリップチェーンで表されます。コンポジション エンジンは、1 つの Direct3D デバイスを使用して、ラウンドロビン方式でモニターごとに手順 6 と 7 を繰り返します。 複数のビデオ アダプターがある場合、コンポジション エンジンは、手順 6 と 7 のビデオ アダプターごとに個別の Direct3D デバイスを使用します。
コンポジション フレームは、次の図に示すように、常に垂直ブランクから開始するようにスケジュールされています。
コンポジション フレームのスケジューリングする
コンポジション ツリーが変更されていないためにコンポジション エンジンに作業がない場合、コンポジション スレッドは新しいバッチを待機している間スリープ状態になります。 新しいバッチが送信されると、コンポジション スレッドは起動しますが、次の垂直空白まですぐにスリープ状態に戻ります。 この動作により、アプリケーションとコンポジション エンジンのフレームの開始時刻と終了時刻が予測可能になります。
コンポジション エンジンは、フレーム表示時間と現在のフレーム レートを発行します。 この情報を公開すると、アプリケーションは独自のバッチのプレゼンテーション時間を見積もり、アニメーションを同期できるようになります。 特に、アプリケーションでは、コンポジション エンジンからのフレーム統計と、UI スレッドがバッチを生成するのにかかる時間の履歴モデルを組み合わせて、独自のアニメーションのサンプリング時間を決定できます。
たとえば、前の図に示したアプリケーション バッチの先頭で、アプリケーションはコンポジション エンジンに対してクエリを実行して、次のフレームの正確な表示時間を決定できます。 その後、アプリケーションは、生成された以前のバッチに関する情報と共に、現在の時刻を使用して、アプリケーションが次の垂直ブランクの前に現在のバッチを完了できるかどうかを判断できます。 そのため、アプリケーションはフレームプレゼンテーション時間を独自のアニメーションのサンプリング時間として使用します。 アプリケーションが現在の垂直ブランクで作業を完了する可能性が低いと判断した場合、アプリケーションは、コンポジション エンジンから返されたフレーム レート情報を使用して、その時間を計算する代わりに、後続のフレーム時間をサンプリング時間として使用できます。
関連トピック