DirectX グラフィックス インフラストラクチャ (DXGI): ベスト プラクティス

Microsoft DirectX Graphics Infrastructure (DXGI) は、Direct3D 10、10.1、11、11.1 で必要とされる低レベルのタスクの一部をカプセル化する、Windows Vista で導入された新しいサブシステムです。 Direct3D 9 プログラマの観点から見ると、DXGI には、以前に Direct3D 9 API にパックされていた列挙、スワップ チェーンの作成、およびプレゼンテーションのほとんどのコードが含まれています。 アプリを DXGI と Direct3D 10.x および Direct3D 11.x に移植する場合は、プロセスがスムーズに実行されるように考慮する必要があります。

この記事では、移植に関する主な問題について説明します。

Full-Screenの問題

Direct3D 9 から DXGI および Direct3D 10.x または Direct3D 11.x への移植では、ウィンドウモードから全画面表示モードへの移行に関連する問題が多く、開発者にとって頭痛の種になる可能性があります。 メインの問題は、DXGI アプリケーションとは異なり、Direct3D 9 アプリケーションでは、ウィンドウのスタイルとウィンドウの状態を追跡するためのより実践的なアプローチが必要であるために発生します。 モード変更コードが DXGI で実行されるように移植されると、多くの場合、予期しない動作が発生します。

多くの場合、Direct3D 9 アプリケーションは、フロント バッファーの解像度を設定し、デバイスを全画面表示排他モードにし、バック バッファーの解像度を一致するように設定することで、全画面表示モードへの移行を処理しました。 ウィンドウ サイズの変更には、アプリケーションがWM_SIZE メッセージを受信するたびにウィンドウ プロセスから管理する必要があるため、別のパスが使用されました。

DXGI では、2 つのケースを組み合わせてこのアプローチを簡略化しようとします。 たとえば、ウィンドウ枠がウィンドウモードでドラッグされると、アプリケーションはWM_SIZEメッセージを受け取ります。 DXGI はこのメッセージをインターセプトし、フロント バッファーのサイズを自動的に変更します。 アプリケーションで行う必要がある操作は、 IDXGISwapChain::ResizeBuffers を呼び出して、バック バッファーのサイズを、WM_SIZEのパラメーターとして渡されたサイズに変更することです。 同様に、アプリケーションが全画面表示モードとウィンドウ モードを切り替える必要がある場合、アプリケーションは単に IDXGISwapChain::SetFullscreenState を呼び出すことができます。 DXGI は、新しく選択した全画面表示モードに合わせてフロント バッファーのサイズを変更し、WM_SIZEメッセージをアプリケーションに送信します。 アプリケーションは、ウィンドウの境界線がドラッグされた場合と同様に、 ResizeBuffers を再度呼び出します。

上記の説明の方法論は、非常に具体的な道に従います。 DXGI は、既定で全画面表示の解像度をデスクトップ解像度に設定します。 ただし、多くのアプリケーションは、優先する全画面表示解像度に切り替えます。 このような場合、DXGI は IDXGISwapChain::ResizeTarget を提供します。 これは 、SetFullscreenState を呼び出す前に呼び出す必要があります。 これらのメソッドは逆の順序で呼び出すことができますが (最初に SetFullscreenState を呼び出し、その後に ResizeTarget を実行します)、これにより、追加のWM_SIZE メッセージがアプリケーションに送信されます。 (これを行うと、DXGI が 2 つのモード変更を強制的に実行する可能性があるため、ちらつきが発生する可能性もあります)。SetFullscreenState を呼び出した後、RefreshRate メンバーをゼロにして ResizeTarget をもう一度呼び出DXGI_MODE_DESCすることをお勧めします。これは DXGI の操作なしの命令に相当しますが、更新レートに関する問題を回避できます。これについては、次に説明します。

全画面表示モードの場合、デスクトップ ウィンドウ マネージャー (DWM) は無効になります。 DXGI はフリップを実行して、blit を実行する代わりにバック バッファーの内容を表示できます。これはウィンドウ モードで行われます。 ただし、特定の要件が満たされていない場合は、このパフォーマンスの向上を元に戻すことができます。 DXGI が blit ではなくフリップを実行できるようにするには、フロント バッファーとバック バッファーのサイズを同じにする必要があります。 アプリケーションがWM_SIZEメッセージを正しく処理する場合、これは問題になりません。 また、形式は同じである必要があります。

ほとんどのアプリケーションの問題は、更新速度です。 ResizeTarget の呼び出しで指定される更新レートは、スワップ チェーンで使用されている IDXGIOutput オブジェクトによって列挙される更新レートである必要があります。 アプリケーションが ResizeTarget に渡されるDXGI_MODE_DESCRefreshRate メンバーをゼロにした場合、DXGI は自動的にこの値を計算できます。 特定の更新レートが常にサポートされると想定せず、単に値をハードコーディングすることが重要です。 多くの場合、開発者はリフレッシュ レートとして 60 Hz を選択します。モニターからの列挙された更新レートがモニターから約 60,000 / 1,001 Hz であることを知りません。 更新レートが予想される更新レート 60 と一致しない場合、DXGI はフリップではなく全画面表示モードで blit を強制的に実行します。

開発者がよく直面する最後の問題は、全画面表示モードのままで全画面表示の解像度を変更する方法です。 ResizeTargetSetFullscreenState の呼び出しは成功することがありますが、全画面表示はデスクトップの解像度のままです。 また、開発者は全画面表示のスワップ チェーンを作成し、特定の解像度を指定できます。DXGI の既定値は、渡された数値に関係なく、デスクトップ解像度に設定されていることがわかります。 特に指示がない限り、DXGI は全画面表示スワップ チェーンのデスクトップ解像度に既定で設定されます。 全画面表示スワップ チェーンを作成する場合、DXGI の既定の動作をオーバーライドするには、DXGI_SWAP_CHAIN_DESC構造体の Flags メンバーをDXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCHに設定する必要があります。 このフラグを ResizeTarget に渡して、この機能を動的に有効または無効にすることもできます。

複数のモニター

複数のモニターで DXGI を使用する場合は、2 つのルールに従う必要があります。

最初のルールは、複数のモニターで 2 つ以上の全画面表示スワップ チェーンを作成する場合に適用されます。 このようなスワップ チェーンを作成するときは、すべてのスワップ チェーンをウィンドウとして作成し、全画面表示に設定することをお勧めします。 スワップ チェーンが全画面表示モードで作成された場合、2 つ目のスワップ チェーンを作成すると、モードの変更が最初のスワップ チェーンに送信され、全画面表示モードが終了する可能性があります。

2 番目のルールは出力に適用されます。 スワップ チェーンの作成時に使用される出力に注意してください。 DXGI では、 IDXGIOutput オブジェクトは、全画面表示になったときにスワップ チェーンが使用する監視を制御します。 DXGI とは異なり、Direct3D 9 には出力の概念がありませんでした。

ウィンドウ スタイルと DXGI

Direct3D 9 アプリケーションでは、全画面表示モードとウィンドウ モードを切り替えるときに多くの作業を行う必要がありました。 この作業の多くは、ウィンドウ スタイルを変更して罫線を追加および削除したり、スクロール バーを追加したりしました。 アプリケーションが DXGI と Direct3D 10.x または Direct3D 11.x に移植される場合、このコードは多くの場合、所定の位置に残ります。 加えられた変更によっては、モードを切り替えると予期しない動作が発生する可能性があります。 たとえば、ウィンドウ モードに切り替えると、これらのスタイルを特に設定するコードがあるにもかかわらず、アプリケーションにウィンドウ フレームやウィンドウの境界線が設定されなくなる可能性があります。 これは、DXGI が独自にこのスタイルの変更の多くを処理するためです。 ウィンドウ スタイルを手動で設定すると DXGI が妨げられ、予期しない動作が発生する可能性があります。

推奨される動作は、できるだけ少ない作業を行うことと、DXGI がウィンドウとの対話の大部分を処理できるようにすることです。 ただし、アプリケーションが独自のウィンドウ動作を処理する必要がある場合は、 IDXGIFactory::MakeWindowAssociation を使用して、DXGI に対して自動ウィンドウ処理の一部を無効にするように指示できます。

マルチスレッドと DXGI

マルチスレッド アプリケーションで DXGI を使用する場合、デッドロックが発生しないように特に注意する必要があります。 DXGI はウィンドウと密接にやり取りするため、関連付けられているアプリケーション ウィンドウにウィンドウ メッセージを送信することがあります。 DXGI では、ウィンドウの変更を続行する前に行う必要があるため、同期呼び出しである SendMessage を使用します。 SendMessage が返される前に、アプリケーションでウィンドウ メッセージを処理する必要があります。

DXGI が呼び出し、メッセージ ポンプが同じスレッド (またはシングル スレッド アプリケーション) 上にあるアプリケーションでは、ほとんど行う必要がありません。 DXGI 呼び出しがメッセージ ポンプと同じスレッドにある場合、 SendMessage は ウィンドウの WindowProc を呼び出します。 これによりメッセージ ポンプがバイパスされ、 SendMessage の呼び出し後も実行を続行できます。 IDXGISwapChain::P resent などの IDXGISwapChain 呼び出しも DXGI 呼び出しと見なされます。DXGI は、Present が呼び出されるまで ResizeBuffers または ResizeTarget から作業を延期する場合があります。

DXGI 呼び出しとメッセージ ポンプが異なるスレッドにある場合は、デッドロックを回避するために注意する必要があります。 メッセージ ポンプと SendMessage が異なるスレッドにある場合、 SendMessage はウィンドウのメッセージ キューにメッセージを追加し、ウィンドウがそのメッセージを処理するまで待機します。 ウィンドウ プロシージャがビジー状態の場合、またはメッセージ ポンプによって呼び出されない場合、メッセージが処理されないことがあり、DXGI は無期限に待機します。

たとえば、あるスレッドにメッセージ ポンプがあり、別のスレッドでレンダリングされているアプリケーションでは、モードを変更できます。 メッセージ ポンプ スレッドは、モードを変更するようにレンダリング スレッドに指示し、モードの変更が完了するまで待機します。 ただし、レンダリング スレッドは DXGI 関数を呼び出し、 SendMessage を呼び出し、メッセージ ポンプがメッセージを処理するまでブロックします。 デッドロックが発生するのは、両方のスレッドがブロックされ、互いに待機しているためです。 これを回避するには、メッセージ ポンプをブロックしないでください。 ブロックが避けられない場合は、すべての DXGI 相互作用がメッセージ ポンプと同じスレッドで行われる必要があります。

ガンマと DXGI

ガンマは、SRGB テクスチャを使用して Direct3D 10.x または Direct3D 11.x で最適に処理できますが、ガンマ ランプは、2.2 とは異なるガンマ値を必要とする開発者や、SRGB をサポートしないレンダー ターゲット形式を使用している開発者にとって、引き続き役立ちます。 DXGI でガンマ ランプを設定する場合は、2 つの問題に注意してください。 最初の問題は、 IDXGIOutput::SetGammaControl に渡されるランプ値が WORD 値ではなく float 値であるということです。 また、Direct3D 9 から移植されたコードが、SetGammaControl に渡す前に WORD 値への変換を試みないことを確認します。

2 つ目の問題は、全画面表示モードに変更した後、使用されている IDXGIOutput オブジェクトに依存して SetGammaControl が機能しない可能性があるということです。 全画面表示モードに変更すると、DXGI は新しい出力オブジェクトを作成し、出力に対する後続のすべての操作に オブジェクトを使用します。 全画面表示モード スイッチの前に列挙された出力で SetGammaControl を呼び出す場合、DXGI が現在使用している出力に対して呼び出しが送信されません。 これを回避するには、 IDXGISwapChain::GetContainingOutput を呼び出して現在の出力を取得し、 SetGammaControl をこの出力から呼び出して正しい動作を取得します。

ガンマ補正の使用方法については、「ガンマ補正の 使用」を参照してください。

DXGI 1.1

Windows 7 に含まれ、Windows Vista にインストールされた Direct3D 11 ランタイムには、バージョン 1.1 の DXGI が含まれています。 この更新プログラムでは、リモート デスクトップ接続を列挙するための新しい形式 (特に BGRA、10 ビット X2 バイアス、Direct3D 11 の BC6H および BC7 テクスチャ圧縮) の定義と、DXGI ファクトリおよびアダプター インターフェイスの新しいバージョン (CreateDXGIFactory1IDXGIFactory1IDXGIAdapter1) が追加されます。

Direct3D 11 を使用する場合、ランタイムは、NULL IDXGIAdapter ポインターを使用して D3D11CreateDevice または D3D11CreateDeviceAndSwapChain を呼び出すときに、既定で DXGI 1.1 を使用します。 同じプロセスでの DXGI 1.0 と DXGI 1.1 の混在はサポートされていません。 同じプロセス内の異なるファクトリからの DXGI オブジェクト インスタンスの混在もサポートされていません。 したがって、DirectX 11 を使用する場合、DXGI インターフェイスを明示的に使用する場合は、CreateDXGIFactory1 エントリ ポイントによって作成された "DXGI.DLL" の IDXGIFactory1 を使用して、アプリケーションが常に DXGI 1.1 を使用していることを確認します。

DXGI 1.2

Windows 8に含まれる Direct3D 11.1 ランタイムには、DXGI のバージョン 1.2 も含まれています。

DXGI 1.2 では、次の機能が有効になります。

  • ステレオ レンダリング

  • 16 ビット/ピクセル形式

    • DXGI_FORMAT_B5G6R5_UNORMとDXGI_FORMAT_B5G5R5A1_UNORMが完全にサポートされるようになりました
    • 新しいDXGI_FORMAT_B5G5R5A1_UNORM形式が追加されました
  • ビデオ形式

  • 新しい DXGI インターフェイス

DXGI 1.2 の機能の詳細については、「 DXGI 1.2 の機能強化」を参照してください。